What Are Meta-Arguments in Terraform?
What Is Terraform Meta - Arguments? I'm really into meta-arguments in Terraform. They are special arguments that you chuck into the resource, data, or module blocks to tweak the behavior of these blocks. Different block types in Terraform have different meta-arguments that they support. For instance, the resource block goes for the count meta-argument. This guy also supports the for_each meta-argument plus a few others.
Maximizing Resource Deployment Efficiency
A resource block in Terraform by default splurts out just the one infrastructure resource, could be a storage bucket or load balancer or compute instance. But what if you want to kickstart oodles of infrastructure resources of the same type? That's where meta-arguments like count and for_each come in.
Harnessing the Power of Terraform's Count and For_each Meta-Arguments
These are the two saviors when you need to get your mitts on multiple instances of an infrastructure resource in Terraform, but what is Terraform by the way? Instead of tapping away at individual resource blocks for each instance of an infrastructure resource you're gunning for, you can bang out a single resource block then slam in a meta-argument like count or for_each to whip up as many instances as you fancy.
Banging out a resource block for each bit of infrastructure you're slinging with count isn't the best move. I'll explain why in a bit.
Why Is the 'count' Meta-Argument Problematic?
Lists are ordered collections which means that the order of every element within a list is significant. The count meta_argument works with a list type, meaning every element has an index position - as seen below - when you run the Terraform state list command to list all resources in the Terraform.tfstate file
aws_instance.sandbox[0]
aws_instance.sandbox[1]
aws_instance.sandbox[2]
Maintaining Stability in Terraform Infrastructure with Index Referencing
From above, every element is referenced using an index number, and not even the string values in the sandboxes variable in the code snippet seen earlier, i.e., default = ["sandbox_one", "sandbox_two", "sandbox_three"]. If you remove any element that is not the last element in the list above, you will get unexpected infrastructure resource changes when you run Terraform plan to see the execution plan.
For example, if you remove the element at index 0 (i.e., sandbox_one), the following happens:
- The current element at index 1 (i.e., sanbox_two) will now become the new element at index 0, i.e., sandbox_two is now at index 0
- The current element at index 2 (i.e., sanbox_three) will now be the new element at index 1, i.e., sandbox_three is now at index 1
- There will be no element at index 3, and this index will be destroyed
This is where the flexibility of the for_each meta-argument comes into play. Since for_each works with unordered collections like a map or set, elements are instead referenced by string values. Let’s dive deeper into the for_each meta-argument in the following sections.
Empowering Engineering Teams with Zeet's CI/CD Platform
Zeet helps you to get more from your cloud, Kubernetes, and Terraform investments and helps your engineering team become strong individual contributors through our CI/CD & deployment platform.
Contact Zeet to learn more about how Zeet helps you get seamless cloud deployments every time, and helps your team to become a top-performing engineering team.
Related Reading
How the Terraform for_each Meta-Argument Improves Terraform Configurations
Terraform for_each is a meta argument that helps in creating multiple instances of a defined resource. It also provides us with the flexibility of dynamically setting the attributes of each resource instance created, depending on the type of variables being used to create real-world replicas.
for_each primarily works with a set of strings (set(string)) and map of strings (map(string)). The provided string values are used to set instance-specific attributes.
Customizing Resources with Terraform's for_each
For example, when creating multiple subnets, we can specify different CIDR ranges for each subnet created using the same resource block.
When for_each meta-argument is used in a resource block, a special object each is automatically available to refer to each instance created by the for_each. each object is used to refer to the values provided in set, and key and value pairs provided in a map type variable
Examples of Using the for_each Meta-Argument in Terraform
Let us take a real-world example to better understand the for_each meta-argument.
Say you have a map of instance configurations where the string value for each key is an identifier for the EC2 instance, and the value is another map containing the instance type and AMI ID.
Let’s break everything down:
- variable "instances" defines a map where each element represents an EC2 instance configuration. For instance, "amzlinux" and "ubuntu" are identifiers for these configurations, each specifying an AMI ID and an instance type.
- resource "aws_instance" "servers" uses for_each to iterate over each element in the var.instances map. For each element, it creates an EC2 instance with the specified AMI and instance type.
- each.key in this context refers to the key in the map (e.g., "amzlinux", "ubuntu"), which we use to uniquely name each instance with the Name tag.
each.value.ami and each.value.instance_type access the nested values for each instance's configuration. - After successfully running the Terraform workflow (init->plan->apply), we have provisioned these two instances using for_each.’
Using for_each with map
The map type provides a “set” of key value pairs, offering more customization options for more complex recurring resources to be provisioned.
In this case, the number of instances to be created is equal to the length of the map object. Along with the each.value, we can also leverage the string value stored in each.key. This is a next step where instead of one, there are two attributes to be set dynamically using the same Terraform resource block.
The instance_map variable above has a default value of two key-value pairs.
We use this variable in the for_each meta-argument to create two EC2 instances, as shown in the configuration block below.
The for_each meta-argument in the above code snippet is responsible for creating two EC2 instances.
The Name and ID tag refer to the value and key strings set in the default value of the instance_map variable. The plan command output confirms the same.
Related Reading
- Terraform Apply Auto Approve
- Terraform Module
- Terraform vs Cloudformation
- Terraform AWS Security Group
- Terraform Kubernetes Provider
- Terraform AWS Lambda
- Datadog Terraform
- Terraform Cloud Pricing
- Terraform IAM Role
- Terraform Debug
- Terraform Docker
- Github Actions Terraform
- Terraform Import Existing Resources
- Terraform ECS
- DevOps Terraform
- Terraform Automation
- Terraform CI CD
- Terraform Workflow
- Terraform Security
- Terraform Orchestration
- Terraform Multi Cloud
- Terraform No Code Provisioning
- Terraform Migrate State
- Terraform State Management
- Terraform AWS RDS
- What is Terragrunt
- Terragrunt vs Terraspace
- Terraform Multiple Environments
- Terraform Multiple Users
- Upgrade Terraform Version
- Terraform Test
- Terraform Commands
- Terraform Alternatives
- Terraform Stacks
- Crossplane Vs Terraform
- Terraform Import
- Terraform Tutorial
- Terraform Dynamic Block
How Can Terraform for_each Be Applied Within Dynamic Blocks?
In Terraform, dynamic blocks come in handy when constructing repetitive nested blocks without code duplication. One common use case is the incorporation of the for_each meta-argument in dynamic blocks. The for_each argument allows the creation of a distinct resource for each element in a map or list. Let’s take a look at a code snippet to understand this concept better:
In the code above, we are defining an AWS security group resource that permits ingress traffic into the specified VPC using vpc_id variable. Instead of manually duplicating the nested ingress block, a dynamic block is employed within the resource block, which dynamically generates repeatable ingress blocks using the for_each argument.
Within a dynamic block, the block’s name (e.g., ingress in the example) is used and not each. By leveraging for_each with dynamic blocks, multiple nested blocks can be easily created It is important to note that dynamic blocks in Terraform can also be utilized inside other block types, such as data, provider, and provisioner blocks.
Key Considerations When Working With the for_each Meta-Argument?
When working with the for_each meta-argument in Terraform, it's essential to understand how to reference both blocks and block instances. This involves using specific syntax to refer to each instance and the block itself.
For example, when referencing a resource block, you would use: <resource_type>.<resource_name>. To refer to an instance, you would use <resource_type>.<resource_name>[<key>].
Limitations and Considerations with for_each in Terraform
There are several important limitations and considerations to keep in mind when using the for_each meta-argument in Terraform. For example, the keys or values used for iteration in for_each serve as identifiers for the multiple resources they create. This means that they are always visible in the Terraform UI output and the state file.
Handling Sensitive Values in Terraform's for_each
Sensitive values cannot be used as arguments in for_each implementations. This includes sensitive input variables, sensitive outputs, and sensitive resource attributes.
Preparing Keys and Values for Terraform's for_each
The keys or values of a for_each must be known before a Terraform apply operation. These values cannot depend on the result of any impure function like a timestamp because they are evaluated later during the main evaluation step. It is also crucial to use descriptive keys or values to easily identify resources.
Expressions and Chaining for_each in Terraform
When using for_each in Terraform, the types of values used must be a set or a map. If you have a list of values, you can use the to set type conversion function to convert it to a set. Nested data structures that are not suitable values for for_each can be handled using the for construct or other helpful built-in functions like flatten.
It is important to note that Terraform does not support nested loops in the resource block. You can only use one for_each meta-argument, and it cannot be nested. Chaining for_each can be done when there is a one-to-one relationship between objects. This allows you to use one resource as the for_each of another to create associations between resources, such as provisioning an AWS EBS volume for every EC2 instance.
Related Reading
- Atlantis Terraform
- Terraform Tools
- Terraform Cloud Alternatives
- Spacelift vs Terraform Cloud
- Atlantis Alternatives
- Scalr vs Terraform
- Env0 vs Terraform Cloud
- Terraform Testing Tools
- Ansible vs Terraform
- Terraform vs Ansible
Advantages of Using Terraform For_each
Terraform for_each is a game-changer when it comes to managing dynamic resources. With for_each, I can define a list of project names and provision a dynamic number of S3 buckets based on that list. This ability to dynamically create resources based on a collection of items allows for flexible and efficient infrastructure provisioning.
Conditional Resource Creation with Terraform for_each
When combined with Terraform's conditional expressions, for_each enables the creation of resources based on specific criteria within the data it iterates over. This granular control allows me to conditionally create resources, ensuring that only the necessary resources are created, updated, or destroyed. This level of control adds a layer of sophistication to infrastructure management that was not previously possible.
Improved Code Reusability with Terraform for_each
With for_each, I can write modular and reusable code. Instead of duplicating resource blocks for each instance of a resource, I can define a single block that is applied to each item in a collection. This approach abstracts common configuration elements into a parameterized block, allowing for dynamic resource creation. I can even combine for_each with modules to keep my code DRY (Don't Repeat Yourself), promoting efficiency and maintainability in my infrastructure as code.
Get Control of Your Releases With Zeet's CI/CD & Deployment Platform for Kubernetes and Terraform
Zeet helps you to get more from your cloud, Kubernetes, and Terraform investments. Our platform is designed to assist your engineering team in becoming strong individual contributors.
With Zeet, your team can achieve seamless cloud deployments every time. Our CI/CD & deployment platform is designed to help your team become a top-performing engineering team. Reach out to Zeet to discover how we can help you maximize your investments in cloud, Kubernetes, and Terraform and help your team achieve its full potential.