Hello!

In this programming, I will talk about variables in terraform and terraform data.

Variables

I will use the code from previous posts. I create a new file variables.tf and add the instance_type variable to it.

variable "instance_type" {
  default = "t2.micro"
  description = "Instance type for test instance"
  type = string
}

In this block, several attributes are added to the instance_type variable: default - the variable will have the value specified in this attribute unless otherwise specified description - description of the variable type - is the type of the variable

These attributes are optional. The instance_type variable can be created as follows

variable "instance_type" {}

But it is more difficult to understand what this variable is. After the variable is created, it can be used in the terraform code as var.instance_type

resource "aws_instance" "foo" {
  ami                    = "ami-0cff7528ff583bf9a"
  instance_type          = var.instance_type
  subnet_id              = "subnet-db73f0ac"
  vpc_security_group_ids = ["sg-b04b8cd4"]
  tags = {
    Env = "Dev"
  }
  volume_tags = {
    "Env" = "Dev"
  }
}

If I need to use a different type of instance, there are several options. The first is to pass it when running terraform with parameter var

$ terraform apply -var instance_type=t3.micro
  # aws_instance.foo will be created
  + resource "aws_instance" "foo" {
      + ami                                  = "ami-0cff7528ff583bf9a"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3.micro"

Another option is using a file with variables, I will create a file called prod.tfvars with content

instance_type = "m5.large"

Then I will call terraform with -var-file option

$ terraform plan -var-file=prod.tfvars
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.foo will be created
  + resource "aws_instance" "foo" {
      + ami                                  = "ami-0cff7528ff583bf9a"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "m5.large"

The next option is via env variables. To do this, you need to create an env variable called TF_VAR_name.

$export TF_VAR_instance_type=r5.xlarge
$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.foo will be created
  + resource "aws_instance" "foo" {
      + ami                                  = "ami-0cff7528ff583bf9a"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "r5.xlarge"

And the last option uses a file named .auto.tfvars. Create a file with the name prod.auto.tfvars with the next content

instance_type = "c5.large"

Once I launch terraform it will automatically read variables from this file

$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.foo will be created
  + resource "aws_instance" "foo" {
      + ami                                  = "ami-0cff7528ff583bf9a"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "c5.large"

Locals

In addition to the global variables in terraform, you can also use locals. They are convenient to use when some value is repeated often, but there is no need to transfer it to the global variables. They are created in the local’s block. Example:

locals {
  subnet_id = "subnet-db73f0ac"
}

And now this local variable can be used in the code as local.subnet_id

resource "aws_instance" "foo" {
  ami                    = "ami-0cff7528ff583bf9a"
  instance_type          = var.instance_type
  subnet_id              = local.subnet_id
  vpc_security_group_ids = "sg-123"
  tags = {
    Env = "Dev"
  }
  volume_tags = {
    "Env" = "Dev"
  }
}

Data

Some attributes are not always convenient to use as variables, and it is better to dynamically receive values when starting terraform. For example, the ami attribute, if you need always to have the latest version of ami regardless of the region. Or there are other resources created by other terraform code or using the AWS UI, such as VPC or subnet_id and you need to use their IDs in your code. Terraform has a data block for this.

I will show how you can dynamically get the value of ami. To do this, I will create a separate file named data.tf with such content

data "aws_ami" "amzn" {
  most_recent = true
  owners = ["amazon"]

  filter {
    name = "name"
    values = ["amzn2-ami-kernel-*"]
  }
}

Inside the data block are several attributes: most_recent - whether to always get the latest version of AMI or not owners - which AWS account owns ami filter - by what criteria to filter. In this case, I’m filtering by a name that must start with amzn2-ami-kernel. And that way I’ll always have the ID of the latest amazon Linux 2 AMI no matter in what region I’m creating the resources.

This can now be used in our aws_instance resource as data.aws_ami.amzn.id

resource "aws_instance" "foo" {
  ami                    = data.aws_ami.amzn.id
  instance_type          = var.instance_type
  subnet_id              = local.subnet_id
  vpc_security_group_ids = "sg-123"
  tags = {
    Env = "Dev"
  }
  volume_tags = {
    "Env" = "Dev"
  }
}

Video