Table of Contents

Lists

In Terraform, lists are one of the fundamental data types used to store an ordered collection of values. They are particularly useful when you need to manage multiple items of the same type, such as a list of IP addresses, server names, or AWS availability zones.

Defining Lists

Lists can be defined using square brackets [] and separating elements with commas.

variable "server_names" {
  type    = list(string)
  default = ["web01", "web02", "db01"]
}

Accessing List Elements

You can access elements using their index.

output "first_server" {
  value = var.server_names[0]  # Outputs "web01"
}

Iterating Over Lists

Lists are often used with loops to create multiple resources.

Using the count Parameter

resource "aws_instance" "servers" {
  count         = length(var.server_names)
  instance_type = "t2.micro"
  ami           = "ami-0c55b159cbfafe1f0"
  tags = {
    Name = var.server_names[count.index]
  }
}

for_each

See also: Dynamic Blocks with Lists

List Functions

Terraform provides built-in functions to manipulate lists.

length(list)

Returns the number of elements in a list.

locals {
  num_servers = length(var.server_names)
}

concat(list1, list2, ...)

Concatenates two or more lists.

locals {
  all_servers = concat(var.web_servers, var.db_servers)
}

element(list, index)

Safely retrieves an element at a given index.

locals {
  server = element(var.server_names, 1)  # "web02"
}

slice(list, start, end)

Extracts a sublist.

locals {
  first_two_servers = slice(var.server_names, 0, 2)
}

Using Lists with for Expressions

You can transform lists using for expressions.

locals {
  uppercased_servers = [for name in var.server_names : upper(name)]
}

Dynamic Blocks with Lists

Dynamic blocks can generate nested configurations based on lists.

resource "aws_security_group" "example" {
  name = "example-sg"
 
  dynamic "ingress" {
    for_each = var.allowed_ports
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
}

Best Practices

Type Constraints

Always specify the type of variables to avoid unexpected behavior.

variable "ip_addresses" {
  type = list(string)
}

Use Descriptive Variable Names

Names should reflect the content or purpose of the list.

Avoid Hardcoding Indices

Instead of accessing elements by index, use loops or functions to handle lists dynamically.

Validate Inputs

Use validation blocks to ensure lists meet certain criteria.

variable “subnets” {

type = list(string)
validation {
  condition     = length(var.subnets) >= 2
  error_message = "At least two subnets are required."
}

}

Common Use Cases

Managing Multiple Resources

Creating multiple resources based on a list of inputs.

resource "aws_subnet" "example" {
  count = length(var.availability_zones)
  vpc_id     = var.vpc_id
  cidr_block = cidrsubnet(var.vpc_cidr_block, 8, count.index)
  availability_zone = var.availability_zones[count.index]
}

Passing Lists to Modules

Modules can accept lists to configure resources.

module "network" {
  source     = "./modules/network"
  subnets    = var.subnets
}

Advanced List Manipulation

Flattening Nested Lists

locals {
  nested_list = [["a", "b"], ["c", "d"]]
  flat_list   = flatten(local.nested_list)  # ["a", "b", "c", "d"]
}

Filtering Lists

locals {
  numbers    = [1, 2, 3, 4, 5]
  even_numbers = [for num in local.numbers : num if num % 2 == 0]
}

Result: [2, 4]

Debugging Lists

Use output blocks or the terraform console for debugging.

output “server_list” {

value = var.server_names

}