Skip to main content

Hashicorp Terraform - Providers, Variables & Attributes

Shenzhen, China

Providers

Terraform offers a large quantity of providers that you can use in your configuration file - e.g. the Random Provider that you can use to generate random:

  • random_id
  • random_integer
  • random_password
  • random_pet
  • random_shuffle
  • random_string
  • random_uuid

Resource Attributes

Let's add this to our configuration:

sudo nano /etc/terraform.d/main.tf
resource "random_uuid" "uuid" {
}

resource "local_file" "resource_name" {
  filename = "/opt/uuid"
  content =  "${random_uuid.uuid.result}-rg"
  directory_permission = "0777"
  file_permission = "0700"
}

Here I am using the result of the random_uuid as an attribute in the local_file resource:

sudo terraform init
sudo terraform plan
sudo terraform apply

And check the result - the file now contains the generated UUID:

sudo cat /opt/uuid
833732a2-438a-76dd-828f-a7fe2a64f5bb-rg

Dependency

The example above works because the random_uuid provider is run before the local_file provider. But it is also possible to declare an explicit dependency:

resource "local_file" "resource_name" {
  filename = "/opt/uuid"
  content =  "${random_uuid.uuid.result}-rg"
  directory_permission = "0777"
  file_permission = "0700"
  depends_on = [
    random_uuid.uuid
  ]
}

resource "random_uuid" "uuid" {
}

Input Variables

To make configuration files re-useable we can variables instead of hardcoding values:

resource "random_uuid" "uuid" {
}

resource "local_file" "resource_name" {
  filename = var.filename
  content =  "${random_uuid.uuid.result}-rg"
  directory_permission = var.directory_permission
  file_permission = var.file_permission
}

If no variables are provided the Terraform CLI will ask you for values when you try to run this configuration file. But they can also be provided with CLI flags:

terraform apply -var "filename=/opt/uuid" -var "directory_permission=0777" -var "file_permission=0700"

Or use environment variables:

export TF_VAR_filename="/opt/uuid"
export TF_VAR_directory_permission="0777"
export TF_VAR_file_permission="0700"

Or create variable definition file terraform.auto.tfvars

filename = "/opt/uuid"
directory_permission = "0777"
file_permission = "0700"

Default Variables

You can also define default variable in a file variables.tf next to our main configuration:

variable "filename" {
  default = "/opt/uuid"
  type = string
  description = "ID file location"
}
variable "directory_permission" {
  default = "0777"
}
variable "file_permission" {
  default = "0700"
}
sudo terraform plan
sudo terraform apply
sudo cat /opt/uuid
4c148730-9cd1-bc23-04eb-ae3f5e5000d7-rg

Precedence

These defaults will be overridden by the following in this order:

  1. Default variables.tf
  2. Environment Variables
  3. terraform.tfvars
  4. *.auto.tfvars
  5. CLI flags

Variable Types

types can be any, string, numbers, bool, list, set, map, object, tuple:

List

variable "prefix" {
  default = ["Mr.", "Mrs."]
  type = list(string)
}
resource "random_pet" "pet" {
  prefix = var.prefix[0]
}

Map

variable "file-content" {
  type = map(string)
  default = {
    "key1" = "value1"
    "key2" = "value2"
  }
}
variable "file-content" {
  type = map(number)
  default = {
    "key1" = "1"
    "key2" = "2"
  }
}
resource local_file "test" {
  filename = "/opt/test"
  content = var.file-content["key2"]
}

Set

A set is a list without duplicated elements:

variable "prefix" {
  default = ["Mr.", "Mrs.", "Dr."]
  type = set(string)
}
variable "prefix" {
  default = ["123", "456", "789]
  type = set(number)
}

Object

variable "product" {
  type = object({
    model = string
    color = string
    article = number
    compatibility = set(string)
    sale = bool
  })

  default = {
    model = "IN-9408 WQHD"
    color = "purple"
    article = 479831
    compatibility = ["Windows", "macOS", "LINUX"]
    sale = false
  }
}

Tuples

Just like lists but with mixed types:

variable "not_a_list" {
  default = ["Artemis", true, 666]
  type = tuple([string, bool, number])
}

Output Variables

Output values are like the return values of a Terraform module, and have several uses:

  • A child module can use outputs to expose a subset of its resource attributes to a parent module.
  • A root module can use outputs to print certain values in the CLI output after running terraform apply.
  • When using remote state, root module outputs can be accessed by other configurations via a terraform_remote_state data source.

Resource instances managed by Terraform each export attributes whose values can be used elsewhere in configuration. Output values are a way to expose some of that information to the user of your module.

resource "random_uuid" "uuid" {
}

resource "local_file" "resource_name" {
  filename = var.filename
  content =  "${random_uuid.uuid.result}-rg"
  directory_permission = var.directory_permission
  file_permission = var.file_permission
}

output uuid {
  value = random_uuid.uuid.result
  description = "Output the randomly generated UUID"
}

The UUID will now be output the the terminal when you run the job:

sudo terraform apply

random_uuid.uuid: Refreshing state... [id=4c148730-9cd1-bc23-04eb-ae3f5e5000d7]
local_file.resource_name: Refreshing state... [id=59c2ee47c7e515c04607c0cfa3d0028dd32e2f13]

Changes to Outputs:
  + uuid = "4c148730-9cd1-bc23-04eb-ae3f5e5000d7"

You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

uuid = "4c148730-9cd1-bc23-04eb-ae3f5e5000d7"

And it can also be directly queried to make it available for other applications:

terraform output
uuid = "4c148730-9cd1-bc23-04eb-ae3f5e5000d7"

terraform output uuid
"4c148730-9cd1-bc23-04eb-ae3f5e5000d7"

State

The state of the result is stored inside a file terraform.tfstate next to the main configuration file and can also be read from there:

{
  "mode": "managed",
  "type": "random_uuid",
  "name": "uuid",
  "provider": "provider[\"registry.terraform.io/hashicorp/random\"]",
  "instances": [
    {
      "schema_version": 0,
      "attributes": {
        "id": "4c148730-9cd1-bc23-04eb-ae3f5e5000d7",
        "keepers": null,
        "result": "4c148730-9cd1-bc23-04eb-ae3f5e5000d7"
      },
      "sensitive_attributes": [],
      "private": "bnVsbA=="
    }
  ]
}

To update the Terraform State without applying changes to your infrastructure run:

terraform refresh

To apply changes to your infrastructure from the existing state (instead of updating it):

terraform plan --refresh=false