How to Use Terraform Index Function [Examples]

Terraform’s index function returns the position of a value in a list. You can use it to align related lists or turn a readable value into a numeric position that another expression can use.

Mariusz Michalowski

This article explains how index works through three practical infrastructure examples.

What is the Terraform index function?

The index function in Terraform searches a list and returns the position of the first matching element. It takes two arguments: the list to search and the value to find.

  • If the value exists, Terraform returns its zero-based position.
  • If the value is missing, Terraform returns an error.
  • If the value appears more than once, Terraform returns the position of the first match.

That makes index a good fit when you know the value exists and need to reuse its position elsewhere.

The syntax is simple:

index(list, value)

index is most useful when you are already working with ordered lists. If you control the data structure, a map is often easier to read and maintain.

Note: If there is any chance the value is missing, validate it first with contains(), or use a map with lookup() instead.

The index function works the same way in OpenTofu. The syntax and behavior in all examples below apply to both tools.

Example 1: Mapping an environment name to a CIDR block

A common use case for index is pairing two lists that share the same order. Imagine you have one list of environment names and another list of matching CIDR blocks. You know the environment name at runtime, but you need the corresponding CIDR block.

variable "environment" { default = "staging" } locals { env_names = ["development", "staging", "production"] env_cidrs = ["10.0.0.0/24", "10.1.0.0/24", "10.2.0.0/24"] env_index = index(local.env_names, var.environment) cidr_block = local.env_cidrs[local.env_index] } resource "aws_vpc" "main" { cidr_block = local.cidr_block }

Here, index finds that "staging" is at position 1 in env_names. Terraform then uses that same position to select "10.1.0.0/24" from env_cidrs.

This pattern works well when your data already exists as ordered lists. If you are defining the relationship yourself, a map is usually clearer.

Example 2: Setting log retention by environment

Another use case for the index function is resolving a numeric value from a name.

Imagine you want to assign a CloudWatch log retention period based on the environment. You have one list of environment names and a matching list of retention values.

variable "environment" { default = "staging" } locals { env_names = ["development", "staging", "production"] retention_days = [7, 30, 90] env_index = index(local.env_names, var.environment) log_retention = local.retention_days[local.env_index] } resource "aws_cloudwatch_log_group" "app" { name = "/app/${var.environment}" retention_in_days = local.log_retention }

Here, index finds that "staging" is at position 1 in env_names. Terraform then uses that same position to select 30 from retention_days.

This example is fully self-contained and follows the same parallel-list pattern as Example 1. If you are defining the mapping yourself, a map is usually the cleaner choice.

Example 3: Assigning an instance type by role

In larger deployments, you might keep a fixed list of server roles and a matching list of instance types. When a module variable provides the role name, index can resolve the correct instance type.

variable "server_role" { description = "The role assigned to this server" default = "worker" } locals { roles = ["bastion", "worker", "controller"] instance_types = ["t3.micro", "t3.large", "m5.xlarge"] role_index = index(local.roles, var.server_role) instance_type = local.instance_types[local.role_index] } resource "aws_instance" "node" { ami = "ami-0abcdef1234567890" instance_type = local.instance_type }

When server_role is set to "worker", index returns 1, so Terraform selects t3.large. If you change the role to "controller", Terraform returns 2 and selects m5.xlarge.

This approach keeps the relationship between roles and sizes in one place. That said, if the mapping is static, a map is often easier to understand than parallel lists.

Key points

Terraform’s index function works best when your data is already organized as ordered lists and you need to find a position by value. If the value might be missing, guard the call with contains() first. And if you are modeling named relationships yourself, a map with lookup() is usually clearer and easier to maintain.

And if you are modeling named relationships yourself, a map is usually clearer and easier to maintain. For known keys, direct map access with var.my_map[key] is the idiomatic choice. Use lookup() only when you need a fallback default value for a missing key.

Terraform is really powerful, but to achieve an end-to-end secure GitOps approach, you need to use a product that can run your Terraform workflows. Spacelift takes managing Terraform to the next level by giving you access to a powerful CI/CD workflow and unlocking features such as:

  • Policies (based on Open Policy Agent)
  • Multi-IaC workflows
  • Self-service infrastructure
  • Integrations with any third-party tools

If you want to learn more about Spacelift, create a free account today or book a demo with one of our engineers.

Note: New versions of Terraform are released under the BUSL license, but everything created before version 1.5.x remains open source. OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.

Manage Terraform better and faster

If you are struggling with Terraform automation and management, check out Spacelift. It helps you manage Terraform state, build more complex workflows, and adds several must-have capabilities for end-to-end infrastructure management.

Learn more