When using variables marked as sensitive in a Terraform configuration, you may encounter an error if you attempt to use them directly as for_each arguments.
Problem
When you use a sensitive variable in a configuration to create multiple resources with a for_each argument, Terraform returns an error to prevent exposing sensitive data in resource instance keys.
The error message is similar to the following output:
╷ │ Error: Invalid for_each argument │ │ on main.tf line 11, in resource "random_pet" "p": │ 11: for_each = var.lengths │ ├──────────────── │ │ var.lengths has a sensitive value │ │ Sensitive values, │ or values derived from sensitive values, │ cannot be used as for_each arguments. If used, the sensitive value │ could be exposed as a resource instance key.
Prerequisites
- Terraform v0.14.0 or later.
Cause
This error occurs when a variable is marked as sensitive and is also used in a for_each expression.
A variable can be marked as sensitive in two ways:
-
The Terraform configuration declares the variable as
sensitive.## In this example, the values 4 and 7 are intended to be secret, ## while the keys 'foo' and 'bar' can be known. variable "lengths" { sensitive = true type = map(number) default = { foo = 4 bar = 7 } } - The variable is marked as sensitive in the HCP Terraform or Terraform Enterprise workspace variables page.
The configuration then attempts to use this sensitive variable directly in a for_each expression.
resource "random_pet" "p" {
for_each = var.lengths
length = each.value
}Solutions
Solution 1: Use the nonsensitive function
For Terraform v0.15 and later, you can use the nonsensitive function to explicitly mark an expression as not sensitive. Use this function with care, as it can expose information that was intended to be redacted. In this case, it is appropriate because the keys of the variable, which are used by for_each, are not sensitive.
Update your configuration to use the nonsensitive function on the keys of the variable.
variable "pets" {
sensitive = true
type = map(number)
default = {
foo = 4
bar = 7
}
}
resource "random_pet" "p" {
for_each = nonsensitive(toset(keys(var.pets)))
length = var.pets[each.key]
}After applying this change, a terraform plan will succeed, and the sensitive values will be redacted in the output.
# random_pet.p["bar"] will be created
+ resource "random_pet" "p" {
+ id = (known after apply)
+ length = (sensitive)
+ separator = "-"
}
# random_pet.p["foo"] will be created
+ resource "random_pet" "p" {
+ id = (known after apply)
+ length = (sensitive)
+ separator = "-"
}
Plan: 2 to add, 0 to change, 0 to destroy.Solution 2: Separate sensitive values from keys
Another option is to separate the non-sensitive keys from the sensitive values into two distinct variables. You can then iterate over the non-sensitive keys and use them to look up the corresponding sensitive values from the other variable.
First, define a variable for the non-sensitive keys and another for the map containing sensitive values.
variable "pets" {
type = set(string)
default = [
"foo",
"bar"
]
}
variable "pet_lengths" {
sensitive = true
type = map(number)
default = {
foo = 4
bar = 7
}
}Next, update the resource block to iterate over the non-sensitive var.pets set and look up the length from var.pet_lengths.
resource "random_pet" "p" {
for_each = var.pets
length = var.pet_lengths[each.key]
}Outcome Validation
After implementing either solution, run terraform plan again. The plan should now succeed without the Invalid for_each argument error, and the sensitive values will be correctly redacted in the plan output.