Manage GCP Compute Instance IAM Bindings

The gcp:compute/instanceIAMBinding:InstanceIAMBinding resource, part of the Pulumi GCP provider, manages IAM role bindings for Compute Engine instances, controlling which identities can access specific instances. This guide focuses on three capabilities: authoritative role binding with InstanceIAMBinding, non-authoritative member addition with InstanceIAMMember, and time-based access with IAM Conditions.

GCP provides three resources for instance IAM management with different authority levels. InstanceIAMPolicy replaces the entire policy; InstanceIAMBinding is authoritative for one role; InstanceIAMMember adds individual members non-authoritatively. InstanceIAMPolicy conflicts with the other two, but InstanceIAMBinding and InstanceIAMMember can coexist if they manage different roles. The examples are intentionally small. Combine them with your own instance references and access requirements.

Grant a role to multiple members with InstanceIAMBinding

When managing instance access, teams often need to grant the same role to multiple users or service accounts.

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

const binding = new gcp.compute.InstanceIAMBinding("binding", {
    project: _default.project,
    zone: _default.zone,
    instanceName: _default.name,
    role: "roles/compute.osLogin",
    members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp

binding = gcp.compute.InstanceIAMBinding("binding",
    project=default["project"],
    zone=default["zone"],
    instance_name=default["name"],
    role="roles/compute.osLogin",
    members=["user:jane@example.com"])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewInstanceIAMBinding(ctx, "binding", &compute.InstanceIAMBindingArgs{
			Project:      pulumi.Any(_default.Project),
			Zone:         pulumi.Any(_default.Zone),
			InstanceName: pulumi.Any(_default.Name),
			Role:         pulumi.String("roles/compute.osLogin"),
			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 binding = new Gcp.Compute.InstanceIAMBinding("binding", new()
    {
        Project = @default.Project,
        Zone = @default.Zone,
        InstanceName = @default.Name,
        Role = "roles/compute.osLogin",
        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.compute.InstanceIAMBinding;
import com.pulumi.gcp.compute.InstanceIAMBindingArgs;
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 binding = new InstanceIAMBinding("binding", InstanceIAMBindingArgs.builder()
            .project(default_.project())
            .zone(default_.zone())
            .instanceName(default_.name())
            .role("roles/compute.osLogin")
            .members("user:jane@example.com")
            .build());

    }
}
resources:
  binding:
    type: gcp:compute:InstanceIAMBinding
    properties:
      project: ${default.project}
      zone: ${default.zone}
      instanceName: ${default.name}
      role: roles/compute.osLogin
      members:
        - user:jane@example.com

InstanceIAMBinding is authoritative for the specified role: it replaces all existing members for that role with the members list you provide. The role property specifies a predefined or custom role (custom roles use the format [projects|organizations]/{parent-name}/roles/{role-name}). The members array accepts various identity formats: individual users, service accounts, groups, domains, or special identifiers like allUsers. Other roles on the instance remain unchanged.

Add time-based conditions to role bindings

Access grants sometimes need expiration dates or time windows for temporary access.

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

const binding = new gcp.compute.InstanceIAMBinding("binding", {
    project: _default.project,
    zone: _default.zone,
    instanceName: _default.name,
    role: "roles/compute.osLogin",
    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

binding = gcp.compute.InstanceIAMBinding("binding",
    project=default["project"],
    zone=default["zone"],
    instance_name=default["name"],
    role="roles/compute.osLogin",
    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/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewInstanceIAMBinding(ctx, "binding", &compute.InstanceIAMBindingArgs{
			Project:      pulumi.Any(_default.Project),
			Zone:         pulumi.Any(_default.Zone),
			InstanceName: pulumi.Any(_default.Name),
			Role:         pulumi.String("roles/compute.osLogin"),
			Members: pulumi.StringArray{
				pulumi.String("user:jane@example.com"),
			},
			Condition: &compute.InstanceIAMBindingConditionArgs{
				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 binding = new Gcp.Compute.InstanceIAMBinding("binding", new()
    {
        Project = @default.Project,
        Zone = @default.Zone,
        InstanceName = @default.Name,
        Role = "roles/compute.osLogin",
        Members = new[]
        {
            "user:jane@example.com",
        },
        Condition = new Gcp.Compute.Inputs.InstanceIAMBindingConditionArgs
        {
            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.compute.InstanceIAMBinding;
import com.pulumi.gcp.compute.InstanceIAMBindingArgs;
import com.pulumi.gcp.compute.inputs.InstanceIAMBindingConditionArgs;
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 binding = new InstanceIAMBinding("binding", InstanceIAMBindingArgs.builder()
            .project(default_.project())
            .zone(default_.zone())
            .instanceName(default_.name())
            .role("roles/compute.osLogin")
            .members("user:jane@example.com")
            .condition(InstanceIAMBindingConditionArgs.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:
  binding:
    type: gcp:compute:InstanceIAMBinding
    properties:
      project: ${default.project}
      zone: ${default.zone}
      instanceName: ${default.name}
      role: roles/compute.osLogin
      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")

IAM Conditions attach temporal constraints to role bindings using CEL (Common Expression Language) expressions. The condition block requires a title, optional description, and an expression that evaluates to true or false. Here, the expression checks if the current request time is before midnight on January 1, 2020. When the condition evaluates to false, the binding no longer grants access. Conditions have known limitations documented in the GCP IAM Conditions overview.

Add a single member to a role with InstanceIAMMember

When multiple teams manage access independently, InstanceIAMMember lets you add one member without affecting others.

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

const member = new gcp.compute.InstanceIAMMember("member", {
    project: _default.project,
    zone: _default.zone,
    instanceName: _default.name,
    role: "roles/compute.osLogin",
    member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp

member = gcp.compute.InstanceIAMMember("member",
    project=default["project"],
    zone=default["zone"],
    instance_name=default["name"],
    role="roles/compute.osLogin",
    member="user:jane@example.com")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewInstanceIAMMember(ctx, "member", &compute.InstanceIAMMemberArgs{
			Project:      pulumi.Any(_default.Project),
			Zone:         pulumi.Any(_default.Zone),
			InstanceName: pulumi.Any(_default.Name),
			Role:         pulumi.String("roles/compute.osLogin"),
			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 member = new Gcp.Compute.InstanceIAMMember("member", new()
    {
        Project = @default.Project,
        Zone = @default.Zone,
        InstanceName = @default.Name,
        Role = "roles/compute.osLogin",
        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.compute.InstanceIAMMember;
import com.pulumi.gcp.compute.InstanceIAMMemberArgs;
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 member = new InstanceIAMMember("member", InstanceIAMMemberArgs.builder()
            .project(default_.project())
            .zone(default_.zone())
            .instanceName(default_.name())
            .role("roles/compute.osLogin")
            .member("user:jane@example.com")
            .build());

    }
}
resources:
  member:
    type: gcp:compute:InstanceIAMMember
    properties:
      project: ${default.project}
      zone: ${default.zone}
      instanceName: ${default.name}
      role: roles/compute.osLogin
      member: user:jane@example.com

InstanceIAMMember is non-authoritative: it adds the specified member to the role without removing existing members. This allows different teams or Pulumi stacks to manage access independently. The member property uses the same identity formats as the members array in InstanceIAMBinding, but accepts only a single identity. If another resource removes this member, InstanceIAMMember will restore it on the next update.

Beyond these examples

These snippets focus on specific IAM binding features: role binding management (authoritative and non-authoritative) and IAM Conditions for temporal access control. They’re intentionally minimal rather than full access control configurations.

The examples reference pre-existing infrastructure such as Compute Engine instances and a GCP project with configured zone. They focus on binding configuration rather than instance provisioning.

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

  • InstanceIAMPolicy for full policy replacement
  • Custom role definitions and formatting
  • Conflict resolution between Policy, Binding, and Member resources
  • IAM Conditions limitations and edge cases

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

Let's manage GCP Compute Instance 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
Can I use InstanceIAMPolicy together with InstanceIAMBinding or InstanceIAMMember?
No, InstanceIAMPolicy cannot be used with InstanceIAMBinding or InstanceIAMMember because they’ll conflict over the policy. Use InstanceIAMPolicy alone, or use InstanceIAMBinding and InstanceIAMMember together.
Can I use InstanceIAMBinding and InstanceIAMMember together?
Yes, but only if they don’t grant privileges to the same role. If both resources manage the same role, they’ll conflict.
What's the difference between InstanceIAMPolicy, InstanceIAMBinding, and InstanceIAMMember?
InstanceIAMPolicy is authoritative and replaces the entire IAM policy. InstanceIAMBinding is authoritative for a specific role but preserves other roles. InstanceIAMMember is non-authoritative and preserves other members for the same role.
Configuration & Setup
How do I specify a custom IAM role?
Custom roles must use the format [projects|organizations]/{parent-name}/roles/{role-name}. For example, projects/my-project/roles/my-custom-role.
What member identity formats are supported?
You can use allUsers, allAuthenticatedUsers, user:{email}, serviceAccount:{email}, group:{email}, domain:{domain}, projectOwner/Editor/Viewer:{projectid}, or federated identities like principal://iam.googleapis.com/....
Can I use IAM Conditions with this resource?
Yes, set the condition property with title, description, and expression fields. However, IAM Conditions have known limitations that you should review before using them.
Immutability & Limitations
What properties can't I change after creating the binding?
The instanceName, role, zone, project, and condition properties are all immutable and cannot be changed after creation.
What happens if I don't specify the zone or project?
If not specified, zone and project are parsed from the parent resource identifier. If still unavailable, they’re taken from the provider configuration.

Using a different cloud?

Explore security guides for other cloud providers: