Search code examples
terraformterragrunt

Create multiple objects from one Terraform module using Terragrunt


I am using terraform via terragrunt. I have a folder with a single terragrunt.hcl file in it. The purpose of this file is to create multiple subnetworks in GCP.

To create a subnetwork, I have a module that takes several inputs.

I want to be able to create several subnetworks in my terragrunt.hcl file.

I think the best way would be to create a list with dictionaries (or maps as terraform call them) and then iterate over them.

I have some code that is not working

Here is some non-working code.

#terragrunt.hcl

include {
  path = find_in_parent_folders()
}

  inputs = {
    # Common tags to be assigned to all resources
    subnetworks = [
      {
        "subnetName": "subnet1-euw"
        "subNetwork": "10.2.0.0/16"
        "region": "europe-west1"
      },
      {
        "subnetName": "subnet1-usc1"
        "subNetwork": "10.3.0.0/16"
        "region": "us-central1"
      }
    ]
  }

terraform {
  module "subnetworks" {
    source = "github.com/MyProject/infrastructure-modules.git//vpc/subnetwork"
    vpc_name = "MyVPC"
    vpc_subnetwork_name = [for network in subnetworks: network.subnetName]
    vpc_subnetwork_cidr = [for network in subnetworks: network.subNetwork]
    vpc_subnetwork_region = [for network in subnetworks: network.region]
  }
}

Seems I cannot use "module" inside the "terraform" block. Hopefully the code at least show what I want to achieve.

For reference, the module I am calling looks like this

#main.tf

terraform {
  # Intentionally empty. Will be filled by Terragrunt.
  backend "gcs" {}
}

resource "google_compute_subnetwork" "vpc_subnetwork" {
  name          = var.vpc_subnetwork_name
  ip_cidr_range = var.vpc_subnetwork_cidr
  region        = var.vpc_subnetwork_region
  network       = var.vpc_name
}
#variables.tf
variable "vpc_name" {
  description = "Name of VPC"
  type        = string
}

variable "vpc_subnetwork_name" {
  description = "Name of subnetwork"
  type        = string
}

variable "vpc_subnetwork_cidr" {
  description = "Subnetwork CIDR"
  type        = string
}

variable "vpc_subnetwork_region" {
  description = "Subnetwork region"
  type        = string
}

Solution

  • Terragrunt does not have a loop construct. In Terragrunt, you'd use a directory hierarchy to do what you want here. For example, to achieve your goals above, something like this:

    └── live
        ├── empty.yaml
        ├── euw
        │   ├── region.yaml
        │   └── vpc
        │       └── terragrunt.hcl
        ├── terragrunt.hcl
        └── usc1
            ├── region.yaml
            └── vpc
                └── terragrunt.hcl
    

    Within live/terragrunt.hcl, you make the other yaml files available within the terragrunt configuration:

    # live/terragrunt.hcl
    inputs = merge(
      # Configure Terragrunt to use common vars encoded as yaml to help you keep often-repeated variables (e.g., account ID)
      # DRY. We use yamldecode to merge the maps into the inputs, as opposed to using varfiles due to a restriction in
      # Terraform >=0.12 that all vars must be defined as variable blocks in modules. Terragrunt inputs are not affected by
      # this restriction.
      yamldecode(
        file("${get_terragrunt_dir()}/${find_in_parent_folders("region.yaml", "${path_relative_from_include()}/empty.yaml")}"),
      )
    )
    

    In the region.yaml within each region, you simply state the region:

    # live/euw/region.yaml
    # These variables apply to this entire region. They are automatically pulled in using the extra_arguments
    # setting in the root terraform.tfvars file's Terragrunt configuration.
    region: "europe-west1"
    
    # live/usc1/region.yaml
    region: "us-central1"
    

    Now you can refer to the region in your per-region terragrunt.hcl files as a variable:

    # live/euw/vpc/terragrunt.hcl
    terraform {
      source = "github.com/MyProject/infrastructure-modules.git//vpc/subnetwork"
    }
    
    include {
      path = find_in_parent_folders()
    }
    
    inputs = {
      vpc_subnetwork_name = "subnet1-${region}"
      vpc_subnetwork_cidr = "10.2.0.0/16"
      vpc_subnetwork_region = region
      vpc_name = "MyVPC"
    }
    

    Also:

    # live/usc1/vpc/terragrunt.hcl
    terraform {
      source = "github.com/MyProject/infrastructure-modules.git//vpc/subnetwork"
    }
    
    include {
      path = find_in_parent_folders()
    }
    
    inputs = {
      vpc_subnetwork_name = "subnet1-${region}"
      vpc_subnetwork_cidr = "10.3.0.0/16"
      vpc_subnetwork_region = region
      vpc_name = "MyVPC"
    }
    

    You might find the example terragrunt repository from Gruntwork helpful.