Manage GCP Backend Service IAM Permissions

The gcp:compute/backendServiceIamMember:BackendServiceIamMember resource, part of the Pulumi GCP provider, grants IAM roles to identities on Compute Engine backend services, controlling who can view, modify, or manage load balancer backends. This guide focuses on three capabilities: single-member role grants, time-limited access with IAM Conditions, and role-level member list management.

IAM resources reference existing backend services and projects. The examples are intentionally small. Combine them with your own backend service infrastructure and organizational access policies.

Grant a role to a single member

Most IAM configurations start by granting a specific role to one identity without affecting other members who already have that role.

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

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

member = gcp.compute.BackendServiceIamMember("member",
    project=default["project"],
    name=default["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.NewBackendServiceIamMember(ctx, "member", &compute.BackendServiceIamMemberArgs{
			Project: pulumi.Any(_default.Project),
			Name:    pulumi.Any(_default.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.BackendServiceIamMember("member", new()
    {
        Project = @default.Project,
        Name = @default.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.BackendServiceIamMember;
import com.pulumi.gcp.compute.BackendServiceIamMemberArgs;
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 BackendServiceIamMember("member", BackendServiceIamMemberArgs.builder()
            .project(default_.project())
            .name(default_.name())
            .role("roles/compute.admin")
            .member("user:jane@example.com")
            .build());

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

The member property specifies the identity receiving access, using formats like “user:email@example.com” or “serviceAccount:name@project.iam.gserviceaccount.com”. The role property defines the permission level. This resource is non-authoritative: it adds one member to the role without removing others.

Grant time-limited access with IAM Conditions

Teams often need temporary access that expires automatically, such as contractor access or time-boxed testing.

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

const member = new gcp.compute.BackendServiceIamMember("member", {
    project: _default.project,
    name: _default.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.BackendServiceIamMember("member",
    project=default["project"],
    name=default["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.NewBackendServiceIamMember(ctx, "member", &compute.BackendServiceIamMemberArgs{
			Project: pulumi.Any(_default.Project),
			Name:    pulumi.Any(_default.Name),
			Role:    pulumi.String("roles/compute.admin"),
			Member:  pulumi.String("user:jane@example.com"),
			Condition: &compute.BackendServiceIamMemberConditionArgs{
				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.BackendServiceIamMember("member", new()
    {
        Project = @default.Project,
        Name = @default.Name,
        Role = "roles/compute.admin",
        Member = "user:jane@example.com",
        Condition = new Gcp.Compute.Inputs.BackendServiceIamMemberConditionArgs
        {
            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.BackendServiceIamMember;
import com.pulumi.gcp.compute.BackendServiceIamMemberArgs;
import com.pulumi.gcp.compute.inputs.BackendServiceIamMemberConditionArgs;
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 BackendServiceIamMember("member", BackendServiceIamMemberArgs.builder()
            .project(default_.project())
            .name(default_.name())
            .role("roles/compute.admin")
            .member("user:jane@example.com")
            .condition(BackendServiceIamMemberConditionArgs.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:BackendServiceIamMember
    properties:
      project: ${default.project}
      name: ${default.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 time-based 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 December 31, 2019. The title and description properties document the condition’s purpose.

Grant a role to multiple members at once

When multiple identities need the same role, BackendServiceIamBinding manages the complete member list authoritatively.

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

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

binding = gcp.compute.BackendServiceIamBinding("binding",
    project=default["project"],
    name=default["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.NewBackendServiceIamBinding(ctx, "binding", &compute.BackendServiceIamBindingArgs{
			Project: pulumi.Any(_default.Project),
			Name:    pulumi.Any(_default.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.BackendServiceIamBinding("binding", new()
    {
        Project = @default.Project,
        Name = @default.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.BackendServiceIamBinding;
import com.pulumi.gcp.compute.BackendServiceIamBindingArgs;
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 BackendServiceIamBinding("binding", BackendServiceIamBindingArgs.builder()
            .project(default_.project())
            .name(default_.name())
            .role("roles/compute.admin")
            .members("user:jane@example.com")
            .build());

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

The members property lists all identities that should have this role. This resource is authoritative for the specified role: it replaces any existing members with the provided list. Use BackendServiceIamMember for additive grants; use BackendServiceIamBinding when you need to control the complete member list for a role.

Beyond these examples

These snippets focus on specific IAM grant features: single-member grants (BackendServiceIamMember), role-level member lists (BackendServiceIamBinding), and time-based access with IAM Conditions. They’re intentionally minimal rather than full access control policies.

The examples reference pre-existing infrastructure such as backend services (by name) and GCP projects. They focus on granting access rather than provisioning the underlying compute resources.

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

  • Full policy replacement (BackendServiceIamPolicy)
  • Complex condition expressions (multiple clauses, resource attributes)
  • Federated identity and workload identity pool principals
  • Custom role definitions and formatting

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

Let's manage GCP Backend Service 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
What's the difference between BackendServiceIamPolicy, BackendServiceIamBinding, and BackendServiceIamMember?
BackendServiceIamPolicy is authoritative and replaces the entire IAM policy. BackendServiceIamBinding is authoritative for a single role, managing all members for that role while preserving other roles. BackendServiceIamMember is non-authoritative, adding a single member to a role while preserving other members.
Can I use BackendServiceIamPolicy with BackendServiceIamBinding or BackendServiceIamMember?
No, BackendServiceIamPolicy cannot be used with BackendServiceIamBinding or BackendServiceIamMember as they will conflict over the policy state.
Can I use BackendServiceIamBinding and BackendServiceIamMember together?
Yes, but only if they manage different roles. Using both for the same role will cause conflicts.
IAM Configuration
What formats can I use for the member identity?
You can use allUsers, allAuthenticatedUsers, user:{email}, serviceAccount:{email}, group:{email}, domain:{domain}, projectOwner:{projectid}, projectEditor:{projectid}, projectViewer:{projectid}, or federated identities like principal://iam.googleapis.com/....
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.
How do I add time-based or conditional access to an IAM binding?
Use the condition property with title, description, and expression fields. For example, to expire access at a specific time, use expression: "request.time < timestamp(\"2020-01-01T00:00:00Z\")".
What properties can't I change after creating the resource?
All properties are immutable: member, role, name, project, and condition. You must recreate the resource to change any of these.
Limitations & Beta Status
Is this resource production-ready?
This resource is in beta and requires the terraform-provider-google-beta provider. Be aware of potential breaking changes.
Are there any limitations with IAM Conditions?
Yes, IAM Conditions have known limitations. Review the Google Cloud IAM Conditions limitations documentation if you encounter issues.
Import & Management
How do I import an existing IAM member binding?
Use space-delimited identifiers with the backend service path, role, and member identity: pulumi import gcp:compute/backendServiceIamMember:BackendServiceIamMember editor "projects/{{project}}/global/backendServices/{{backend_service}} roles/compute.admin user:jane@example.com"

Using a different cloud?

Explore security guides for other cloud providers: