I have a module for service-accounts in GCP being used to populate kubernetes secrets
Here is my module
resource "google_service_account" "service_account" {
count = var.enabled ? 1 : 0
account_id = var.account_id
display_name = var.display_name
}
resource "google_project_iam_member" "service_account_roles" {
count = var.enabled ? length(var.roles) : 0
role = "roles/${element(var.roles, count.index)}"
member = "serviceAccount:${google_service_account.service_account[0].email}"
}
resource "google_service_account_key" "service_account_key" {
count = var.enabled ? 1 : 0
service_account_id = google_service_account.service_account[0].name
}
'output.tf' contains the following
output "private_decoded_key" {
value = base64decode(
element(
concat(
google_service_account_key.service_account_key.*.private_key,
[""],
),
0,
),
)
description = "The base 64 decoded version of the credentials"
}
Since there is a conditional that none of these resources are created without the enabled
flag, I had to handle it in TF 0.11.14 this way, and the tf0.12 autoupgrade tool didnt do much changes here.
How can I simplify this in Terraform 0.12.24, I tried modifying the output to simply
value = base64decode(google_service_account_key.service_account_key[0].private_key)
But the problem there is that if the corresponding kubernetes cluster gets deleted during a deletion, and there are errors midway because terraform, I will not be able to cleanup the terraform state of the rest of the resources using `terraform destroy'
Attempts to convert the count
to for_each
as shown below gave me the following errors
resource "google_service_account" "service_account" {
# count = var.enabled ? 1 : 0
for_each = var.enabled ? 1 : 0
account_id = var.account_id
display_name = var.display_name
}
resource "google_project_iam_member" "service_account_roles" {
# count = var.enabled ? length(var.roles) : 0
for_each = var.enabled ? toset(var.roles) : 0
# role = "roles/${element(var.roles, count.index)}"
role = "roles/${each.value}"
member = "serviceAccount:${google_service_account.service_account[0].email}"
}
for_each = var.enabled ? toset(var.roles) : 0
The true and false result expressions must have consistent types. The given
expressions are set of dynamic and number, respectively.
What am I doing wrong above ?
In the terraform version you mentioned (0.12.24) you should be able to use try()
in your outputs.tf
:
value = try(base64decode(google_service_account_key.service_account_key[0].private_key), "")
This would default to ""
if google_service_account_key.service_account_key[0].private_key
is not resolvable for any reason; you can also default to null
of course.
Edit/Update: To answer second (edited) part of the question:
To get rid of the error that both sides need to have the same type, you need to use []
as an empty set instead of 0
when converting to for_each
:
for_each = var.enabled ? toset(var.roles) : []
Please pay attention with existing infrastructure as you need to manipulate the state file when converting from count
to for_each
or terraform will try to destroy and create resources.
(I will cover this in more detail in part 3 of a series of stories I am currently working on about how to write terraform modules. You can find part 1 on medium and part 2 will be released next week.)