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:
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

When declaring a variable in Terraform, you can have an argument called type that could denote what kind of value is accepted. For example, you might only want a variable to accept strings, or only numbers, or only true or false values.
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:

Complex type

There are five complex input types in Terraform, which extend the basic input types:

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,
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:
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:
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:

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,
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:

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:
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:
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?