Manage GCP Machine Image IAM Permissions

The gcp:compute/machineImageIamMember:MachineImageIamMember resource, part of the Pulumi GCP provider, grants IAM roles to individual members on machine images without affecting other members or roles. This guide focuses on three capabilities: single-member role grants, time-limited access with IAM Conditions, and multi-member role bindings.

These resources reference existing machine images and grant access to users, service accounts, or groups. The examples are intentionally small. Combine them with your own machine image references and identity management.

Grant a role to a single member

When you need to add one user or service account to a role without affecting existing members, MachineImageIamMember provides non-authoritative access control.

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

const member = new gcp.compute.MachineImageIamMember("member", {
    project: image.project,
    machineImage: image.name,
    role: "roles/compute.admin",
    member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp

member = gcp.compute.MachineImageIamMember("member",
    project=image["project"],
    machine_image=image["name"],
    role="roles/compute.admin",
    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.NewMachineImageIamMember(ctx, "member", &compute.MachineImageIamMemberArgs{
			Project:      pulumi.Any(image.Project),
			MachineImage: pulumi.Any(image.Name),
			Role:         pulumi.String("roles/compute.admin"),
			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.MachineImageIamMember("member", new()
    {
        Project = image.Project,
        MachineImage = image.Name,
        Role = "roles/compute.admin",
        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.MachineImageIamMember;
import com.pulumi.gcp.compute.MachineImageIamMemberArgs;
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 MachineImageIamMember("member", MachineImageIamMemberArgs.builder()
            .project(image.project())
            .machineImage(image.name())
            .role("roles/compute.admin")
            .member("user:jane@example.com")
            .build());

    }
}
resources:
  member:
    type: gcp:compute:MachineImageIamMember
    properties:
      project: ${image.project}
      machineImage: ${image.name}
      role: roles/compute.admin
      member: user:jane@example.com

The member property specifies who receives access using identity formats like “user:email@example.com” or “serviceAccount:name@project.iam.gserviceaccount.com”. The role property defines the permission level. This resource adds the member to the role without removing other members who already have access.

Grant time-limited access with IAM Conditions

Organizations implementing temporary access can use IAM Conditions to expire permissions automatically at a specific date and time.

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

const member = new gcp.compute.MachineImageIamMember("member", {
    project: image.project,
    machineImage: image.name,
    role: "roles/compute.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

member = gcp.compute.MachineImageIamMember("member",
    project=image["project"],
    machine_image=image["name"],
    role="roles/compute.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/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := compute.NewMachineImageIamMember(ctx, "member", &compute.MachineImageIamMemberArgs{
			Project:      pulumi.Any(image.Project),
			MachineImage: pulumi.Any(image.Name),
			Role:         pulumi.String("roles/compute.admin"),
			Member:       pulumi.String("user:jane@example.com"),
			Condition: &compute.MachineImageIamMemberConditionArgs{
				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 member = new Gcp.Compute.MachineImageIamMember("member", new()
    {
        Project = image.Project,
        MachineImage = image.Name,
        Role = "roles/compute.admin",
        Member = "user:jane@example.com",
        Condition = new Gcp.Compute.Inputs.MachineImageIamMemberConditionArgs
        {
            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.MachineImageIamMember;
import com.pulumi.gcp.compute.MachineImageIamMemberArgs;
import com.pulumi.gcp.compute.inputs.MachineImageIamMemberConditionArgs;
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 MachineImageIamMember("member", MachineImageIamMemberArgs.builder()
            .project(image.project())
            .machineImage(image.name())
            .role("roles/compute.admin")
            .member("user:jane@example.com")
            .condition(MachineImageIamMemberConditionArgs.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:
  member:
    type: gcp:compute:MachineImageIamMember
    properties:
      project: ${image.project}
      machineImage: ${image.name}
      role: roles/compute.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 temporal constraints to the role grant. The expression property uses CEL (Common Expression Language) to define when access is valid. Here, the timestamp comparison automatically revokes access after 2019-12-31. The title and description properties document the condition’s purpose for auditing.

Bind a role to multiple members at once

When several users or service accounts need identical access, MachineImageIamBinding grants the role to all specified members in a single resource.

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

const binding = new gcp.compute.MachineImageIamBinding("binding", {
    project: image.project,
    machineImage: image.name,
    role: "roles/compute.admin",
    members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp

binding = gcp.compute.MachineImageIamBinding("binding",
    project=image["project"],
    machine_image=image["name"],
    role="roles/compute.admin",
    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.NewMachineImageIamBinding(ctx, "binding", &compute.MachineImageIamBindingArgs{
			Project:      pulumi.Any(image.Project),
			MachineImage: pulumi.Any(image.Name),
			Role:         pulumi.String("roles/compute.admin"),
			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.MachineImageIamBinding("binding", new()
    {
        Project = image.Project,
        MachineImage = image.Name,
        Role = "roles/compute.admin",
        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.MachineImageIamBinding;
import com.pulumi.gcp.compute.MachineImageIamBindingArgs;
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 MachineImageIamBinding("binding", MachineImageIamBindingArgs.builder()
            .project(image.project())
            .machineImage(image.name())
            .role("roles/compute.admin")
            .members("user:jane@example.com")
            .build());

    }
}
resources:
  binding:
    type: gcp:compute:MachineImageIamBinding
    properties:
      project: ${image.project}
      machineImage: ${image.name}
      role: roles/compute.admin
      members:
        - user:jane@example.com

The members property accepts a list of identities, granting the role to all of them simultaneously. MachineImageIamBinding is authoritative for the specified role: it replaces the member list for that role but preserves other roles on the machine image. This differs from MachineImageIamMember, which adds individual members without affecting others.

Beyond these examples

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

The examples reference pre-existing infrastructure such as machine images and GCP projects. They focus on granting access rather than creating the underlying resources.

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

  • Full policy replacement (MachineImageIamPolicy)
  • Custom role definitions and formatting
  • Federated identity and workload identity pool configuration
  • Resource conflict handling between Policy, Binding, and Member resources

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

Let's manage GCP Machine Image IAM Permissions

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
Which IAM resource should I use for managing Machine Image permissions?
Choose based on your needs: MachineImageIamPolicy for full policy control (replaces entire policy), MachineImageIamBinding for managing all members of a specific role (preserves other roles), or MachineImageIamMember for adding individual members without affecting others (non-authoritative).
Why am I getting conflicts when using MachineImageIamPolicy with other IAM resources?
MachineImageIamPolicy cannot be used with MachineImageIamBinding or MachineImageIamMember because they compete to control the policy. Choose either Policy (full control) or Binding/Member (granular control), never mix them.
Can I use MachineImageIamBinding and MachineImageIamMember together?
Yes, but only if they grant different roles. Using both for the same role causes conflicts.
Configuration & Identity Formats
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 like principal://iam.googleapis.com/....
What format do custom roles need?
Custom roles must use the format [projects|organizations]/{parent-name}/roles/{role-name}.
What properties can't be changed after creation?
All input properties are immutable: machineImage, member, role, condition, and project.
Advanced Features
How do I add time-based or conditional access?
Use the condition block with title, description, and expression fields. For example, to expire access at midnight 2019-12-31, use expression request.time < timestamp("2020-01-01T00:00:00Z").
Are there limitations with IAM Conditions?
Yes, IAM Conditions have known limitations documented at https://cloud.google.com/iam/docs/conditions-overview#limitations. Review these before implementing conditions.
Resource Lifecycle
Is this resource production-ready?
This resource is in beta and requires the terraform-provider-google-beta provider.
What happens if I don't specify a project?
The project will be parsed from the parent resource identifier, or default to the provider project if not specified anywhere.

Using a different cloud?

Explore security guides for other cloud providers: