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 aset
, 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 theaccess_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.