azureterraformyamlterraform-provider-azure

Terraform not recognizing attributes read from yaml


I have an yaml file as given below

sites:
  - name: site1
    counter: "001"
    keyvaults:
      - name: "KV1"
        role_id: "0023"
        access_policies:
          SP1:
            object_id: "0000-0000-0000-00000"
            tenant_id: "xxxx-xxxx-xxxx-xxxx"
            certificate_permissions: ["Get"]
            key_permissions: []
            storage_permissions: []
            secret_permissions: ["Get"]

But terraform code for reading this is

locals {
  sites = yamldecode(file("${path.module}/sites.yaml"))["sites"]
  keyvaults = merge(flatten([
     for site in local.sites : {
      for keyvault in site.keyvaults : "${site.name}-${keyvault.name}" => {
        keyvault_name                 = keyvault.name
        keyvault_role_id              = keyvault.role_id
        counter                       = site.counter
        access_policies               = keyvault.access_policies
    }
   }
  ])...)
}

But , when i execute "terraform plan" it is failing saying:

Error: Unsupported attribute
│ 
│   on appservice_env.tf line 66, in locals:
│   66:           for policy_name, policy in tomap(keyvault.access_policies) : 
policy_name => {
│ 
│ This object does not have an attribute named "access_policies".

Solution

  • TL;DR your problem is probably caused by elements in your yaml file that don't have an access_policies property.

    Instead of:

    for policy_name, policy in keyvault.access_policies
    

    Try:

    for policy_name, policy in try(keyvault.access_policies, [])
    

    Example

    Consider the following file - sites.yaml:

    sites:
      - name: site1
        counter: "001"
        keyvaults:
          - name: "KV1"
            role_id: "0023"
            access_policies:
              SP1:
                object_id: "0000-0000-0000-00000"
                tenant_id: "xxxx-xxxx-xxxx-xxxx"
                certificate_permissions: ["Get"]
                key_permissions: []
                storage_permissions: []
                secret_permissions: ["Get"]
      - name: site2
        counter: "002"
        keyvaults:
          - name: "KV2"
            role_id: "0024"
    

    Reproducing the issue

    Adding an output variable to your original code:

    locals {
      sites = yamldecode(file("${path.module}/sites.yaml"))["sites"]
    
      keyvaults = merge(flatten([
        for site in local.sites : {
          for keyvault in site.keyvaults : "${site.name}-${keyvault.name}" => {
            keyvault_name    = keyvault.name
            keyvault_role_id = keyvault.role_id
            counter          = site.counter
            access_policies = {
              for policy_name, policy in keyvault.access_policies : policy_name => {
                tenant_id               = policy.tenant_id
                object_id               = policy.object_id
                key_permissions         = policy.key_permissions
                secret_permissions      = policy.secret_permissions
                certificate_permissions = policy.certificate_permissions
                storage_permissions     = policy.storage_permissions
              }
            }
          }
        }
      ])...)
    }
    
    output "keyvaults" {
      value = local.keyvaults
    }
    

    Output of terraform plan:

    Error: Unsupported attribute
    
      on main.tf line 11, in locals:
      11:           for policy_name, policy in keyvault.access_policies : policy_name => {
    
    This object does not have an attribute named "access_policies".
    

    Fixing the code

    locals {
      sites = yamldecode(file("${path.module}/sites.yaml"))["sites"]
    
      keyvaults = merge(flatten([
        for site in local.sites : {
          for keyvault in site.keyvaults : "${site.name}-${keyvault.name}" => {
            keyvault_name    = keyvault.name
            keyvault_role_id = keyvault.role_id
            counter          = site.counter
            access_policies = {
              for policy_name, policy in try(keyvault.access_policies, []) : policy_name => {
                tenant_id               = policy.tenant_id
                object_id               = policy.object_id
                key_permissions         = policy.key_permissions
                secret_permissions      = policy.secret_permissions
                certificate_permissions = policy.certificate_permissions
                storage_permissions     = policy.storage_permissions
              }
            }
          }
        }
      ])...)
    }
    
    output "keyvaults" {
      value = local.keyvaults
    }
    

    Output of terraform plan:

    Changes to Outputs:
      + keyvaults = {
          + site1-KV1 = {
              + access_policies  = {
                  + SP1 = {
                      + certificate_permissions = [
                          + "Get",
                        ]
                      + key_permissions         = []
                      + object_id               = "0000-0000-0000-00000"
                      + secret_permissions      = [
                          + "Get",
                        ]
                      + storage_permissions     = []
                      + tenant_id               = "xxxx-xxxx-xxxx-xxxx"
                    }
                }
              + counter          = "001"
              + keyvault_name    = "KV1"
              + keyvault_role_id = "0023"
            }
          + site2-KV2 = {
              + access_policies  = {}
              + counter          = "002"
              + keyvault_name    = "KV2"
              + keyvault_role_id = "0024"
            }
        }