Manage GCP Compute Instance IAM Bindings

The gcp:compute/instanceIAMBinding:InstanceIAMBinding resource, part of the Pulumi GCP provider, manages IAM access to Compute Engine instances by controlling which members have specific roles. This guide focuses on three capabilities: authoritative role management with multiple members, incremental member addition, and time-based access with IAM Conditions.

InstanceIAMBinding is one of three related resources for instance access control. InstanceIAMPolicy replaces the entire policy, InstanceIAMBinding manages all members for a specific role, and InstanceIAMMember adds individual members. The examples are intentionally small. Combine them with your own instance infrastructure and access requirements.

Grant a role to multiple members at once

Teams managing instance access often need to 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 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 controls the complete member list for that role on the instance. The role property specifies which permission set to grant (e.g., “roles/compute.osLogin” for SSH access). The members array lists all identities that should have this role. When you update the members list, the binding replaces the previous member set entirely.

Add a single member to a role incrementally

When multiple teams manage access to the same instance, InstanceIAMMember allows adding individual members without coordinating the full member list.

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 one member to a role without affecting other members. This lets different Pulumi stacks or teams grant access independently. The member property takes a single identity (user, service account, group, or domain), while InstanceIAMBinding’s members property takes an array. You can use multiple InstanceIAMMember resources for the same role, but only one InstanceIAMBinding per role.

Apply time-based access with IAM Conditions

Temporary access grants expire automatically when you attach IAM Conditions to bindings.

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")

The condition block adds temporal or contextual constraints to role grants. The expression property uses Common Expression Language (CEL) to define when access is valid. Here, the expression compares request.time against a timestamp to enforce an expiration date. The title and description properties document the condition’s purpose. IAM Conditions work with both InstanceIAMBinding and InstanceIAMMember, but have known limitations documented by Google Cloud.

Beyond these examples

These snippets focus on specific IAM binding features: role-based and member-based access control, and IAM Conditions for time-based access. They’re intentionally minimal rather than full access management solutions.

The examples reference pre-existing infrastructure such as Compute Engine instances and Google Cloud project with configured zone. They focus on configuring IAM bindings rather than provisioning the instances themselves.

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

  • Full policy replacement (InstanceIAMPolicy resource)
  • Custom role definitions and formatting
  • Conflict resolution between Policy, Binding, and Member resources
  • Advanced IAM Condition expressions (location, resource attributes)

These omissions are intentional: the goal is to illustrate how each IAM binding feature 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
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, preserving other roles. InstanceIAMMember is non-authoritative, adding a single member to a role while preserving other members.
Can I use multiple IAM resources together on the same instance?
InstanceIAMPolicy cannot be used with InstanceIAMBinding or InstanceIAMMember, as they will conflict. However, InstanceIAMBinding and InstanceIAMMember can be used together only if they manage different roles.
Configuration & Identity Management
What member identity formats are supported?
Supported formats include allUsers, allAuthenticatedUsers, user:{email}, serviceAccount:{email}, group:{email}, domain:{domain}, projectOwner/Editor/Viewer:{projectid}, and federated identities (e.g., principal://iam.googleapis.com/...).
How do I specify custom roles?
Custom roles must use the format [projects|organizations]/{parent-name}/roles/{role-name}.
What properties can't I change after creating the binding?
The instanceName, role, zone, project, and condition properties are immutable and cannot be changed after creation.
What happens if I don't specify the project or zone?
If not specified, project and zone are parsed from the parent resource identifier. If unavailable there, the provider configuration is used.
Advanced Features
Can I use IAM Conditions with this resource?
Yes, IAM Conditions are supported (e.g., time-based expiration), but they have known limitations. Review the GCP documentation on IAM Conditions limitations before use.
How do I grant time-limited access using conditions?
Use the condition property with title, description, and expression fields. For example, set expression to request.time < timestamp("2020-01-01T00:00:00Z") for access expiring at a specific time.

Using a different cloud?

Explore security guides for other cloud providers: