terraform

Nested Loop IF resource contains key in Terraform


I would like to iterate over a list of values ONLY if the object contains a specific property. Is that possible?

For this use case I want to provision GitHub Repository Variables and Secrets if the local variable has the 'variables' property.

Locals.tf

locals {

  repos = {
    "terraform-tfe" : {
      description        = "Automation for Terraform Cloud"
      visibility         = "private"
    }
    "terraform-github" : {
      description        = "Automation for Github Repos"
      visibility         = "private"

    }
    "terraform-testapp" : {
      description        = "Test web app "
      visibility         = "private"

      variables = ["STORAGEACCOUNT","HOSTURL"]
      secrets   = ["CLIENTID", "CLIENTSECRET]
    }
  }
}

Main.tf

resource "github_repository" "repos" {

  for_each = local.repos

  name               = each.key
  description        = each.value.description
  visibility         = each.value.visibility

}

resource "github_actions_variable" "repository_variables" {

  for_each      = local.repos
  
  # IF each has 'variables' property
  
  repository = each.key
  # Iterate over variables in each.value.repositories

}

resource "github_actions_secrets" "repository_secrets" {

  for_each      = local.repos
  
  # IF each has 'secrets' property
  
  repository = each.key
  # Iterate over secrets in each.value.secrets

}

Solution

  • As mentioned in Filtering elements, a for expression can also include an optional if clause to filter elements from the source collection:

    locals {
      repos = { ... }
    
      secrets = {
        for repo_name, repo in local.repos :
        repo_name => repo.secrets
        if can(repo.secrets)
      }
    }
    

    In this case, the can function returns false if the secrets property doesn't exist.

    Full working example:

    locals {
      repos = {
        "terraform-tfe" : {
          description = "Automation for Terraform Cloud"
          visibility  = "private"
    
          variables = [
            "STORAGEACCOUNT_1"
          ]
        }
        "terraform-github" : {
          description = "Automation for Github Repos"
          visibility  = "private"
    
          secrets = [
            "CLIENTID_1",
            "CLIENTSECRET_1"
          ]
    
        }
        "terraform-testapp" : {
          description = "Test web app "
          visibility  = "private"
    
          variables = [
            "STORAGEACCOUNT_2",
            "HOSTURL_2"
          ]
    
          secrets = [
            "CLIENTID_2",
            "CLIENTSECRET_2"
          ]
        }
      }
    
      secrets = {
        for repo_name, repo in local.repos :
        repo_name => repo.secrets
        if can(repo.secrets)
      }
    
      variables = {
        for repo_name, repo in local.repos :
        repo_name => repo.variables
        if can(repo.variables)
      }
    }
    
    output "secrets" {
      value       = local.secrets
      description = "Secret variables for all repos."
    }
    
    output "variables" {
      value       = local.variables
      description = "Variables for all repos."
    }
    

    Running terraform plan:

    Changes to Outputs:
      + secrets   = {
          + terraform-github  = [
              + "CLIENTID_1",
              + "CLIENTSECRET_1",
            ]
          + terraform-testapp = [
              + "CLIENTID_2",
              + "CLIENTSECRET_2",
            ]
        }
      + variables = {
          + terraform-testapp = [
              + "STORAGEACCOUNT_2",
              + "HOSTURL_2",
            ]
          + terraform-tfe     = [
              + "STORAGEACCOUNT_1",
            ]
        }
    
    You can apply this plan to save these new output values to the Terraform
    state, without changing any real infrastructure.