Unable to associate multiple iam user with the custom iam role in gcp


I am trying to write a terraform file to assign multiple gcp users to the custom role that i created. I am following “google_project_iam_binding” resource document.

I am using count to loop over the members. But looks like the iam role assigned to the last resource as per terraform run is retained while the iam role assigned to other members are removed during the terrafrom run.

My main.tf looks like this:

resource “google_project_iam_binding” “project” {
project = <project_id>
role = <gcp_role>
count = length(var.user_id)
members = [ var.user_id[count.index], ]

condition {
title = “expires_after_2022_12_31”
description = “Expiring at midnight of 2022-12-31”
expression = “request.time < timestamp("2023-01-01T00:00:00Z")”

lifecycle {
prevent_destroy = true
ignore_changes = [ members ]

and variable.tf variable looks like this:

variable “user_id” {
type = list(string)
default = [“user:<user1_id>@<domain_name>”, “user:<user2_id>@<domain_name>”, “user:<user3_id>@<domain_name>”, “user:<user4_id>@<domain_name>”]

How can i make this work ? How can i substitute value of the “user_id” variable holding all the user ids to the members attribute at once rather than going in loop ?

Is there a reason you’re looping through the user list? The way you have this written has a resource being created once for each user in the list, effectively adding the first user on the first loop, then overriding that member with each subsequent loop.

The “members” attribute takes in a list of strings, so you should be able to get rid of the count loop and pass in the full list of users:

resource "google_project_iam_binding" "project" {
  project = <project_id>
  role    = <gcp_role>
  members = var.user_id

  condition {
    title       = "expires_after_2022_12_31"
    description = "Expiring at midnight of 2022-12-31"
    expression  = "request.time < timestamp('2023-01-01T00:00:00Z')"

  lifecycle {
    prevent_destroy = true
    ignore_changes  = [members]

Is there a reason you need to iterate over the users?

Yeah I got the issue. But is there a way to update this list of users while associating with the iam role ? I mean right now I may have 4 users to associate to this role and at a future point in time I may add new members, the way this resource is it does not allow me add new members if I want to. It will override the previously entered members.

Do you suggest anyway to achieve that ?

There was no specific reason to iterate. I was trying to systematically apply this resource but seems like it needs all the members before hand and apply all of them at once.

If you have 4 users in the user_id variable list and run the terraform apply, all 4 users will be members of that role. If you needed to add a 5th user, you would just add the new user to the user_id variable list and re-run the apply. Terraform should see that as a resource change and add that 5th user to the role. I haven’t tested this, but that’s how the logic should work in Terraform.

I did that… it does not work. If i try to apply the list variable value one by one in a for_each loop or count loop… it overides the previous user_id and only puts the user_id that was last run.

There’s no need to loop over the users. I’m suggesting to feed in the full list of users without the for_each or count loop:

resource "google_project_iam_binding" "project" {
  project = <project_id>
  role    = <gcp_role>
  members = var.user_id

As mentioned, the members attribute accepts a full list, not a single user, so there is no need to loop over each user to execute the resource.

yeah thats the way… but can we expand a list variable in place ?

like i am trying to expand as below:

members = [ for userids in var.user_id : userids ]

This works but my user_id is a list like below:

[“userid1”, “userid2”, “userid3”]

I do not wish to provide a complete value from list variable like :

[ “user:userid1@domain_name.com” , “user:userid2@domain_name.com”, “user:userid3@domain_name.com” ]

How do i keep the “user:” and “@domain_name.com” as static in the above for loop ?

If your var.user_id is a list of users like this:

user_id = ["user1", "user2", "user3"]

You can append the domain to each of them in a local variable block using the formatlist function, and feed the local variable to members.

domain_name = "domain_name.com"
locals {
  user_list = formatlist("%s@${var.domain_name}", var.user_id)

This produces a local variable with the following output:

> local.user_list
1 Like

Thankyou soo much that worked. (Y)