Terraform & GCP : How to specify the same role for several users?

Viewed 29

I have been using this piece of code to specify a number of users that can impersonate a service account :

resource "google_service_account_iam_binding" "super-admin-impersonators" {
  provider = google.as_super_admin
  for_each = toset([
    for user in var.user_accs_impersonators_info.as_super_admin :
    "${user.acc_type}:${user.acc_details.email}"
  ])

  service_account_id = google_service_account.terraform-super-admin.name
  role               = "roles/iam.serviceAccountTokenCreator"
  members            = [each.key]
}

It was working well when I was using only one user in the associated input & *.tfvars

In the input.tf :

variable "user_accs_impersonators_info" {
  type = map(
    list(object({
      acc_type = string
      acc_details = object(
        {
          email = string
        }
  ) })))
}

in the inputs.tfvars

user_accs_impersonators_info = {
  as_super_admin = [
    {
      acc_type = "user",
      acc_details = {
        email = "old_corp_email@domain.com"
      } 
    },    
  ],
}

As I was logged on gcloud with the old_corp_email@domain.com, everything worked swimmingly.

Now I have a new corp email new_corp_email@domain.com so I tried to add it to the inputs.tfvars

user_accs_impersonators_info = {
  as_super_admin = [
    {
      acc_type = "user",
      acc_details = {
        email = "old_corp_email@domain.com"
      } 
    },    
    {
      acc_type = "user",
      acc_details = {
        email = "new_corp_email@domain.com"
      } 
    },
  ],

After I performed terraform apply, it said what I was expecting : Plan: 1 to add, 0 to change, 0 to destroy.. But when I went to the IAM/permissions for this impersonated service account, I found that the old_corp_email@domain.com was stripped of the roles/iam.serviceAccountTokenCreator, and that it could no longer impersonate the relevant service account.

So I thought, maybe it is because I am using google_service_account_iam_binding that is authoritative instead of google_service_account_iam_members that is non-authoritative, but the documentation clearly states :

google_service_account_iam_binding: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the service account are preserved.

So it should've preserved the role for the old_corp_email@domain.com.

I tried using google_service_account_iam_member instead, but it does not support the same arguments and I get this error :

│ Error: Unsupported argument
│
│   on impersonators_x_users.tf line 17, in resource "google_service_account_iam_member" "super-admin-impersonators":
│   17:   members            = [each.key]
│
│ An argument named "members" is not expected here. Did you mean "member"?
1 Answers

Here are both answers to the questions :

Authoritative vs non-authoritative distinction

So I thought, maybe it is because I am using google_service_account_iam_binding that is authoritative instead of google_service_account_iam_members that is non-authoritative, but the documentation clearly states :

google_service_account_iam_binding: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the service account are preserved.

Indeed, google_service_account_iam_binding is Authoritative, it preserves Other roles for the service account or user account. It does not preserve the same role for several accounts when changed. This post explains it very well :

if a role is bound to a set of IAM identities and you want to add more identities, authoritative one will require you to specify all the old identities again plus the new identies you wanna add otherwise any old identities you didn't specify will be unbinded from the role.

Perhaps, I was also using members = [each.key] erroneously.

How to specify the same role for several users :

I tried using google_service_account_iam_member instead, but it does not support the same arguments and I get this error : Error: Unsupported argument

I misunderstood the different mode of operation of member in google_service_account_iam_member and members in google_service_account_iam_binding

The answer is to use the following ressource block, by looping through the input variables but without putting it in a list.

resource "google_service_account_iam_member" "super-admin-impersonators" {
  provider = google.as_super_admin
  for_each = toset([
    for account in var.user_accs_impersonators_info.as_super_admin :
    "${account.acc_type}:${account.acc_details.email}"
  ])

  service_account_id = google_service_account.terraform-super-admin.name
  role               = "roles/iam.serviceAccountTokenCreator"
  member             = each.value
}
Related