I have a list of Service Accounts that will be created, and to assign roles to them I'm using a module that Google provides, and my code is as follows:
module "service-accounts" {
source = "terraform-google-modules/service-accounts/google"
version = "4.1.1"
project_id = var.project
names = var.sa_list
project_roles = [
"${var.project}=>roles/appengine.appAdmin",
"${var.project}=>roles/artifactregistry.reader",
"${var.project}=>roles/cloudbuild.builds.builder",
"${var.project}=>roles/cloudsql.client",
"${var.project}=>roles/cloudsql.instanceUser"
]
display_name = "Google App Engine SA - Managed by Terraform"
}
This works just fine. But I don't want to make the roles in project_roles explicit, so I've tried to use the for_each meta-argument"
variables.tf:
variable "rolesList" {
type =list(string)
default = ["roles/appengine.appAdmin","roles/artifactregistry.reader", "roles/cloudbuild.builds.builder", "roles/cloudsql.client", "roles/cloudsql.instanceUser"]
}
main.tf:
module "service-accounts" {
source = "terraform-google-modules/service-accounts/google"
version = "4.1.1"
project_id = var.project
names = var.sa_list
for_each = (toset[var.rolesList])
project_roles = ["${var.project}=>${each.key}"]
display_name = "Google App Engine SA - Managed by Terraform"
}
This way won't work because Terraform will create multiple accounts with the same id. How can I remove the hard code from project_roles? Is there a way to store the roles elsewhere and then call them? Or I'll need to assign roles individually?
If you want to give many roles to many services accounts, you can try by using the resources from official Google provider :
First, put the service accounts you want to create in a config file like a JSON file, it allows to be more flexible in case of modification. You will change the config file, not the Terraform code:
{
"servicesAccount": {
"first-service-account": {
"account_id": "first-service-account",
"display_name": "First SA managed by Terraform",
"roles": [
"roles/appengine.appAdmin",
"roles/artifactregistry.reader",
"roles/cloudbuild.builds.builder",
"roles/cloudsql.client",
"roles/cloudsql.instanceUser"
]
},
"second-service-account": {
"account_id": "second-service-account",
"display_name": "Second SA managed by Terraform",
"roles": [
"roles/appengine.appAdmin",
"roles/artifactregistry.reader",
"roles/cloudbuild.builds.builder",
"roles/cloudsql.client",
"roles/cloudsql.instanceUser"
]
}
}
}
Then you can parse this JSON file and putting in a local variable :
locals {
services_account = jsondecode(file("${path.module}/path/to/your/file.json"))["servicesAccount"]
sa_flattened = flatten([
for sa in local.services_account : [
for role in sa["roles"] : {
account_id = sa["account_id"]
display_name = sa["display_name"]
role = role
}
]
])
}
Finally you can create the SA and then give them the associated roles :
resource "google_service_account" "sa_names" {
project = var.project
for_each = local.services_account
account_id = each.value["account_id"]
display_name = each.value["display_name"]
}
resource "google_project_iam_member" "sa_roles" {
depends_on = [google_service_account.sa_names]
for_each = {for idx, sa in local.sa_flattened: "${sa["account_id"]}_${sa["role"]}" => sa}
project = var.project
role = each.value["role"]
member = "serviceAccount:${each.value["account_id"]}@${var.project}.iam.gserviceaccount.com"
}