Variables in a Terraform configuration can be marked as sensitive in both the configuration and the Terraform Cloud / Enterprise interface. Attempting to use sensitive variables as for_each
arguments will result in an error.
Problem
The developer wishes to use a sensitive variable in a configuration to create multiple resources using a
for_each
argument. Since the variable is marked sensitive, an error occurs like the following:
╷
│ 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 0.14.0 or later
Cause
- Either the Terraform configuration declares the variable as sensitive:
# In this example, the desire is that the values 4 and 7 be secret,
# while foo and bar can be known.
variable "lengths" {
sensitive = true
type = map(number)
default = {
foo = 4
bar = 7
}
}
- Or the variable is marked sensitive in the TFC / TFE workspace variables page.
-
And the configuration attempts to use the sensitive variable in a
for_each
expression:
resource "random_pet" "p" {
for_each = var.lengths
length = each.value
}
Solution 1
The
nonsensitive
function can be used to explicitly tell Terraform that a given expression should not be treated as sensitive. This function should be used with care, as it could lead to information that was intended to be sensitive and redacted from output to be leaked. However, in this case it is appropriate because the part of the variable of interest for use as for_each
keys is actually not sensitive.
Note: This function is only available in Terraform v0.15 and later.
A configuration using
nonsensitive
looks like the following:
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]
}
Solution 2
One option to work around this error is to split up the values that will be making up the keys from the values that will contain the sensitive values. Terraform can parse expressions using functions to determine if they contain sensitive values, so the
keys
function can serve to do the split.
The following configuration produces the error:
# In this example, the desire is that the values 4 and 7 be secret,
# while foo and bar can be known.
variable "lengths" {
sensitive = true
type = map(number)
default = {
foo = 4
bar = 7
}
}
resource "random_pet" "p" {
for_each = var.lengths
length = each.value
}
The error can be avoided by looping over the keys in a separate variable, and using
each.key
to refer to the values in the original map:variable "pets" {
type = set(string)
default = [
"foo",
"bar"
]
}
variable "pet_lengths" {
sensitive = true
type = map(number)
default = {
foo = 4
bar = 7
}
}
resource "random_pet" "p" {
for_each = var.pets
length = var.pet_lengths[each.key]
}
Outcome
After making the changes to split the keys into a separate map or set, rerun the plan to ensure the plan succeeds without error.
Solution 1 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 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.