Terraform-provider-google: How to add bind a role to service account?

Created on 20 Mar 2018  ·  17Comments  ·  Source: hashicorp/terraform-provider-google

Hi, thank you for maintaining this project to allow GCP be used on terraform and potentially looking at this issue.

I'm trying to follow the guide to connect GKE applications to Cloud SQL, but instead of using the console gcloud to create the necessary service accounts and binding, using terraform with very limited success.

I was able to create a service account no problem with:

resource "google_service_account" "db-service-account" {
  account_id   = "db-service-account"
  display_name = "DB Service Account"
}

but trying to bind it with the roles/cloudsql.client role was not successful at all:


resource "google_service_account_iam_member" "db-iam" {
  service_account_id = "projects/my-project/serviceAccounts/[email protected]"
  role               = "roles/cloudsql.client"
  member             = "serviceAccount:[email protected]"
}

or


resource "google_service_account_iam_binding" "db-iam" {
  service_account_id = "projects/my-project/serviceAccounts/[email protected]"
  role               = "roles/cloudsql.client"
  members            = ["serviceAccount:[email protected]"]
}

Logs

google_service_account_iam_member.api-cards-db-iam: Creating...
  etag:               "" => "<computed>"
  member:             "" => "serviceAccount:[email protected]"
  role:               "" => "roles/cloudsql.client"
  service_account_id: "" => "projects/my-project/serviceAccounts/[email protected]"

Error: Error applying plan:

1 error(s) occurred:

* google_service_account_iam_member.api-cards-db-iam: 1 error(s) occurred:

* google_service_account_iam_member.api-cards-db-iam: Error applying IAM policy for service account 'projects/my-project/serviceAccounts/[email protected]': Error setting IAM policy for service account 'projects/my-project/serviceAccounts/[email protected]'.

verbose logging shows the API request and response:

2018/03/20 10:04:47 [DEBUG] Google API Request Details:
---[ REQUEST ]---------------------------------------
POST /v1/projects/my-project/serviceAccounts/[email protected]:setIamPolicy?alt=json HTTP/1.1
Host: iam.googleapis.com
User-Agent: google-api-go-client/0.5 (darwin amd64) Terraform/0.11.3-dev
Content-Length: 173
Content-Type: application/json
Accept-Encoding: gzip
-----------------------------------------------------

2018/03/20 10:04:48 [DEBUG] Google API Response Details:
---[ RESPONSE ]--------------------------------------
HTTP/2.0 400 Bad Request
Alt-Svc: hq=":443"; ma=2592000; quic=51303431; quic=51303339; quic=51303335,quic=":443"; ma=2592000; v="41,39,35"
Cache-Control: private
Content-Type: application/json; charset=UTF-8
Date: Tue, 20 Mar 2018 17:04:47 GMT
Server: ESF
Vary: Origin
Vary: X-Origin
Vary: Referer
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block

{
  "error": {
    "code": 400,
    "message": "Role roles/cloudsql.client is not supported for this resource.",
    "errors": [
      {
        "message": "Role roles/cloudsql.client is not supported for this resource.",
        "domain": "global",
        "reason": "badRequest"
      }
    ],
    "status": "INVALID_ARGUMENT"
  }
}

-----------------------------------------------------

Wrong/Misused Terraform Resource?

I have a feeling I'm using the wrong terraform resource to achieve the goal. But I'm not sure which other resource is more fitting.

I also don't understand what is the service_account_id field in those resources. I've tried using the id of the service account I'm trying to bind and also the terraform service account with no success. Similar guide using gloud nor the API requires it.

Resolution with gcloud

I was able to proceed with just the gcloud command for creating the binding, so I'm going with that for now.

gcloud projects add-iam-policy-binding  my-project \
    --member serviceAccount:[email protected] \
    --role  roles/cloudsql.client

Version Info

$ terraform -v
Terraform v0.11.3
+ provider.google v1.6.0
documentation question

Most helpful comment

Thanks @rickypai for the detailed report! The IAM resources are tricky to get right, and we're still thinking about how to make it clearer which to use in what situations.

In your case, you should be looking at the google_project_iam_binding resource- similar to how your gcloud command was gcloud projects add-iam-policy-binding. The google_service_account_iam_binding resource corresponds to this gcloud command.

All 17 comments

Thanks @rickypai for the detailed report! The IAM resources are tricky to get right, and we're still thinking about how to make it clearer which to use in what situations.

In your case, you should be looking at the google_project_iam_binding resource- similar to how your gcloud command was gcloud projects add-iam-policy-binding. The google_service_account_iam_binding resource corresponds to this gcloud command.

Hi @danawillow , I have the same issue, use gcloud projects add-iam-policy-binding is suceesful, but it doesn't work if I use google_service_account_iam_binding in TF. Is it still an open issue? Or is there any workarounds for this?
Thanks

Hey @jason-tian, the fix I mentioned in https://github.com/terraform-providers/terraform-provider-google/issues/1225#issuecomment-374706467 will apply to you as well. Since your gcloud command was gcloud projects, you'll want to use the equivalent google_projects_iam_binding resource.

Thanks @danawillow , the issue is fixed when I use projects iam binding.

I think we got the documentation for this updated, so I'm going to close this out. If people think we could still stand to have better documentation here, feel free to reply and I can reopen this.

forgot to say this but thanks for the documentation updates!

It worked for me using: gcloud projects add-iam-policy-binding, I could'nt add role roles/cloudsql.client using gcloud iam service-accounts add-iam-policy-binding.

Hey @danawillow! The fix you mentioned is indeed working however terraform destroy will be very destructive. It will remove every iam binding with the specified role and not just from the specified members only. I had to learn it the hard way. Thankfully audit logs are for the rescue and I was able to recover.

EDIT:

I realized I didn't understand the google_project_iam_binding resource properly. Initially it preserves other member settings but upon deletion it removes all of them. I should probably have used google_project_iam_member

@MrBlaise you're absolutely right!!!

I tested google_project_iam_binding and when I executed terraform destroy I lost all the members that were previously added to the role specified in the resource (the one that I added using Terraform and other that were added using other means) -

resource "google_service_account" "cloudsql-sa" {
    account_id   = "cloudsql-sa"
}

resource "google_project_iam_binding" "cloudsql-sa-cloudsql-admin-role" {
    role    = "roles/cloudsql.admin"
     members = [
         "serviceAccount:${google_service_account.cloudsql-sa.email}"
     ]
 }

It's actually dangerous to use google_project_iam_binding.

Initially it preserves other member settings

That sounds like a bug and unintended behaviour to me. If you have a repro, I'd appreciate a new issue with a bug report.

But as you mentioned, that's how iam_binding is meant to work. If you want Terraform to ignore members outside the config, use iam_member. :)

@tsadoklevi

(the one that I added using Terraform and other that were added using other means)

There aren't supposed to be any users added to the role using other means. That's the point of iam_binding. Would love any suggestions you have for how to make this clearer in the docs.

We purposefully designed our IAM resources into these three levels to allow people to decide the amount of control and danger they wanted when dealing with IAM. For some people, they want to make sure an IAM policy hasn't been modified by hand; for them, iam_binding and iam_policy are perfect. Some people want to just make sure certain members have a certain role, but don't care if other things change; for them, iam_member is perfect.

Initially it preserves other member settings

That sounds like a bug and unintended behaviour to me. If you have a repro, I'd appreciate a new issue with a bug report.

If I'm understanding what everybody is talking about correctly, then: nah, it's intended: https://github.com/terraform-providers/terraform-provider-google/blob/2.0.0/google/resource_iam_binding.go#L60

            // Creating a binding does not remove existing members if they are not in the provided members list.
            // This prevents removing existing permission without the user's knowledge.
            // Instead, a diff is shown in that case after creation. Subsequent calls to update will remove any
            // existing members not present in the provided list.

Interesting, I stand corrected! I'm sure there's a reason for that, but I can't recall what it is (beyond what's in the comment; I don't know why refresh wouldn't show that diff, I mean).

Yeah refresh should definitely show the diff.

This is related to #2379.

Hello! 👋 Is there consensus about whether or not this is a bug? (see also my comments in #2379, thank you!)

Hey @dossett, if you think there's a bug then please file a new issue and fill out the template. This issue was specifically about a misunderstanding of the difference between google_project_iam_* and google_service_account_iam_*

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 [email protected]. Thanks!

Was this page helpful?
0 / 5 - 0 ratings