Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Variables, count and for_each in OpenTofu

After this lesson: I can make several of a thing from a variable.

60 min lesson12 commandsOpenTofu lab: infrastructure as code with plan, apply and state on one VM

One block can make many things

In OpenTofu, a variable is a named value that a project reads, so the same files can be used with different values. Two more tools let one block make several things: count makes numbered copies and for_each makes one copy for each name in a list.

A market with ten lanes and six stalls does not need sixteen blocks.

By the end of this lesson

Note

I can make several of a thing from a variable.

Key words

WordWhat it meansExample
Variablea named value given to a projectvar.lanes
Outputa value a project prints when it finishes
countmakes that many copies, numbered from 0
for_eachmakes one copy for each name
Instanceone copy made by count or for_each
Addressthe unique name of one instanceterraform_data.lane[0]

The cells students fill in are left blank in this preview.

A variable is a value set from outside

A variable block gives a value a name, a type and, usually, a default. The rest of the project reads it as var.name.

You can replace the default with -var name=value on the command line. An output block prints a value when the apply finishes, so you can read the result without opening the state.

A project with two variables and an output

Code: bash
# main.tf (the three blocks are shown in the order the file holds them)
variable "market" {
  type    = string
  default = "Saturday market"
}

variable "lanes" {
  type    = number
  default = 2
}

resource "terraform_data" "lane" {
  count = var.lanes
  input = format("%s lane %d", var.market, count.index + 1)
}

output "first_lane" {
  value = terraform_data.lane[0].output
}

Make a folder for the market

Command
mkdir -p ~/iac-demo/market

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Go into it

Command
cd ~/iac-demo/market

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the file shown above

Command
printf '%s\n' 'variable "market" {' '  type    = string' '  default = "Saturday market"' '}' '' 'variable "lanes" {' '  type    = number' '  default = 2' '}' '' 'resource "terraform_data" "lane" {' '  count = var.lanes' '  input = format("%s lane %d", var.market, count.index + 1)' '}' '' 'output "first_lane" {' '  value = terraform_data.lane[0].output' '}' > main.tf

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Prepare the folder

Command
tofu init -no-color | grep successfully

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Apply with the default of two lanes

Command
tofu apply -auto-approve -no-color | grep "Apply complete"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Read the output

Command
tofu output -no-color

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

count makes numbered copies

Setting count = var.lanes makes that many copies of the block. Inside it, count.index is the copy's position, and it starts at 0.

Each copy has its own address, with the position in square brackets. Growing the count adds copies at the end. Shrinking it removes copies from the end.

Worked example: lanes set to 3

count.indexAddressInput it gets
0terraform_data.lane[0]Saturday market lane 1
1terraform_data.lane[1]Saturday market lane 2
2terraform_data.lane[2]Saturday market lane 3

The cells students fill in are left blank in this preview.

Plan four lanes instead of two

Command
tofu plan -no-color -var lanes=4 | grep -E "will be|^Plan"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Plan one lane instead of two

Command
tofu plan -no-color -var lanes=1 | grep -E "will be|^Plan"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

for_each makes one copy for each name

for_each takes a set of names, or a map, and makes one copy for each. Inside the block, each.key is that copy's name.

The address carries the name in quotes and square brackets. A set has no repeats and OpenTofu puts the names in order, so the order you type them does not matter.

Add named stalls to the same file

Command
printf '%s\n' '' 'variable "stalls" {' '  type    = set(string)' '  default = ["bread", "cakes"]' '}' '' 'resource "terraform_data" "stall" {' '  for_each = var.stalls' '  input    = format("%s: %s", var.market, each.key)' '}' >> main.tf

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Plan: two stalls to create

Command
tofu plan -no-color | grep -E "will be|^Plan"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Apply

Command
tofu apply -auto-approve -no-color | grep "Apply complete"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

List every address in the state

Command
tofu state list

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A common mistake

Warning

Many people think count and for_each are two spellings of one tool. They are not. count gives copies a position, so [0] means the first. for_each gives copies a name, so ["bread"] always means the bread stall. Use names whenever the things have names.

Expanding both into addresses

Code: python
# Expand count and for_each into the names OpenTofu uses.
lanes = 3
stalls = ["cakes", "bread", "cakes"]
print("count:")
for i in range(lanes):
    print("  terraform_data.lane[" + str(i) + "]")
# A set has no repeats, and OpenTofu puts the keys in order.
keys = []
for name in stalls:
    if name not in keys:
        keys.append(name)
keys.sort()
print("for_each:")
for key in keys:
    print('  terraform_data.stall["' + key + '"]')
# Change lanes to 5 and add "fruit" to stalls, then run it again.

About this lab and its source: OpenTofu lab: infrastructure as code with plan, apply and state on one VM

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.