Input block
Input variable is used to customize Terraform modules without having to edit their source code. This makes it an extremely flexible, reusable, and composable module across a wide variety of Terraform configurations. If you have experience with traditional programming languages, you can think about Terraform modules being similar to function definitions. Just like functions have arguments, Terraform modules have input variables.
In naming variables in Terraform, there are some reserved names that must be avoided. They are source, version, providers, count, for_each, lifecycle, depends_on, and locals. These names are reserved within Terraform to support meta-arguments in the configuration of a module, and the use of such names as input variables might lead to conflicts or errors in the Terraform configuration.
Input block syntax
Input block syntax is as follows:
variable "local_name" {
# Input Specific Arguments
}
Here's a breakdown of the syntax:
variable
is the keyword to declare an input block is variable.
local_name
is the name of the input variable. This name is to be used to refer to the input variable inside a Terraform module.
- The block body (between
{}
) contains input-specific arguments. Here you declare properties and constraints on the input variable.
For example, consider a Terraform module responsible for provisioning an AWS EC2 instance. We would like this module to be flexible enough that its users can specify things like what type of instance and in what region it is provisioned. This could be achieved by defining input variables for things like instance type and region.
variable instance_type {
type = string
default = "t2.micro"
description = "The type of EC2 instance to provision"
}
variable region {
type = string
default = "us-west-2"
description = "The AWS region to provision the instance in"
}
In the above example, we have created two input variables: instance_type and region. We have assigned a type to each variable and also defined default values and a short description for each one. The above two input variables can now be used in our Terraform module to customize the provisioning of the EC2 instance.
Arguments of input block
The following arguments are supported:
1. default
The default argument is an optional argument. When there is a variable with a default argument, the variable is taken as optional. Meaning, it's not necessary to provide a value when calling the module or running Terraform. If you pass no value, the default one will be used instead.
The default argument must be a literal value, meaning it cannot refer to any other objects or variables in configuration. You need to provide a fixed, hard-coded value that will be used by default.
variable "bucket_name" {
default = "my-default-bucket"
}
In the above example, the default argument is set to "my-default-bucket", which is a literal value. If no value for the variable named bucket_name is provided while execution using Terraform, then it will default to "my-default-bucket".
2. type
If you don't add a type argument, by default Terraform will automatically allow any type of value to be assigned to the variable. so users can provide a string, a number, a boolean or any other type of value.
Basics type
There are three basic types of inputs in Terraform:
- String
- Number
- Bool
Complex type
There are five complex input types in Terraform, which extend the basic input types:
- list
- set
- map
- object
- tuple
1. String
The String type in Terraform provides variables of input type with a
sequence of characters
, such as words or sentences. Defining a variable or input as a String enforces the idea that only text values can be attached to it.
Suppose you are creating a Terraform configuration where you deploy a storage bucket on AWS. You need to specify the name of the bucket, so you'd create a variable called bucket_name.
variable "bucket_name" {
type = string
default = "my-bucket"
}
By setting the type of bucket_name to string, you are telling Terraform that this variable will only accept text values and you can't assign a number or any other data type other than text to this variable. It must be a string of characters, such as "my-bucket" or "example-storage-bucket".
2. Number
The Number data type in Terraform could store the numeric value of either
integer
or floating-point
in an Input Variable. When one declares a variable or input as a number, it assures that only numeric values could be assigned to it.
For instance, suppose you are creating a Terraform configuration to set the number of instances for a resource. You can declare a variable with the name instance_count and set its type as number:
variable "instance_count" {
type = number
default = 5
}
By setting instance_count type to number, you are telling Terraform that this variable can only accept numerical values and you can assign values like 3, 10, 3.14 or 2.5 to this variable but you cannot assign nonnumerical values like "three" or "high".
3. Boolean
The Boolean data type in Terraform is used to store the logical data and consists of two values:
true
or false
. When you define any variable or input as a Boolean, it only accepts these two logical values.
For instance, suppose you are writing a Terraform configuration that enables or disables a resource feature. You declare a variable by the name feature_enabled and its type as bool:
variable "feature_enabled" {
type = bool
default = true
}
By setting the type of feature_enabled to bool, you're telling Terraform this variable can only accept values true or false, and you can assign to this variable values like true or false, but you cannot assign non-boolean values such as "yes", "no", 1, or 0.
4. List
In Terraform, the list is a complex data type. Lists store a collection of values, although all elements in that list must be of the same basic data type. The basic data types in Terraform are string, number, and bool. List type allows duplicate elements.
Lists will come in handy in the case when you need to pass more than one value of the same type inside your Terraform configuration down to a resource. For example, You might use a list of server names or a list of port numbers as input to a resource that needs to manage multiple items of the same type.
The syntax for declaring a list variable in Terraform is:
list(<BASIC_TYPE>)
Once you have defined a list variable, you could refer to its individual elements, or the entire list using the following syntax:
# To Access Entire elements from List
var.<LOCAL_NAME>
# To Access specific elements from List
var.<LOCAL_NAME>[<index>]
For instance, consider a Terraform configuration that provisions a few ec2 instances, and where the person running the Terraform wants to provide the instance names as an input. You could declare a variable of type "list(string)" as follows:
variable "instance_names" {
type = list(string)
default = ['ec2_instance','db_instance','build_instance']
}
Here, the instance_names variable is of type list of string. That means you can input a list of instance names if you run Terraform.
5. Set
In Terraform, a Set is a complex data type that enables you to store values of the same basic data type in a collection. Much like a List, but with the key difference that a Set enforces uniqueness of all the values in that collection.
Unlike lists, you cannot access individual elements of a set using an index since the sets are unordered collections, and the order of the elements cannot be guaranteed. However, you can still access the whole set.
Here is the syntax for declaring or defining a set variable in Terraform:
set(<BASIC_TYPE>)
Once you have set a variable, you can access the entire set by using the following syntax:
# To Access Entire elements from Set
var.<LOCAL_NAME>
Sets are helpful when you want to ensure input values you give to a resource are unique. For example, you might have a set of port numbers so that you can ensure each port is only used once in your infrastructure.
Suppose you are creating a Terraform configuration that provisions a collection of web servers, and you want to ensure each server uses a different port number. You declare a variable of type "set(number)" to do this:
variable "server_ports" {
type = set(number)
default = [0,90,8080]
}
Here, the variable server_ports is a set of numbers. That means you can provide input and supply a set of unique port numbers at the time of execution of the Terraform run.
6. Map
In Terraform, a Map is another complex data type. These are used to store a collection of key-value pairs of the same basic type. Whereas a Set would store a collection of unique values, a Map stores a collection of unique keys that have an associated value.
The syntax for declaring a Map variable in Terraform is:
map(<BASIC_TYPE>)
Once the map variable is defined, the individual elements or the whole map can be accessed using the following syntax:
# To Access Entire Key-Value from Map
var.<LOCAL_NAME>
# To Access specific key from Map
var.<LOCAL_NAME>[<key>]
Maps come in handy when you need to keep track of specific pieces of information associated with a unique identifier, or label. For example, you could have a Map that keeps configuration settings for some set of resources, where keys are the names of the settings and values are the actual settings.
Now, suppose you are writing a Terraform configuration that provisions a group of web servers, and each of them must be assigned a specific domain name. You can declare a variable of type "map(string)" as follows:
variable "server_domains" {
type = map(string)
default = {
server1 = "example1.com"
server2 = "example2.com"
server3 = "example3.com"
}
}
In the above example, it has been specified that the variable server_domains is to be a Map of strings. Therefore you will be able to supply a set of key-value pairs where the keys are the server names and the values are the correspondent domain names.
7. Object
In Terraform, an Object data type is a complex data type that allows one to represent structured data with named attributes. An Object is basically a collection of key-value pairs, but unlike a Map, it facilitates a more complex schema being defined for the actual data.
The syntax to define a Object variable in Terraform is:
object({<ATTRIBUTE_NAME> = <BASIC_TYPE>})
Here,
<ATTRIBUTE_NAME>
is the name of a specific attribute.
<BASIC_TYPE>
is the data type of that attribute such as string, number, bool.
Once you have defined an object variable you may access its individual elements or the entire object using the following syntax:
# To Access Entire Key-Value from Object
var.<LOCAL_NAME>
# To Access specific key from Object
var.<LOCAL_NAME>[<key>]
Objects are convenient when one wants to represent more complex data structures, such as configuration settings or resource attributes, where the data does have a welldefined structure that contains multiple named fields.
For example, you might be writing a Terraform configuration that provisions a virtual machine, and you want to allow the user to specify the machine's hardware configuration. You can declare an object variable for the hardware configuration:
variable "server" {
type = object({
hostname = string
is_production = bool
cpu_count = number
memory_gb = number
disk_size_gb = number
})
default = {
hostname = "build.com"
is_production = true
cpu_count = 4
memory_gb = 25
disk_size_gb = 120
}
}
8. Tuple
In Terraform, a tuple is a complex data type that represents an ordered collection of values. Each value in the tuple has a specific data type known beforehand. Tuples are similar to lists, except for the fact that the data type of individual elements in the tuple are fixed and get known beforehand.
The syntax to define a Tuple in Terraform is:
tuple([<BASIC_TYPE>, <BASIC_TYPE>, ....])
Once you define a tuple variable, you can retrieve either its individual elements or the entire tuple itself using the following syntax:
# To Access Entire elements from Tuple
var.<LOCAL_NAME>
# To Access specific elements from Tuple
var.<LOCAL_NAME>[<key>]
Tuples are useful when you need to deal with a fixed collection of values of a known data type. For example, representing configuration settings, resource attributes, or the result of some Terraform functions. It helps maintain well-defined structure and data types of the data that is being worked with, thus enhancing maintainability and reliability of Terraform configurations.
For example, suppose you have a setting for which you wants to store a string, a number, and a bool. You can represent such a setting using a Tuple:
variable "config_settings" {
type = tuple([string,number,bool])
default = ["Instance",90,true]
}
Here, individual elements of the config_settings Tuple are accessed using their index in the list ([0], [1], [2]).
3. description
In Terraform, description is the optional argument in input variables that provides a short explanation of what does an input variable does. This should explain to the user two important things:
- Purpose of the Variable
- The Use of the Variable
Here is an example with the use of the description argument:
variable "config_settings" {
type = tuple([string,number,bool])
default = ["Instance",90,true]
}
In this example above, the description argument indicates that the region variable is used to pick an AWS region to deploy resources into. It also enumerates some of the valid values available for this variable, such as us-west-2 and eu-central-1.
4. validation
Custom validation rules in Terraform allow setting up checks against the value of a variable. This comes in handy when requirements or formats need to be met from input values. To use custom validation rules, you will have to add the validation block within another block known as the variable block. The validation block has two parts:
condition
: A boolean expression that is evaluated for whether the variable value meets the condition.
error_message
: This is a string that is displayed as an error message if the condition is not met.
When you define a custom validation rule, Terraform will evaluate the condition expression against input value. If the expression returns false, Terraform treats the input value as invalid and displays the error_message.
Example: Validating an AMI ID
Suppose one wants to validate an AMI ID. In the following example, a custom validation rule is defined for a variable named image_id. It's checking two things in this condition: first, the length of the string of image_id is greater than 4 characters and second, it starts with a prefix "ami-". If this condition does not meet, then error_message is given.
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
validation {
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
}
}
5. sensitive
In Terraform, you can mark a variable as sensitive to not show its value in the plan or apply output in case you use this variable elsewhere in your configuration. This is quite useful when working with sensitive information, like passwords, API keys, or other data you wouldn't want to see in plain text in your Terraform output.
To declare a variable as sensitive, you just have to set the sensitive argument to true inside the variable block, like in this example:
variable "user_information" {
type = object({
username = string
password = string
})
sensitive = true
}
Here, the user_information variable is declared as sensitive, meaning its value will not appear in the plan or apply output when it is used elsewhere in the configuration.
NOTE: While marking a variable as sensitive will prevent its value from being shown in the output, Terraform will still store the sensitive value in the statefile. That means anyone who has access to the state file will have access to the sensitive value in plain text. Also, it is very important to ensure that your state file is properly secured and accessible only to people who have authorization.
Sensitive variables and dependent expressions
When a variable is marked sensitive, any expressions that depend on that variable will also be treated as sensitive. If the given variable is a sensitive variable used within a resource attribute, then resulting resource attribute will be also hidden in a plan output.
For example, suppose you have a sensitive variable db_password and you use this variable in a resource attribute in the following manner:
variable "db_password" { type = string sensitive = true } resource "aws_db_instance" "default" { allocated_storage = 9 db_name = "proddb" engine = "mysql" engine_version = "8.0" username = "admin" password = var.db_password }
In this case, the password attribute from the aws_db_instance resource depends from the sensitive variable db_password. When you run terraform plan or terraform apply, Terraform will generate a plan output that shows the changes it will make to your infrastructure. In this case, because the password attribute of the aws_db_instance resource depends on the sensitive db_password variable, Terraform will hide the value of the password attribute in the plan output. Here is what the plan output might look like.
Terraform will perform the following actions:
# aws_db_instance.default will be created
+ resource "aws_db_instance" "default" {
+ allocated_storage = 9
+ db_name = proddb
+ engine = mysql
+ engine_version = 8.0
+ username = admin
+ password = (sensitive value)
}
Sensitive data in nested blocks
When you use a sensitive variable inside of a nested block in a resource, Terraform decide to hide the entire block from the plan output. This process is called "redaction". Terraform is doing this to prevent your sensitive information from being exposed.
Another cases, the nested block itself contains another nested block that could contain sensitive variables. In such cases, if Terraform were to show the contents of one nested block, it could accidentally reveal information about another nested block that is related to it. This may happen in the situation where all of the blocks of a particular type must be unique, and knowing the contents of one block could give away information about another block. In this case, terraform will also hide both nested blocks.
variable "cidr_block" { type = string default = "10.0.0.0/8" sensitive = true } resource "aws_security_group" "security" { dynamic "ingress" { content { cidr_blocks = var.cidr_block } } }
In this example, the cidr_block variable is marked as sensitive. When Terraform runs, it knows that the nested ingress block contains this sensitive information. To avoid displaying the sensitive information, Terraform will mask the entire ingress block when showing the plan.
The output of the plan would look like:
# some_resource.a will be updated in-place
+ resource "aws_security_group" "security" {
+ dynamic "ingress" {
# At least one attribute in this block is (or was) sensitive,
# so its contents will not be displayed.
+ }
}
This Terraform output is only letting you know that the contents of the ingress block have been hidden because it contains information that could be sensitive. This is to help give you confidence that your data would be safe and secure, even in the plan output.
When Terraform Might Accidentally Reveal Sensitive Variables
While Terraform tries to keep sensitive variables safe, there are some cases where they can be exposed. This happens under two conditions:
Scenario 1: Provider Errors
When a provider (a plugin that interacts with an external service) encounters an error, it may include the sensitive value in the error message. This can lead to a security risk.
Let's say you have a Terraform configuration file with a sensitive variable, password set to Xgty+524ksuhhaDqN. You use this variable in a local-exec provisioner in order to make a curl request to a repository. The input configuration would look something like:.
variable "password" { type = string default = "Xgty+524ksuhhaDqN" sensitive = true } resource "null_resource" "exposing_sensitive" { provisioner "local-exec" { command = "curls -u admin:${var.password} https://repository.com" } }
When one runs terraform apply, the null provider attempts to execute the provisioner, which in turn, attempts to execute the curl command. However, because curls is an invalid command (it should be curl) the provider fails and returns an error. Unfortunately, that error message contains the sensitive password value
null_resource.exposing_sensitive: Creating... null_resource.exposing_sensitive: Provisioning with 'local-exec'... null_resource.exposing_sensitive (local-exec): (output suppressed due to sensitive value in config) Error: local-exec provisioner error Error running command 'curls -u admin:Xgty+524ksuhhaDqN https://repository.com': exit status 127. Output: /bin/sh: curls: command not found
This is a security risk because when an error occurs, the error message shows the value of this sensitive password, which is "Xgty+524ksuhhaDqN", and this may be exposed to anyone who can access the Terraform logs or output.
Scenario 2: User defined resource id
During normal Terraform operations, if Terraform creates a new resource, it will automatically generates an unique identifier for that resource, which is called the
resource ID
. Resource ID is used to uniquely identify that resource in the Terraform Statefile, which keeps track of the infrastructure resources created by Terraform.
Sometimes, however, you might want to define the ID for a resource explicitly, and not let Terraform generate it randomly. One way in which you can do this is by defining attributes within the resource itself. These are utilized in the construction of the custom resource ID. But here's the catch! If one of the attributes utilized for the definition of the custom resource ID contains sensitive data, for example, a password or encryption key, the value that attribute holds will be exposed to view when Terraform applies the configuration.
Let's say you have a Terraform configuration that defines a random_pet resource. This resource has an ability to define its ID using its attributes, and you can define its ID using prefix, length arguments. You have set the value of the prefix attribute to a sensitive variable called user.
variable "user" { type = string default = "admin" sensitive = true } resource "random_pet" "exposing_sensitive" { prefix = var.password length = 3 separator = "-" }
When we run Terraform to create the resource, the output will show the value of the custom resource ID, that includes the sensitive data:
random_pet.exposing_sensitive: Creating... random_pet.exposing_sensitive: Creation complete after 0s [id=admin-evenly-easily-assuring]
As described above, the sensitive data was part of the user variable, which was "admin", included in the ID of the custom resource. In this case, the exposure is a security risk, because it will allow anyone with access to the Terraform logs or output to notice the sensitive value. To that end, custom resource IDs should be defined while considering a proper selection of attributes so that sensitive data attribute exposure can be avoided.
Assigning Values to Input Variables
When you declare variables in the root module of your Terraform configuration, you can set their values in several ways:
- Individual Command Line Option (
-var
)
- Variable Definitions (
.tfvars
) Files
- Environment Variables
1. Individual Command Line Option (-var)
Many of the Terraform commands you've used so far, including terraform plan and terraform apply, accept an option named
-var
that allows you to specify variables individually on the command line. This approach can be a convenient way to override the default values of variables without modifying your Terraform configuration files. If you need to pass multiple variable values, you can use the -var option multiple times in a single command to set multiple variables at once.
The syntax for using the -var option follows:
terraform <command> -var="<variable_name>=<variable_value>"
Here,
<variable_name>
is the name of the variable you want to set.
<variable_value>
is the value you want to assign to that variable.
For example, consider that you have made declarations for three variables within your Terraform configuration:
variable "image_id" {
type = string
}
variable "image_id_list" {
type = list(string)
}
variable "image_id_map" {
type = map(string)
}
You can set the values of these variables on the command line using the -var option:
terraform apply -var="image_id=ami-abc123" -var='image_id_list=["ami-abc123","ami-def456"]' -var='image_id_map={"us-east-1":"ami-abc123","us-east-2":"ami-def456"}'
2. Variable Definitions Files (.tfvars)
Instead of using the -var option to specify every variable value on the command line, sometimes it is more convenient for a lot of variables to have them in a file and then specify that input file when running Terraform commands.
To take advantage of this method, you'll need to create a file ending either in .tfvars or .tfvars.json. It will contain variable name assignments. The syntax of this file is similar to Terraform language files and includes only variable assignments, without resource definitions or any other Terraform configuration.
For example, you could define in a Terraform file, variables for both profile and deployment_regions like the following.
variable "profile" {
type = string
}
variable "deployment_regions" {
type = list(string)
}
Then, you can create a separate file, for example, the file_variables.tfvars, and assign values to those variables.
profile = "Administrator"
deployment_regions = ['us-east-1','ap-southeast-1','ap-southeast-2']
You can use this file with Terraform run commands such as terraform plan and terraform apply using the
-var-file
option to reference this file.
terraform plan -var-file=file_variables.tfvars
The above command will instruct Terraform to use the variable definitions from the file_variables.tfvars file for the plan command. Terraform will use these variable values to give an execution plan about your infrastructure.
Automatic Loading of Variables
In addition to the variables specified on the command line or in a file specified with the -var-file option, Terraform automatically loads variables from files whose names have a special format if those files are present in the current working directory. There are two such sorts of files that Terraform is looking for:
- Exact filenames: Terraform will also look for the following exact filenames -
terraform.tfvars
orterraform.tfvars.json
. If these files exist within your working directory, then Terraform will automatically load the variable definitions from these files.
- Specific Extension Files: Terraform also searches for files with the following extensions for loading
.auto.tfvars
or.auto.tfvars.json
which means that Terraform will load any file with these extensions, regardless of its base name.
3. Environment Variables
Besides setting the variable values directly in your Terraform configuration files, you can also pass variable values in using environment variables. This is yet another way to provide variable values to Terraform. Terraform expects a specific naming convention for these environment variables. This convention goes like this:
- The environment variable must start with the prefix
TF_VAR_
- The rest of the name must match the name of a variable declared within the Terraform configuration.
Let's take an example, Suppose you have declared a variable named "db_user_name" in your Terraform configuration:
variable "db_user_name" {
type = string
}
In order to pass a value for this variable using an environment variable, then you'd have to create an environment variable named "TF_VAR_db_user_name" and set its value. For example, in a Unix-based shell, you could do:
export TF_VAR_db_user_name = my_database_username
When Terraform runs, it will use the values of environment variables whose names start with "TF_VAR_" to populate corresponding variables in the configurations. In that case, for the variable above, Terraform would use the value of the "TF_VAR_db_user_name" environment variable.
Variable Definition Precedence
Terraform uses a specific order to decide which value to use, called "variable definition precedence", when one variable has more than one value assigned to it from different sources.
Here is the order in which Terraform loads variables:
- Command-line: Command-line options, like
-var
and-var-file
, are highest precedence. Any values set via command-line options override values set via other sources.
- *.auto.tfvars and *.auto.tfvars.json files: Next, Terraform also loads variables from files whose names contain the pattern
*.auto.tfvars
or*.auto.tfvars.json
in alphabetical order of their filenames. These files will take precedence over values from environment variables, terraform.tfvars, and terraform.tfvars.json files.
- terraform.tfvars.json: After Terraform loads all variables from files
*.auto.tfvars
and*.auto.tfvars.json
, it will then load variables from the terraform.tfvars.json file. This file takes priority over environment variables and over the terraform.tfvars file.
- terraform.tfvars file: The next in order is the terraform.tfvars file, its values are permitted to override those coming from environment variables.
- Environment variables: Finally, Terraform loads variables from environment variables set on your system. These values have the lowest precedence and can thus be overridden by any of the sources above.
NOTE: In Terraform, if you define a variable, you can only assign one value to it from one single source.
Related Pages
Feedback
Was this page helpful?