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 with authoritative control over that specific role. This guide focuses on two capabilities: granting roles to multiple members and adding time-based conditional access.

IAMBinding is authoritative for a given role, replacing any existing members for that role while preserving other roles in the project. This differs from IAMMember (additive, non-authoritative) and IAMPolicy (replaces entire policy). The examples are intentionally small. Combine them with your own project structure and access requirements.

Grant a role to multiple members at once

Teams managing project access often grant the same role to multiple users, service accounts, or groups simultaneously.

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 all identities that receive the specified role. Each entry uses a prefix (user:, serviceAccount:, group:, or domain:) followed by the identifier. IAMBinding is authoritative: it replaces any existing members for this role, so include everyone who needs access.

Add time-based access with IAM Conditions

Temporary access grants require expiration logic to automatically revoke permissions after a deadline.

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 attaches 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 must be unique within the project and role combination.

Beyond these examples

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

The examples reference pre-existing infrastructure such as a GCP project with valid project ID. They focus on configuring role bindings rather than provisioning projects or defining custom roles.

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

  • Non-authoritative member grants (use IAMMember for additive access)
  • Full policy replacement (use IAMPolicy for complete policy control)
  • Audit logging configuration (use IAMAuditConfig)
  • Custom role definitions and formats

These omissions are intentional: the goal is to illustrate how IAMBinding wires role grants, 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

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.
What are the compatibility rules between IAM resources?
gcp.projects.IAMPolicy cannot be used with gcp.projects.IAMBinding, gcp.projects.IAMMember, or gcp.projects.IAMAuditConfig as they will conflict. However, gcp.projects.IAMBinding can be used with gcp.projects.IAMMember only if they grant privileges to different roles.
Can IAMPolicy lock me out of my project?
Yes. Deleting gcp.projects.IAMPolicy removes access from anyone without organization-level access. It’s not recommended for your provider project. Import the policy before applying changes, and only use with fully managed projects.
Configuration & Constraints
Why am I getting a 400 error when using IAM Conditions?
IAM Conditions cannot be used with Basic Roles like Owner. Use IAM Conditions only with predefined or custom roles to avoid API constraint violations.
What format do custom roles need?
Custom roles must follow the format [projects|organizations]/{parent-name}/roles/{role-name}. For example: projects/my-project/roles/my-custom-role.
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.
What's the difference between members and member properties?
gcp.projects.IAMBinding uses members (plural array) to grant a role to multiple identities, while gcp.projects.IAMMember uses member (singular) to grant a role to one identity.
Immutability & Updates
What properties can't I change after creation?
The project, role, and condition properties are immutable. Changing any of these requires recreating the resource.
Can I use multiple IAMBinding resources for the same role?
No. Only one gcp.projects.IAMBinding can be used per role. Multiple bindings for the same role will conflict.
Import & Migration
How do I import a binding with a custom role?
Use the full custom role name in the import command: [projects/my-project|organizations/my-org]/roles/my-custom-role.
How do I import a conditional IAM binding?
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: