Contents

Loops with Terraform

Introduction

Loops are a powerful tool in any programming language, and Terraform is no exception. They allow you to repeat a set of instructions multiple times, potentially with different values each time. This can be very useful for creating multiple similar resources in Terraform, such as a set of identical EC2 instances or S3 buckets.

To use loops in Terraform, you can use the count argument, which allows you to specify the number of times a resource should be created. You can also use the for_each argument to iterate over a list or map of values.

Here’s an example of using the count argument to create a set of EC2 instances:

1
2
3
4
5
6
resource "aws_instance" "example" {
  count = 3

  ami           = "ami-123456"
  instance_type = "t2.micro"
}

In this example, three EC2 instances will be created with the specified AMI and instance type.

Using the for_each argument, you can iterate over a list or map of values and create a resource for each one. Here’s an example using a list:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
variable "instance_names" {
  type = list(string)

  default = [
    "web-server-1",
    "web-server-2",
    "web-server-3",
  ]
}

resource "aws_instance" "example" {
  for_each = var.instance_names

  ami           = "ami-123456"
  instance_type = "t2.micro"
  tags          = {
    Name = each.value
  }
}

In this example, three EC2 instances will be created, and each one will be given a name based on the corresponding value in the instance_names list.

Using loops in Terraform can greatly simplify your code and make it easier to manage and maintain. It’s a technique that’s well worth learning and incorporating into your Terraform projects.

Real-world scenario

Here is an example of a real-world scenario using the for_each argument in Terraform to create virtual machines in Azure:

Imagine you are working for a client that needs to create a set of virtual machines for a new project. The client has a list of names and sizes for the virtual machines that they want to create. You can use the for_each argument in Terraform to iterate over this list and create the virtual machines. In this scenario, lets say our client already defined a virtual network and working subnet that we can reference called megaservers.

Variables / locals

To define a locals file in Terraform, you can use the locals block in your configuration. The locals block allows you to define local variables that can be used within the same module.

Here is an example of how you might define a locals file in Terraform:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
locals {
  vm = {
    server1 = {
      location             = "West Europe"
      admin_username       = "secretsuperadmin"
      size                 = "Standard_D2ds_v5"
      publisher            = "Canonical"
      offer                = "UbuntuServer"
      sku                  = "18.04-LTS"
      version              = "latest"
      caching              = "ReadWrite"
      storage_account_type = "StandardSSD_LRS"
      os_disk_size_gb      = "30"
    }
    server2 = {
      location             = "West Europe"
      admin_username       = "secretsuperadmin"
      size                 = "Standard_D2ds_v5"
      publisher            = "Canonical"
      offer                = "UbuntuServer"
      sku                  = "18.04-LTS"
      version              = "latest"
      caching              = "ReadWrite"
      storage_account_type = "StandardSSD_LRS"
      os_disk_size_gb      = "30"
    }
  }
}

Creating the resource group

Before we go on and create the virtual machines our customer has listed let’s create the resource groups, and let’s base our resource groups on the values of the locals. For this example, we are using the keys server1 and server2 from the locals to define our resource group names.

The each.key expression in Terraform allows you to access the keys of a map in a for_each block. The each.key expression is used in combination with the for_each argument to iterate over a map and create resources for each key-value pair in the map.

1
2
3
4
5
resource "azurerm_resource_group" "rg" {
  for_each = local.vm
  name     = "rg-magical-${each.key}"
  location = each.value.location
}

Creating the Virtual Machines

The azurerm_network_interface resource is used to create a network interface in Azure. A network interface is a logical networking component that represents a network card in Azure. It provides the ability to connect a virtual machine or other resources to a virtual network. In this example we create this the same way we did the resource groups, using the keys for naming and to point to the correct resource group.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

resource "azurerm_network_interface" "nic" {
for_each = local.vm
  name                = "magical-${each.key}-nic"
  location            = each.value.location
  resource_group_name = azurerm_resource_group.rg[each.key].name
  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.megaservers.id 
    private_ip_address_allocation = "Dynamic"
  }
}

Lets go on to create the Virtual machines. In this example we go further into our locals to define the vm, like previous resources we create the name using the word magical in combination with the key magical-${each.key}.

The each.value expression in Terraform allows you to access the values of a map or list in a for_each block. The each.value expression is used in combination with the for_each argument to iterate over a map or list and create resources for each element in the map or list.

In the example we can see that each.value.location referes to the location value in the locals the same goes for the other values like each.value.size, each.value.caching, etc…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
resource "azurerm_linux_virtual_machine" "vm" {
  for_each                        = local.vm
  name                            = "magical-${each.key}"
  resource_group_name             = azurerm_resource_group.rg[each.key].name
  location                        = each.value.location
  size                            = each.value.size

  admin_ssh_key {
    username   = each.value.admin_username
    public_key = file("~/.ssh/id_rsa.pub")
  }

  
  network_interface_ids = [
    azurerm_network_interface.nic[each.key].id,
  ]

 
  os_disk {
    name                 = "magical-${each.key}-osdisk"
    caching              = each.value.caching
    storage_account_type = each.value.storage_account_type
    disk_size_gb         = each.value.os_disk_size_gb
  }

  source_image_reference {
    publisher = each.value.publisher
    offer     = each.value.offer
    sku       = each.value.sku
    version   = each.value.version
  }
}

Conclusion

Using loops in Terraform can be a smart and efficient way to create multiple resources in a repeatable and modular manner. Here are a few reasons why using loops in Terraform can be a timesaver:

  1. Avoid repetition: Instead of writing separate blocks of code to create multiple resources, loops allow you to create multiple resources using a single block of code. This can help to reduce duplication and make your code more readable and maintainable.

  2. Simplify configuration: Using loops allows you to define resources in a list or map format, which can be easier to understand and modify than writing out each resource individually. You can also use variables and expressions to customize the configuration of each resource, making it easy to adapt your code to different environments or requirements.

  3. Streamline resource management: Loops make it easy to manage a large number of resources by allowing you to apply changes or updates to all resources in a single operation. This can save time and effort when compared to managing resources individually.

Overall, using loops in Terraform can help you write more efficient and modular code, which can save time and effort when managing your infrastructure.

I hope this helps! Let me know if you have any questions.