Not able to pass inputs as array to Terraform

2 min read 30-08-2024
Not able to pass inputs as array to Terraform


Passing Arrays as Inputs in Terraform: A Terragrunt Example

This article explores a common challenge in Terraform: passing arrays as inputs from Terragrunt to Terraform. We'll analyze the error message and provide a solution, along with explanations and additional best practices.

The Problem

The provided code attempts to use a dynamic block in Terraform to manage multiple access policies for an Azure Key Vault. The user wants to provide a list of object IDs, secret permissions, and key permissions via Terragrunt. However, the code encounters an error:

Error: Invalid value for input variable
...
Unsuitable value for var.access_policies set using the
TF_VAR_access_policies environment variable: element 0: attribute
"object_id": string required. 

This error indicates that the access_policies variable, which is expected to be a set of objects, is receiving invalid data. It seems the object ID is not a string as required. Let's break down why.

The Solution

The issue stems from the way Terragrunt is handling the input array. Terragrunt expects an array of objects, while the access_policies variable within Terraform is defined as a set of objects. This mismatch needs to be resolved.

Here's the corrected Terragrunt input file:

terragrunt.hcl
inputs = {
  access_policies = [
    { object_id = ["xyz", "abc"], secret_permissions = ["Get", "Set"], key_permissions = ["Get"] }
    { object_id = toset(include.env.locals.env_vars.locals.object_id1), secret_permissions = ["Get"], key_permissions = ["Get"] }
  ]
}

Explanation:

  • Terragrunt expects an array of objects: Terragrunt treats the access_policies variable as an array of objects. Each object within the array represents a single access policy.
  • Terraform requires a set of objects: The access_policies variable in Terraform is defined as a set, which means it expects a collection of unique objects.
  • Bridging the gap: The key is to convert the array of objects from Terragrunt into a set of objects for Terraform. The toset function from Terraform can be used to achieve this conversion within the Terragrunt input file.

Modified Terraform Code:

resource "azurerm_key_vault_access_policy" "main" {
  key_vault_id = azurerm_key_vault.main.id

  dynamic "access_policy" {
    for_each = toset(flatten([
      for policy in var.access_policies : [
        for object_id in policy.object_id : {
          object_id = object_id
          secret_permissions = policy.secret_permissions
          key_permissions = policy.key_permissions
        }
      ]
    ]))

    content {
      tenant_id = data.azurerm_client_config.current.tenant_id
      object_id = access_policy.value.object_id
      secret_permissions = access_policy.value.secret_permissions
      key_permissions = access_policy.value.key_permissions
    }
  }
}

Further Improvements:

  • Data Validation: Use Terraform's validate function within the access_policies variable definition to ensure that the data received from Terragrunt adheres to the expected structure.
  • Documentation: Include clear comments and descriptions in both Terraform and Terragrunt to document the expected input structure and data types.

Conclusion:

By understanding the expected data structures in both Terraform and Terragrunt, and utilizing the toset function, we can effectively pass arrays as inputs. This approach ensures data integrity and consistency, leading to smooth deployments and reliable infrastructure management.