DEV Community

Cover image for Terraform Meta-Arguments
Anil KUMAR
Anil KUMAR

Posted on

Terraform Meta-Arguments

Today marks the Day 8 of 30 Days Terraform challenge and today we will deep dive into the Terraform meta arguments and how we have multiple types of meta-arguments in Terraform and what each value does each meta-argument holds.

Generally while creating any resource in terraform, we will have multiple arguments out of which some are mandatory while some are optional.

resource "aws_s3_bucket" "example" {
  bucket = "my-tf-test-bucket"

  tags = {
    Name        = "My bucket"
    Environment = "Dev"
  }
}
resource "aws_instance" "example" {
  ami           = "ami-005e54dee72cc1d00"
  instance_type = "t3.micro"
  count = var.instance_count
  tags = {
    Environment = var.environment
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above block, both bucket and tags are optional but not mandatory. If you omit them, terraform will create those resources with some random names instead of our customized ones. But If you look at aws_instance: ami and instance_type is mandatory. Similarly Now we have meta arguments in Terraform which will make our life using terraform better.

Different Types of Meta Arguments:

There are 5 different types of Meta Arguments in Terraform which includes depends_on, count, for_each, provider, lifecycle.

In this blog, we will mainly focus on 3 Meta-arguments: depends_on, count and for_each. Will deep-dive into lifecycle meta-argument in the next blog.

Also Provider meta-argument is just a plugin which we will use to connect with other cloud-providers like AWS, Azure and GCP.

1. Depends_on:

As the name indicates, depends_on in the Terraform sense means depending on other resource before getting created. This again has 2 types such as Implicit dependency and explicit dependency.

Implicit Dependency:

This means we do not need to add the meta-argument "depends_on", terraform will automatically which resource to create first and then follows the other resource.

Example: You are trying to create Ec2 instance in a subnet and a VPC. Here for subnet to exist first, VPC needs to be created first followed by EC2. So Terraform will automatically understands this and will create VPC followed by EC2 resources.

Explicit Dependency:

This means as the name indicates we need to explicitly define the name or mention that first resource needs to be created than the second resource. If we do not mention them, it will cause issues because it will try to create second resource first but since we have dependency on first resource where the internal networking or communication would fail.

resource "aws_iam_role_policy" "example" {
  name   = "example"
  role   = aws_iam_role.example.name
  policy = jsonencode({
    "Statement" = [{
      "Action" = "s3:*",
      "Effect" = "Allow",
    }],
  })
}

resource "aws_instance" "example" {
  ami           = "ami-a1b2c3d4"
  instance_type = "t2.micro"


  # However, if software running in this EC2 instance needs access
  # to the S3 API in order to boot properly, there is also a 
  # dependency on the aws_iam_role_policy that Terraform cannot
  # automatically infer, so you must declare it explicitly:
  depends_on = [
    aws_iam_role_policy.example
  ]
}
Enter fullscreen mode Exit fullscreen mode

In the above code block, you can see we have mentioned depends_on meta argument in the aws_instance resource creation for iam_role, so IAM role will be created first followed by EC2 instance.

Count:

Count is nothing but helping count the value. It is just like a iterator. It helps counting the values in a list or set. We have unknowingly used count in previous blogs too for creation of Ec2 Instances while trying to learn about Varibale types.

Example: Lets try creating 2 S3 buckets using the count meta-argument.

resource "aws_s3_bucket" "example" {
  count = 2
  bucket = var.bucket_names[count.index]

  tags = var.tags
}

variable "bucket_names" {
    type = list(string)
    default = ["some-random-string1", "some-random-string2"]
}

 variable "tags" {
     type = map(string)
     description = "list of tags for EC2 instances"
     default = {
       Environment = "dev"
       Name = "dev-instance"
 } 
Enter fullscreen mode Exit fullscreen mode

In the above code block, we can see we have mentioned count=2, so the above S3_resource will execute 2 times and each time it executes count.index will start from 0 and will increment until count matches 2.

Here in the above block, we can see I have written Terrform definition only once, but we can see 2 buckets getting created after doing terraform apply.

terraform plan

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_s3_bucket.example[0] will be created
  + resource "aws_s3_bucket" "example" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "some-random-string1"
      + bucket_domain_name          = (known after apply)
      + bucket_prefix               = (known after apply)
      + bucket_region               = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = "us-west-1"
      + request_payer               = (known after apply)
      + tags                        = {
          + "Environment" = "Dev"
          + "Name"        = "My bucket"
        }
      + tags_all                    = {
          + "Environment" = "Dev"
          + "Name"        = "My bucket"
        }
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

  # aws_s3_bucket.example[1] will be created
  + resource "aws_s3_bucket" "example" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "some-random-string2"
      + bucket_domain_name          = (known after apply)
      + bucket_prefix               = (known after apply)
      + bucket_region               = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = "us-west-1"
      + request_payer               = (known after apply)
      + tags                        = {
          + "Environment" = "Dev"
          + "Name"        = "My bucket"
        }
      + tags_all                    = {
          + "Environment" = "Dev"
          + "Name"        = "My bucket"
        }
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.
Enter fullscreen mode Exit fullscreen mode

3. For_EACH:

The above approach works if you have used list(string) in variables.tf, but when u start to use set(string). The above approach will not work because we cannot iterate set of strings through index number in set, so for that we will use For_Each Meta Argument.

For_each is a kind of of for loop which iterates through all the values in a loop or index.

resource "aws_s3_bucket" "example" {
  for_each = var.bucket_names
  bucket = each.value

  tags = var.tags
}

variable "bucket_names" {
    type = set(string)
    default = ["some-random-string1", "some-random-string2"]
}

 variable "tags" {
     type = map(string)
     description = "list of tags for EC2 instances"
     default = {
       Environment = "dev"
       Name = "dev-instance"
 } 
Enter fullscreen mode Exit fullscreen mode

In the above block, the for_each argument will store all teh bucket_names variable within themself, and when we use each.value. It will go through the loop and gives us the required result of the string.

Since it is a set of string, we can reference that either through each.key or each.value as set contains only strings both key and value will point out to a same value.

But when we use maps, each.key and each.value will have different values as maps are nothing but a collection of key-value pairs.

map(string) = { name="piyush", series="tf"}

In the above example, each.key will iterate over name and series and each.value will iterate over piyush and tf.

Conclusion:

Terraform meta-arguments changed how I think about Infrastructure as Code. HCL is providing all the feature which we will use in Programming languages through these meta-arguments. Instead of long repetitive files and logic blocks, we can use Meta-Arguments which will be majorly used when we start building projects to keep the Terraform code clean, organized and reusable.

Below is the Youtube Video for reference: Tech Tutorials with Piyush — “Terraform Meta Arguments”

Top comments (0)