Nonsensitive function fails in Terraform
When I was trying to work with a module in Terraform, I came across an interesting issue. The module in question created an Azure AD service principal and optionally a secret for the service principal.
I wanted to print the service principal application id and secret to the screen so I could use it for testing. Naturally, the output for the service principal secret (client_secret
) had been marked as sensitive, so in order to print it to the terminal window I would need to use the nonsensitive
function.
output "client_secret" {
value = nonsensitive(module.sp.client_secret)
}
But it didn’t work! I got the following error message:
│ Error: Output refers to sensitive values
│
│ on main.tf line 33:
│ 33: output "client_secret" {
│
│ To reduce the risk of accidentally exporting sensitive data that was intended to be only internal, Terraform requires that any root module output containing
│ sensitive data be explicitly marked as sensitive, to confirm your intent.
│
│ If you do intend to export this data, annotate the output value as sensitive by adding the following argument:
│ sensitive = true
Super weird right? A quick peek into the module showed me what was happening. The creation of the service principal secret is optional. You might want to create a certificate instead. The code creating the secret looks like this, note the conditional expression for the count meta-argument:
resource "azuread_service_principal_password" "main" {
count = var.enable_service_principal_certificate == false ? 1 : 0
service_principal_id = azuread_service_principal.main.object_id
rotate_when_changed = {
rotation = time_rotating.main.id
}
}
The count
meta-argument means the resulting data structure will be a list of azuread_service_principal_password
instances, and the address of the secret value would be azuread_service_principal_password.main[0].value
.
Looking at the actual output for client_secret
that’s not what we see:
output "client_secret" {
description = "Password for service principal."
value = azuread_service_principal_password.main.*.value
sensitive = true
}
The expression azuread_service_principal_password.main.*.value
is going to return a list of the values in the value
attribute for all instances of the azuread_service_principal_password.main
resource.
The splat expression above is actually shorthand for this:
[ for k in azuread_service_principal_password.main : k.value ]
In our case, that will be a list with a single element, the client secret. For example, if the client secret is 1234567890
, the expression will evaluate to ["1234567890"]
. It’s a subtle but critical difference.
By setting the output’s sensitive argument to true
, the list is set as sensitive. The client secret value inside the list is already set as sensitive. When I use the nonsensitive
function to remove the sensitivity marker from the list, the sensitivity marker on the value inside the list remains. And thus I got the confusing error.
To prove that was the issue, I updated the value for my output to be the following:
output "client_secret" {
value = nonsensitive(module.sp.client_secret[0])
}
And sure enough the error went away! The nonsensitive
function was now being applied to the value inside the list and not the list as a whole.
A similar situation can arise when you’re dealing with maps. Check out this excellent post by Chad Quinlan that explains the necessary workaround.
If you’re in a similar situation, I hope this post helped you out!