1

I am trying to add service accounts to a certain role in a project in GCP. In order to do so, I get the IAM policy and then add the accounts desired, but am unable to get set_iam_policy to work correctly. I add the projects_to_add to the member list for the role I want to add service accounts to, but the GCP Docs are confusing and I'm not able to add the roles_dict I updated to set_iam_policy. Does anyone know how I should do this?

client = resourcemanager_v3.ProjectsClient(credentials=credentials)
    req = iam_policy_pb2.GetIamPolicyRequest(
        resource="projects/mse-sandbox-registry-anushka"
    )
    response = client.get_iam_policy(request=req)
    roles_dict = MessageToDict(response)

    projects_to_add = []
    projects_to_remove = []
    for proj in proj_numbers:
        account = (
            "serviceAccount:service-"
            + proj
            + "@serverless-robot-prod.iam.gserviceaccount.com"
        )
        if proj not in storage_obj_viewer_list:
            projects_to_add.append(account)

    for dict in roles_dict["bindings"]:
        if (
            dict["role"] == "roles/storage.objectViewer"
        ):  # add the projects into the dictionary
            members_list = dict["members"]
            members_list.append(projects_to_add)
            dict["members"] = members_list

    req = iam_policy_pb2.SetIamPolicyRequest(
        resource="mse-sandbox-registry-anushka", Policy=roles_dict
    )
    response = client.set_iam_policy(
        request=req,
    )```


I tried various forms, including passing in resource="projects/<my-project-name>", policy=roles_dict, but nothing seems to work.

1 Answers1

0

The process of adding service accounts to a specific role in a GCP project involves:

  1. Get the current IAM policy.
  2. Modify the policy to add the new service accounts to the desired role.
  3. Set the updated IAM policy.

Based on your code, it seems you've followed these steps, but there are some issues with the manipulation of the policy and setting it. Let's refine your code:

  1. Use the correct client for IAM operations. resourcemanager_v3.ProjectsClient is for Project resource operations. You need to use IAM clients.
  2. Make sure you're using the right method and service client for your use case. For projects, use projects().getIamPolicy and projects().setIamPolicy.
  3. Ensure you're modifying the IAM policy correctly.

Here's an adjusted code sample:

from google.cloud import resource_manager
from google.auth.credentials import Credentials
from google.iam.v1 import iam_policy_pb2

# Assuming your credentials object is correctly initialized
client = resource_manager.Client(credentials=credentials)

project_id = "YOUR_PROJECT_ID"

# Get the current IAM policy for the project
policy = client.get_project(project_id).get_iam_policy()

projects_to_add = [
    f"serviceAccount:service-{proj}@serverless-robot-prod.iam.gserviceaccount.com"
    for proj in proj_numbers
    if proj not in storage_obj_viewer_list
]

# Check if the role exists in the policy, if not add it
role_exists = False
for binding in policy["bindings"]:
    if binding["role"] == "roles/storage.objectViewer":
        # Add the new members to the existing role
        binding["members"].extend(projects_to_add)
        role_exists = True

if not role_exists:
    # If the role doesn't exist in the policy, add it with the new members
    policy["bindings"].append({
        "role": "roles/storage.objectViewer",
        "members": projects_to_add
    })

# Set the updated IAM policy
client.get_project(project_id).set_iam_policy(policy)

The error "object is not subscriptable" typically arises when trying to use indexing (e.g., using brackets like obj[key]) on an object that does not support it.

Given your error, it seems the current_policy object returned by the get_iam_policy call isn't directly subscriptable. Instead, you should be working with the dictionary version (roles_dict), then convert it back to a proto message before setting the policy.

Here's how you can convert the modified roles_dict back to a policy object and then set it:

  1. First, you need to import ParseDict from google.protobuf.json_format.

  2. Convert the dictionary (roles_dict) back to the policy object.

  3. Make the set_iam_policy call.

Here's a continuation of the code from before:

from google.protobuf.json_format import ParseDict

# ... [rest of your code]

# Convert the modified roles_dict back to a Policy object
modified_policy = iam_policy_pb2.Policy()
ParseDict(roles_dict, modified_policy)

# Set the modified policy
req = iam_policy_pb2.SetIamPolicyRequest(
    resource="projects/mse-sandbox-registry-anushka",
    policy=modified_policy
)
response = client.set_iam_policy(request=req)

This should work without the "not subscriptable" error since you're now manipulating the dictionary representation of the policy and converting it back to a proto message when required.

If you're looking to remove members from the members_list, you can use Python's built-in list methods.

To remove specific members from the list:

  1. Using remove: The remove() method removes the first occurrence of the value.

    members_list.remove(member_to_remove)
    

    This will raise a ValueError if the member doesn't exist, so it's typically wrapped in a try-except block:

    try:
        members_list.remove(member_to_remove)
    except ValueError:
        pass  # or handle as you see fit
    
  2. Using List Comprehension: This is useful if you want to remove multiple members or if you're not sure whether the member exists in the list.

    members_to_remove = ['member1', 'member2', ...]
    members_list = [member for member in members_list if member not in members_to_remove]
    

After you've modified the members_list by either adding or removing members, you can continue with your existing code to convert the roles_dict back to a proto message and set the IAM policy.

Piyush Patil
  • 14,512
  • 6
  • 35
  • 54
  • Thank you for your response and the help! Can this be done with resourcemanager_v3 as well? – Anushka Vijay Aug 22 '23 at 00:47
  • resourcemanager_v3.ProjectsClient is for Project resource operations. You need to use IAM clients – Piyush Patil Aug 22 '23 at 00:49
  • I thought google.iam was related to the iam permissions and get and set_iam_policy was related to resource manager. Is this not the case? – Anushka Vijay Aug 22 '23 at 00:51
  • The google.cloud.resource_manager client in the Google Cloud Python SDK provides methods to interact with Google Cloud Resource Manager, which is responsible for managing resources like projects, folders, and organizations. Within the context of Resource Manager, you indeed have methods like get_iam_policy and set_iam_policy to retrieve and modify IAM policies for projects. – Piyush Patil Aug 22 '23 at 00:55
  • I added new block for above error – Piyush Patil Aug 22 '23 at 16:13
  • 1
    Thank you! It works now, it turns out also we can use . instead of [] and then the subscriptable error goes away as well. – Anushka Vijay Aug 22 '23 at 16:21
  • If extend is the way to append in this case, what would be the function to remove members? – Anushka Vijay Aug 22 '23 at 17:08
  • Added 3 rd section to the answer – Piyush Patil Aug 22 '23 at 17:13
  • for the code to add a binding with role and member if it doesn't exist, that gives me the error Expected a message object, but got {'role': 'roles/storage.objectViewer', 'members': ['serviceAccount:service-823816477089@serverless-robot-prod.iam.gserviceaccount.com']}. – Anushka Vijay Aug 30 '23 at 21:43