Manage GCP Project IAM Members

The gcp:projects/iAMMember:IAMMember resource, part of the Pulumi GCP provider, grants a single identity access to a project-level IAM role without affecting other members of that role. This guide focuses on three capabilities: non-authoritative member assignment, time-limited access with IAM Conditions, and authoritative role binding with IAMBinding.

IAM resources reference existing GCP projects and identities. The examples are intentionally small. Combine them with your own project IDs and identity management.

Grant a single member access to a role

Most IAM configurations add one identity to a role without disrupting existing assignments.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = new gcp.projects.IAMMember("project", {
    project: "your-project-id",
    role: "roles/editor",
    member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp

project = gcp.projects.IAMMember("project",
    project="your-project-id",
    role="roles/editor",
    member="user:jane@example.com")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := projects.NewIAMMember(ctx, "project", &projects.IAMMemberArgs{
			Project: pulumi.String("your-project-id"),
			Role:    pulumi.String("roles/editor"),
			Member:  pulumi.String("user:jane@example.com"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = new Gcp.Projects.IAMMember("project", new()
    {
        Project = "your-project-id",
        Role = "roles/editor",
        Member = "user:jane@example.com",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.projects.IAMMember;
import com.pulumi.gcp.projects.IAMMemberArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var project = new IAMMember("project", IAMMemberArgs.builder()
            .project("your-project-id")
            .role("roles/editor")
            .member("user:jane@example.com")
            .build());

    }
}
resources:
  project:
    type: gcp:projects:IAMMember
    properties:
      project: your-project-id
      role: roles/editor
      member: user:jane@example.com

The member property specifies the identity using a prefix format: user: for Google accounts, serviceAccount: for service accounts, group: for Google groups, or domain: for G Suite domains. The role property references a predefined or custom role. IAMMember is non-authoritative, so it preserves other members already assigned to the role.

Add time-limited access with IAM Conditions

Teams often grant temporary access that expires automatically, such as contractor access or time-boxed privileges.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = new gcp.projects.IAMMember("project", {
    project: "your-project-id",
    role: "roles/firebase.admin",
    member: "user:jane@example.com",
    condition: {
        title: "expires_after_2019_12_31",
        description: "Expiring at midnight of 2019-12-31",
        expression: "request.time < timestamp(\"2020-01-01T00:00:00Z\")",
    },
});
import pulumi
import pulumi_gcp as gcp

project = gcp.projects.IAMMember("project",
    project="your-project-id",
    role="roles/firebase.admin",
    member="user:jane@example.com",
    condition={
        "title": "expires_after_2019_12_31",
        "description": "Expiring at midnight of 2019-12-31",
        "expression": "request.time < timestamp(\"2020-01-01T00:00:00Z\")",
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := projects.NewIAMMember(ctx, "project", &projects.IAMMemberArgs{
			Project: pulumi.String("your-project-id"),
			Role:    pulumi.String("roles/firebase.admin"),
			Member:  pulumi.String("user:jane@example.com"),
			Condition: &projects.IAMMemberConditionArgs{
				Title:       pulumi.String("expires_after_2019_12_31"),
				Description: pulumi.String("Expiring at midnight of 2019-12-31"),
				Expression:  pulumi.String("request.time < timestamp(\"2020-01-01T00:00:00Z\")"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = new Gcp.Projects.IAMMember("project", new()
    {
        Project = "your-project-id",
        Role = "roles/firebase.admin",
        Member = "user:jane@example.com",
        Condition = new Gcp.Projects.Inputs.IAMMemberConditionArgs
        {
            Title = "expires_after_2019_12_31",
            Description = "Expiring at midnight of 2019-12-31",
            Expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.projects.IAMMember;
import com.pulumi.gcp.projects.IAMMemberArgs;
import com.pulumi.gcp.projects.inputs.IAMMemberConditionArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var project = new IAMMember("project", IAMMemberArgs.builder()
            .project("your-project-id")
            .role("roles/firebase.admin")
            .member("user:jane@example.com")
            .condition(IAMMemberConditionArgs.builder()
                .title("expires_after_2019_12_31")
                .description("Expiring at midnight of 2019-12-31")
                .expression("request.time < timestamp(\"2020-01-01T00:00:00Z\")")
                .build())
            .build());

    }
}
resources:
  project:
    type: gcp:projects:IAMMember
    properties:
      project: your-project-id
      role: roles/firebase.admin
      member: user:jane@example.com
      condition:
        title: expires_after_2019_12_31
        description: Expiring at midnight of 2019-12-31
        expression: request.time < timestamp("2020-01-01T00:00:00Z")

The condition block adds time-based constraints to the role binding. The expression property uses Common Expression Language (CEL) to define when access is valid. Here, request.time < timestamp("2020-01-01T00:00:00Z") grants access until midnight on December 31, 2019. The title property uniquely identifies the condition.

Replace all members for a role with IAMBinding

When you need to define the complete set of members for a role, IAMBinding provides authoritative control.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = new gcp.projects.IAMBinding("project", {
    project: "your-project-id",
    role: "roles/editor",
    members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp

project = gcp.projects.IAMBinding("project",
    project="your-project-id",
    role="roles/editor",
    members=["user:jane@example.com"])
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := projects.NewIAMBinding(ctx, "project", &projects.IAMBindingArgs{
			Project: pulumi.String("your-project-id"),
			Role:    pulumi.String("roles/editor"),
			Members: pulumi.StringArray{
				pulumi.String("user:jane@example.com"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = new Gcp.Projects.IAMBinding("project", new()
    {
        Project = "your-project-id",
        Role = "roles/editor",
        Members = new[]
        {
            "user:jane@example.com",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.projects.IAMBinding;
import com.pulumi.gcp.projects.IAMBindingArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var project = new IAMBinding("project", IAMBindingArgs.builder()
            .project("your-project-id")
            .role("roles/editor")
            .members("user:jane@example.com")
            .build());

    }
}
resources:
  project:
    type: gcp:projects:IAMBinding
    properties:
      project: your-project-id
      role: roles/editor
      members:
        - user:jane@example.com

IAMBinding replaces any existing members for the specified role with the members array. This is authoritative for the role: any members not listed are removed. Use IAMBinding when you want to define the exact membership list, and IAMMember when you want to add members incrementally.

Beyond these examples

These snippets focus on specific IAM assignment features: single-member and multi-member role assignment, and time-based access with IAM Conditions. They’re intentionally minimal rather than full access control policies.

The examples reference pre-existing infrastructure such as GCP projects with project IDs, and user accounts, service accounts, or groups to grant access to. They focus on configuring role assignments rather than provisioning projects or identities.

To keep things focused, common IAM patterns are omitted, including:

  • IAMPolicy for full policy replacement (high risk of lockout)
  • IAMAuditConfig for audit logging configuration
  • Custom role definitions (assumes predefined roles)
  • Domain-wide delegation and group membership

These omissions are intentional: the goal is to illustrate how each IAM assignment feature is wired, not provide drop-in access control modules. See the IAMMember resource reference for all available configuration options.

Let's manage GCP Project IAM Members

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Resource Selection & Conflicts
What's the difference between IAMPolicy, IAMBinding, and IAMMember?
gcp.projects.IAMPolicy is authoritative and replaces the entire IAM policy. gcp.projects.IAMBinding is authoritative for a specific role, preserving other roles. gcp.projects.IAMMember is non-authoritative, adding a single member to a role while preserving other members.
Can I use IAMPolicy with IAMBinding or IAMMember?
No, gcp.projects.IAMPolicy cannot be used with gcp.projects.IAMBinding, gcp.projects.IAMMember, or gcp.projects.IAMAuditConfig. They will conflict over the policy state.
Can I use IAMBinding and IAMMember together?
Yes, but only if they don’t grant privileges to the same role. Using both for the same role causes conflicts.
Security & Access Control
Can I accidentally lock myself out of my project?
Yes, deleting gcp.projects.IAMPolicy removes access from anyone without organization-level access. It’s not recommended to use gcp.projects.IAMPolicy with your provider project. Import the policy before applying changes.
What member identity formats are supported?
Four formats are supported: user:{emailid} for Google accounts, serviceAccount:{emailid} for service accounts, group:{emailid} for Google groups, and domain:{domain} for G Suite domains.
Configuration & Constraints
Can I use IAM Conditions with Basic Roles like Owner?
No, IAM Conditions cannot be used with Basic Roles such as Owner. This violates API constraints and results in a 400 error.
How do I specify a custom role?
Custom roles must use the format [projects|organizations]/{parent-name}/roles/{role-name}, for example projects/my-project/roles/my-custom-role.
Is the project ID inferred from the provider configuration?
No, the project property is required and not inferred from the provider.
Immutability & Lifecycle
What properties can't I change after creating the resource?
The member, project, role, and condition properties are all immutable. Changing any of these requires recreating the resource.
How do I import a resource with a custom role?
Use the full custom role name in the format [projects/my-project|organizations/my-org]/roles/my-custom-role when importing.
How do I import a binding with an IAM Condition?
Include the condition title in the import command: terraform import google_project_iam_binding.my_project "{{your-project-id}} roles/{{role_id}} condition-title".

Using a different cloud?

Explore security guides for other cloud providers: