OpenTofu: A Beginners Guide to a Terraform Fork Including Migration From Terraform

Working with OpenTofu

Meta Arguments amp Count and for each

In this guide, you’ll learn how to provision multiple EC2 instances with the count and for_each meta-arguments in OpenTofu. Both approaches let you duplicate a single resource block, but differ in indexing and lifecycle behavior.

Meta-ArgumentKey CharacteristicBest Use Case
countInteger-based, zero-indexedCreate a fixed number of identical resources
for_eachKey-based, uses a set or map of valuesStable addressing of dynamic or named items

1. Using count

The count meta-argument accepts an integer and creates that many instances of a resource.

resource "aws_instance" "web" {
  ami           = var.ami
  instance_type = var.instance_type
  count         = 3
}

variable "ami" {
  default = "ami-06178cf087598769c"
}

variable "instance_type" {
  default = "m5.large"
}

Run:

tofu apply

Then verify:

tofu state list
# aws_instance.web[0]
# aws_instance.web[1]
# aws_instance.web[2]

Dynamic count with length()

Instead of hardcoding an integer, drive count from a list:

variable "webservers" {
  type    = list(string)
  default = ["web1", "web2", "web3"]
}

resource "aws_instance" "web" {
  ami           = var.ami
  instance_type = var.instance_type
  count         = length(var.webservers)

  tags = {
    Name = var.webservers[count.index]
  }
}

variable "ami" {
  default = "ami-06178cf087598769c"
}

variable "instance_type" {
  default = "m5.large"
}

Here, each instance tag is assigned by its count.index:

  • count.index = 0Name = "web1"
  • count.index = 1Name = "web2"
  • count.index = 2Name = "web3"

Reindexing with `count`

Removing or reordering items in the webservers list causes all subsequent resources to be reindexed. This can lead to unintended in-place updates instead of only removing the orphaned resource.

Example plan when deleting "web1":

# aws_instance.web[0] will be updated (web1 → web2)
# aws_instance.web[1] will be updated (web2 → web3)
# aws_instance.web[2] will be destroyed (web3)

2. Using for_each

The for_each meta-argument uses a set or map, giving each resource a stable key based on its value.

variable "webservers" {
  type    = set(string)
  default = ["web1", "web2", "web3"]
}

resource "aws_instance" "web" {
  ami           = var.ami
  instance_type = var.instance_type
  for_each      = var.webservers

  tags = {
    Name = each.value
  }
}

Run and list state:

tofu apply
tofu state list
# aws_instance.web["web1"]
# aws_instance.web["web2"]
# aws_instance.web["web3"]

If you remove "web1" from the set and run tofu plan, only that resource is destroyed:

# aws_instance.web["web1"] will be destroyed
Plan: 0 to add, 0 to change, 1 to destroy.

This ensures stable resource addressing and prevents unnecessary updates.


Watch Video

Watch video content

Previous
Demo Data Sources