Menu

Virtual Geek

Tales from real IT system administrators world and non-production environment

Terraform manage similar resources with for_each loop inside modules

After working on few of the Azure Terraform (Infrastructure as code) projects, I found I had to deploy same resource again and again, instead of defining same resource manually, I can use for_each loop to deploy resources smartly inside module block. In this example I am going to deploy Azure Virtual Network with multiple subnets in it, Below is the hierarchy of files and folders of terraform scripts as shown in the screenshot.

Here I have 2 example articles to understand basics of terraform modules Writing and Using Terraform modules and Terraform Using one module variable in another module for beginners.

Microsoft terraform main.tf variable.tf output.tf tree f modules hashicorp subnets virtual_network module for loop for_each key value map json toset filter.png

Download terraform script Terraform_Module_for_each_loop_example_of_same_resoruce.zip here or it is also available on github.com.

In the module, below are resource block defined to deploy Azure Virtual Network (it is plain resource blocks with variable file) and I haven't mentioned anything about subnet in it and outputting vnet_name and rg_name values.

#Location: .\Modules\virtual_network
#Azure virtual network module main.tf file
resource "azurerm_virtual_network" "vnet" {
  name                = var.name
  resource_group_name = var.resource_group_name  
  location            = var.location
  address_space       = var.address_space
}

#Azure virtual network module output.tf file
output "vnet_name" {
    value = var.name
}

output "rg_name" {
    value = var.resource_group_name
}

#Azure virtual network module variable.tf file
variable "name" {
  type    = string
  default = "example_vnet"
}

variable "resource_group_name" {
  type    = string
  default = "vCloud-lab.com"
}

variable "location" {
  type    = string
  default = "West US"
}

variable "address_space" {
  type    = list(any)
  default = ["10.0.0.0/16"]
}

Below is the subnet scripts block inside module file, it is using plain single subnet resource ready to deploy inside Azure Virtual Network. If you see below, I am not using any for_each loop here.

#Location: .\Modules\subnet
#Azure Subnet module main.tf file
resource "azurerm_subnet" "subnet" {
  name                 = var.name
  resource_group_name  = var.resource_group_name
  virtual_network_name = var.virtual_network_name
  address_prefixes     = var.address_prefixes
}

#Azure Subnet module variable.tf file
variable "name" {
  type = string
  default = "web_subnet"
}

variable "resource_group_name" {
  type = string
  default = "vCloud-lab.com"
}

variable "virtual_network_name" {
  type = string
  default = "example_vnet"
}

variable "address_prefixes" {
  type = list
  default = ["10.0.2.0/24"]
}

This is main.tf  file content on the root folder, I have defined providers, VNET resource. Regarding subnet, One variable block for subnet where I have mentioned type map data with multiple subnet details. The main part in the script I am using depends_on in the module to ensure virtual network is created before creating subnets.

#Location: .\
#Terraform module main.tf file
# We strongly recommend using the required_providers block to set the
# Azure Provider source and version being used
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">=2.86.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}
}

module "virtual_network" {
  source              = "./modules/virtual_network"
  name                = "test_vnet"
  resource_group_name = "vCloud-lab.com"
  location            = "West US"
  address_space       = ["10.1.0.0/16"]
}

variable "subnets" {
  description = "Map of Azure VNET subnet configuration"
  type        = map(any)
  default = {
    app_subnet = {
      name                 = "app_subnet",
      resource_group_name  = "vCloud-lab.com",
      virtual_network_name = "example_vnet",
      address_prefixes     = ["10.1.1.0/24"]
    },
    db_subnet = {
      name                 = "db_subnet",
      resource_group_name  = "vCloud-lab.com",
      virtual_network_name = "example_vnet",
      address_prefixes     = ["10.1.2.0/24"]
    }
  }
}

module "subnet" {
  source               = "./modules/subnet"
  for_each             = var.subnets
  name                 = each.value.name
  virtual_network_name = module.virtual_network.vnet_name
  resource_group_name  = module.virtual_network.rg_name
  address_prefixes     = each.value.address_prefixes
  depends_on = [
    module.virtual_network
  ]
}

After running terraform init, modules and provider plugins are initiated and downloaded.

Terraform Azure hashcorp subnet virtual network terraform modules init initialization provider plugins azurerm devops scrops backend dependency lock.png

PS D:\Projects\Terraform\Demo\All_Resource_In_Module\virtual_network> terraform init

Initializing modules...
- subnet in modules\subnet
- virtual_network in modules\virtual_network

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/azurerm from the dependency lock file
- Using previously-installed hashicorp/azurerm v2.88.1

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

To verify all is good I am running terraform plan to verify no issues in the code/script and it will generate tfstate file, there are no errors.

PS D:\Projects\Terraform\Demo\All_Resource_In_Module\virtual_network> terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.subnet["app_subnet"].azurerm_subnet.subnet will be created
  + resource "azurerm_subnet" "subnet" {      + address_prefix                                 = (known after apply)
      + address_prefixes                               = [
          + "10.1.1.0/24",
        ]
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false
      + id                                             = (known after apply)
      + name                                           = "app_subnet"
      + resource_group_name                            = "vCloud-lab.com"
      + virtual_network_name                           = "test_vnet"
    }
  # module.subnet["db_subnet"].azurerm_subnet.subnet will be created  + resource "azurerm_subnet" "subnet" {
      + address_prefix                                 = (known after apply)
      + address_prefixes                               = [
          + "10.1.2.0/24",
        ]
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false      + id                                             = (known after apply)
      + name                                           = "db_subnet"
      + resource_group_name                            = "vCloud-lab.com"
      + virtual_network_name                           = "test_vnet"
    }

  # module.virtual_network.azurerm_virtual_network.vnet will be created
  + resource "azurerm_virtual_network" "vnet" {
      + address_space         = [
          + "10.1.0.0/16",
        ]
      + dns_servers           = (known after apply)
      + guid                  = (known after apply)
      + id                    = (known after apply)
      + location              = "westus"
      + name                  = "test_vnet"
      + resource_group_name   = "vCloud-lab.com"
      + subnet                = (known after apply)
      + vm_protection_enabled = false
    }

Plan: 3 to add, 0 to change, 0 to destroy.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
PS D:\Projects\Terraform\Demo\All_Resource_In_Module\virtual_network> 

Microsoft Azure terraform plan hashicorp resources address_prefix subnet resource_group_name virtual_network_name Module virtual network vnet cloud devops.png

This is the final step, I have applied the terraform configuration with terraform apply --auto-approve and there is no error, all the resources are deployed successfully.

PS D:\Projects\Terraform\Demo\All_Resource_In_Module\virtual_network> terraform apply --auto-approve  


Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.subnet["app_subnet"].azurerm_subnet.subnet will be created
  + resource "azurerm_subnet" "subnet" {
      + address_prefix                                 = (known after apply)
      + address_prefixes                               = [
          + "10.1.1.0/24",
        ]
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false
      + id                                             = (known after apply)
      + name                                           = "app_subnet"
      + resource_group_name                            = "vCloud-lab.com"
      + virtual_network_name                           = "test_vnet"
    }

  # module.subnet["db_subnet"].azurerm_subnet.subnet will be created
  + resource "azurerm_subnet" "subnet" {
      + address_prefix                                 = (known after apply)
      + address_prefixes                               = [
          + "10.1.2.0/24",
        ]
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false
      + id                                             = (known after apply)
      + name                                           = "db_subnet"
      + resource_group_name                            = "vCloud-lab.com"
      + virtual_network_name                           = "test_vnet"
    }

  # module.virtual_network.azurerm_virtual_network.vnet will be created
  + resource "azurerm_virtual_network" "vnet" {
      + address_space         = [
          + "10.1.0.0/16",
        ]
      + dns_servers           = (known after apply)
      + guid                  = (known after apply)
      + id                    = (known after apply)
      + location              = "westus"
      + name                  = "test_vnet"
      + resource_group_name   = "vCloud-lab.com"
      + subnet                = (known after apply)
      + vm_protection_enabled = false
    }

Plan: 3 to add, 0 to change, 0 to destroy.
module.virtual_network.azurerm_virtual_network.vnet: Creating...
module.virtual_network.azurerm_virtual_network.vnet: Still creating... [10s elapsed]
module.virtual_network.azurerm_virtual_network.vnet: Creation complete after 12s [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/vCloud-lab.com/providers/Microsoft.Network/virtualNetworks/test_vnet]
module.subnet["db_subnet"].azurerm_subnet.subnet: Creating...
module.subnet["app_subnet"].azurerm_subnet.subnet: Creating...
module.subnet["app_subnet"].azurerm_subnet.subnet: Creation complete after 10s [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/vCloud-lab.com/providers/Microsoft.Network/virtualNetworks/test_vnet/subnets/app_subnet]
module.subnet["db_subnet"].azurerm_subnet.subnet: Still creating... [10s elapsed]
module.subnet["db_subnet"].azurerm_subnet.subnet: Creation complete after 18s [id=/subscriptions/9e22xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/vCloud-lab.com/providers/Microsoft.Network/virtualNetworks/test_vnet/subnets/db_subnet]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
PS D:\Projects\Terraform\Demo\All_Resource_In_Module\virtual_network> 

Microsoft Azure powershell terraform hashicorp apply --auto-approve module virtual machine subnet dns_servers guid resource_group_name vm_protection_enabled resource data source address_space address_prefix.png

Useful Articles
Get Azure virtual machine backup reports using Powershell
Why is my Azure recovery services vault not getting deleted?
Create an Azure virtual machine scale set and load balancer using Terraform
Azure Terraform fixed Availibility Zones on Virtual Machine Scale Set
Writing and Using Terraform modules
Terraform Using one module variable in another module
Hashicorp Terraform dynamic block with example
Terraform for_each loop on map example
Terraform for_each loop on resource example

Go Back



Comment

Blog Search

Page Views

8146582

Follow me on Blogarama