Want to assign multiple Google cloud IAM roles to a service account via terraform
Asked Answered
W

3

19

I want to assign multiple IAM roles to a single service account through terraform. I prepared a TF file to do that, but it has an error. With a single role it can be successfully assigned but with multiple IAM roles, it gave an error.

data "google_iam_policy" "auth1" {
  binding {
    role = "roles/cloudsql.admin"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]    
    role = "roles/secretmanager.secretAccessor"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]      
    role = "roles/datastore.owner"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]  
    role = "roles/storage.admin"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]      
  }
}

How can I assign multiple roles against a single service account?

Wynellwynn answered 7/5, 2020 at 15:5 Comment(2)
Hey, your question is not quite clear.... What if you tell us what is the error message that you're getting?Godderd
Neither answer is the best answer. They are both good answers. The best answer depends on details that are not present in the question. Such as does the member already exist in the IAM policy? Are you adding or replacing IAM Roles? There are four Terraform resources that modify a project's IAM Policy. There is a reason for that and the correct selection requires careful consideration.Dredge
V
10

According with the documentation

Each document configuration must have one or more binding blocks, which each accept the following arguments: ....

You have to repeat the binding, like this

data "google_iam_policy" "auth1" {
  binding {
    role = "roles/cloudsql.admin"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]
  }
  binding {
    role = "roles/secretmanager.secretAccessor"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]
  }
  binding {
    role = "roles/datastore.owner"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]
  }
  binding {
    role = "roles/storage.admin"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]
  }
}

It's the same thing with you use the gcloud command, you can add only 1 role at the time on a list of email.

Venn answered 7/5, 2020 at 19:1 Comment(5)
Thanks for your answer. It's working now. But I am facing another error while assigning this service account to cloudrun service thru terraform. It says not supported for the resource -- googleapi: Error 400: Role roles/datastore.owner is not supported for this resource.on helloworld-cloudrun.tf line 60, in resource "google_cloud_run_service_iam_policy" "auth1": 60: resource "google_cloud_run_service_iam_policy" "auth1" { .Wynellwynn
My cloud run code -- resource "google_cloud_run_service_iam_policy" "auth1" { location = google_cloud_run_service.helloworld.location project = google_cloud_run_service.helloworld.project service = google_cloud_run_service.helloworld.name policy_data = data.google_iam_policy.auth1.policy_data }Wynellwynn
The IAM role are strange at the beginning. Think like this: I want to grant a role on member on a service/project/organisation. What you try to perform is I want grant the role datastore.owner on member .... on Cloud Run service. There is no relation between Cloud Run and Datastore. You want to grant the role datastore.owner on member .... on the project .... On cloud run, you can grant only a limited number of role, I want to grant the role run.invoker on member ... for cloud run service. Here that make sense.Venn
I don't know if I'm clear... So use this resource resource "google_project_iam_binding" ....Venn
Intotecho answer is better and should be promoted here.Aigneis
V
64

I did something like this

resource "google_project_iam_member" "member-role" {
  for_each = toset([
    "roles/cloudsql.admin",
    "roles/secretmanager.secretAccessor",
    "roles/datastore.owner",
    "roles/storage.admin",
  ])
  role = each.key
  member = "serviceAccount:${google_service_account.service_account_1.email}"
  project = my_project_id
}

Authoritative vs non-Authoritative

Pay attention to which of the resources you are using.

  • google_project_iam_policy - This is Authoritative - it will replace other policies in your Terraform code. Only use once per workspace directory.

  • google_project_iam_binding - This is Authoritative - it will override other bindings to the role elsewhere in your Terraform code. Only use once per workspace directory.

  • google_project_iam_member - This is non-Authoritative - This you can use many times in the same workspace directory - if using it multiple times better organizes your code.

Read here: https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam

Voltmer answered 18/3, 2022 at 1:29 Comment(4)
Yours is the answer that should be accepted.Elah
Yes, in fact, it can go all the way up if more people vote for this rather than the accepted answer. Thanks @VoltmerAigneis
This worked for me , in my case it is regular user account so I changed it to member="user:emailid"Mckean
You can lock yourself out of a resource with the Authoritive google_project_iam_policy and google_project_iam_binding as they ensure no other permissions are granted. google_project_iam_member only adds the roles to the resource.Voltmer
V
10

According with the documentation

Each document configuration must have one or more binding blocks, which each accept the following arguments: ....

You have to repeat the binding, like this

data "google_iam_policy" "auth1" {
  binding {
    role = "roles/cloudsql.admin"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]
  }
  binding {
    role = "roles/secretmanager.secretAccessor"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]
  }
  binding {
    role = "roles/datastore.owner"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]
  }
  binding {
    role = "roles/storage.admin"
    members = [
      "serviceAccount:${google_service_account.service_account_1.email}",
    ]
  }
}

It's the same thing with you use the gcloud command, you can add only 1 role at the time on a list of email.

Venn answered 7/5, 2020 at 19:1 Comment(5)
Thanks for your answer. It's working now. But I am facing another error while assigning this service account to cloudrun service thru terraform. It says not supported for the resource -- googleapi: Error 400: Role roles/datastore.owner is not supported for this resource.on helloworld-cloudrun.tf line 60, in resource "google_cloud_run_service_iam_policy" "auth1": 60: resource "google_cloud_run_service_iam_policy" "auth1" { .Wynellwynn
My cloud run code -- resource "google_cloud_run_service_iam_policy" "auth1" { location = google_cloud_run_service.helloworld.location project = google_cloud_run_service.helloworld.project service = google_cloud_run_service.helloworld.name policy_data = data.google_iam_policy.auth1.policy_data }Wynellwynn
The IAM role are strange at the beginning. Think like this: I want to grant a role on member on a service/project/organisation. What you try to perform is I want grant the role datastore.owner on member .... on Cloud Run service. There is no relation between Cloud Run and Datastore. You want to grant the role datastore.owner on member .... on the project .... On cloud run, you can grant only a limited number of role, I want to grant the role run.invoker on member ... for cloud run service. Here that make sense.Venn
I don't know if I'm clear... So use this resource resource "google_project_iam_binding" ....Venn
Intotecho answer is better and should be promoted here.Aigneis
M
1

I can't comment or upvote yet so here's another answer, but @intotecho is right.

I'd say do not create a policy with Terraform unless you really know what you're doing! In GCP, there's only one policy allowed per project. If you apply that policy, only the service accounts will have access, no humans. :) Even though we don't want humans to do human things, it's helpful to at least have view access to the GCP project you own.

Especccciallyy if you use the model that there are multiple Terraform workspaces performing iam operations on the project. If you use policies it will be similar to how wine is made, it will be a stomping party! The most recently applied policy will win (if the service account TF is using is included in that policy, otherwise it will lock itself out!)

It's possible humans get an inherited viewer role from a folder or the org itself, but assigning multiple roles using the google_project_iam_member is a much much better way and how 95% of the permissions are done with TF in GCP.

Multiplicity answered 17/5, 2022 at 4:49 Comment(1)
Sometimes you want your policy to stomp on any changes made by others. If so, use google_iam_policy, But normally you want to add roles, even if other humans come before or after to add more roles - in that case, use google_project_iam_member.Voltmer

© 2022 - 2025 — McMap. All rights reserved.