Configure GCP Project IAM Bindings

The gcp:projects/iAMBinding:IAMBinding resource, part of the Pulumi GCP provider, grants a GCP IAM role to a list of members at the project level, replacing any existing members for that role. This guide focuses on two capabilities: authoritative role assignment to multiple members and time-based conditional access.

IAMBinding is one of four IAM resources for project access control. It’s authoritative for a single role, meaning it replaces the member list for that role while preserving other roles. Use IAMMember for incremental additions or IAMPolicy for complete policy control. Bindings reference existing projects and grant access to identities that must exist. The examples are intentionally small. Combine them with your own project IDs and identity management.

Grant a role to multiple members

When managing project access, you often need to grant the same role to multiple users, service accounts, or groups at once.

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

The members array lists identities that receive the specified role. Each entry uses a prefix (user:, serviceAccount:, group:, or domain:) followed by the identifier. IAMBinding is authoritative for this role, so any members not in this list lose access when you apply the configuration.

Add time-based access with IAM Conditions

Temporary access grants expire automatically when you attach IAM Conditions, eliminating manual cleanup for contractors or time-limited permissions.

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/container.admin",
    members: ["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.IAMBinding("project",
    project="your-project-id",
    role="roles/container.admin",
    members=["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.NewIAMBinding(ctx, "project", &projects.IAMBindingArgs{
			Project: pulumi.String("your-project-id"),
			Role:    pulumi.String("roles/container.admin"),
			Members: pulumi.StringArray{
				pulumi.String("user:jane@example.com"),
			},
			Condition: &projects.IAMBindingConditionArgs{
				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.IAMBinding("project", new()
    {
        Project = "your-project-id",
        Role = "roles/container.admin",
        Members = new[]
        {
            "user:jane@example.com",
        },
        Condition = new Gcp.Projects.Inputs.IAMBindingConditionArgs
        {
            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.IAMBinding;
import com.pulumi.gcp.projects.IAMBindingArgs;
import com.pulumi.gcp.projects.inputs.IAMBindingConditionArgs;
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/container.admin")
            .members("user:jane@example.com")
            .condition(IAMBindingConditionArgs.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:IAMBinding
    properties:
      project: your-project-id
      role: roles/container.admin
      members:
        - 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 a CEL expression that controls when the binding applies. The expression property uses Common Expression Language to define access rules; here, it checks whether the current time is before a timestamp. The title and description properties document the condition’s purpose. When the condition evaluates to false, members lose access automatically.

Beyond these examples

These snippets focus on specific IAMBinding features: role-based access control and time-based conditional access. They’re intentionally minimal rather than full access management solutions.

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

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

  • Single-member grants (use IAMMember for incremental additions)
  • Full policy replacement (use IAMPolicy for complete control)
  • Audit logging configuration (use IAMAuditConfig)
  • Resource-level conditions beyond time-based expiration

These omissions are intentional: the goal is to illustrate how IAMBinding wires role assignments, not provide drop-in access control modules. See the IAMBinding resource reference for all available configuration options.

Let's configure GCP Project IAM Bindings

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Safety & Lockout Prevention
Can I accidentally lock myself out using IAMPolicy?
Yes. Deleting an gcp.projects.IAMPolicy removes access from anyone without organization-level access to the project. It’s recommended to import the policy before applying changes and avoid using IAMPolicy with your provider project. Only use it with projects fully managed by Pulumi.
What should I do before applying an IAMPolicy?
Import the existing policy first to avoid accidentally removing access. The schema strongly recommends this when using gcp.projects.IAMPolicy.
Resource Compatibility
What's the difference between IAMPolicy, IAMBinding, IAMMember, and IAMAuditConfig?
gcp.projects.IAMPolicy is authoritative and replaces the entire IAM policy. gcp.projects.IAMBinding is authoritative for a given role, preserving other roles. gcp.projects.IAMMember is non-authoritative, adding one member to a role while preserving other members. gcp.projects.IAMAuditConfig is authoritative for audit logging on a given service.
Can I use IAMPolicy with other IAM resources?
No. gcp.projects.IAMPolicy cannot be used with gcp.projects.IAMBinding, gcp.projects.IAMMember, or gcp.projects.IAMAuditConfig because they will conflict over the policy.
Can I use IAMBinding and IAMMember together?
Yes, but only if they don’t grant privilege to the same role. Each role must be managed by either IAMBinding or IAMMember, not both.
Configuration & Member Formats
What member identity formats are supported?

Four formats are supported:

  1. user:{emailid} - Specific Google account (e.g., alice@gmail.com)
  2. serviceAccount:{emailid} - Service account (e.g., my-app@appspot.gserviceaccount.com)
  3. group:{emailid} - Google group (e.g., admins@example.com)
  4. domain:{domain} - G Suite domain for all users (e.g., example.com)
What's the format for custom roles?
Custom roles must use the format [projects|organizations]/{parent-name}/roles/{role-name}.
How do I add IAM Conditions to a binding?
Add a condition block with title, description, and expression fields. For example, to create a time-based condition, use an expression like request.time < timestamp("2020-01-01T00:00:00Z").
Limitations & Constraints
Can I use IAM Conditions with Basic Roles like Owner?
No. IAM Conditions cannot be used with Basic Roles such as Owner. Violating this constraint will cause the API to return a 400 error.
Why is the project property required if I have a provider configured?
The project property is not inferred from the provider and must be explicitly specified for the target project.
Can I change the role or project after creating an IAMBinding?
No. Both role and project are immutable properties and cannot be changed after resource creation.

Using a different cloud?

Explore security guides for other cloud providers: