Configure GCP Identity-Aware Proxy IAM Policies

The gcp:iap/webRegionBackendServiceIamPolicy:WebRegionBackendServiceIamPolicy resource, part of the Pulumi GCP provider, manages IAM policies for Identity-Aware Proxy regional backend services, controlling who can access IAP-protected resources. This guide focuses on four capabilities: authoritative policy replacement, role binding for multiple members, single member assignment, and time-based access with IAM Conditions.

These resources reference existing regional backend services and require project/region configuration. The examples are intentionally small. Combine them with your own backend service infrastructure and IAM role definitions.

Grant a role to multiple members at once

Teams managing IAP access often need to assign the same role to multiple users or service accounts simultaneously.

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

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

binding = gcp.iap.WebRegionBackendServiceIamBinding("binding",
    project=default["project"],
    region=default["region"],
    web_region_backend_service=default["name"],
    role="roles/iap.httpsResourceAccessor",
    members=["user:jane@example.com"])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := iap.NewWebRegionBackendServiceIamBinding(ctx, "binding", &iap.WebRegionBackendServiceIamBindingArgs{
			Project:                 pulumi.Any(_default.Project),
			Region:                  pulumi.Any(_default.Region),
			WebRegionBackendService: pulumi.Any(_default.Name),
			Role:                    pulumi.String("roles/iap.httpsResourceAccessor"),
			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.Iap.WebRegionBackendServiceIamBinding("binding", new()
    {
        Project = @default.Project,
        Region = @default.Region,
        WebRegionBackendService = @default.Name,
        Role = "roles/iap.httpsResourceAccessor",
        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.iap.WebRegionBackendServiceIamBinding;
import com.pulumi.gcp.iap.WebRegionBackendServiceIamBindingArgs;
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 WebRegionBackendServiceIamBinding("binding", WebRegionBackendServiceIamBindingArgs.builder()
            .project(default_.project())
            .region(default_.region())
            .webRegionBackendService(default_.name())
            .role("roles/iap.httpsResourceAccessor")
            .members("user:jane@example.com")
            .build());

    }
}
resources:
  binding:
    type: gcp:iap:WebRegionBackendServiceIamBinding
    properties:
      project: ${default.project}
      region: ${default.region}
      webRegionBackendService: ${default.name}
      role: roles/iap.httpsResourceAccessor
      members:
        - user:jane@example.com

The WebRegionBackendServiceIamBinding resource grants a role to all members in the list. The role property specifies which IAP permission to grant (here, httpsResourceAccessor for HTTPS access). The members array lists all identities receiving the role. This resource is authoritative for the specified role: it replaces any existing member list for that role but preserves other roles in the policy.

Add a single member to an existing role

When onboarding individual users, you can add members one at a time without affecting others who already have the role.

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

const member = new gcp.iap.WebRegionBackendServiceIamMember("member", {
    project: _default.project,
    region: _default.region,
    webRegionBackendService: _default.name,
    role: "roles/iap.httpsResourceAccessor",
    member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp

member = gcp.iap.WebRegionBackendServiceIamMember("member",
    project=default["project"],
    region=default["region"],
    web_region_backend_service=default["name"],
    role="roles/iap.httpsResourceAccessor",
    member="user:jane@example.com")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := iap.NewWebRegionBackendServiceIamMember(ctx, "member", &iap.WebRegionBackendServiceIamMemberArgs{
			Project:                 pulumi.Any(_default.Project),
			Region:                  pulumi.Any(_default.Region),
			WebRegionBackendService: pulumi.Any(_default.Name),
			Role:                    pulumi.String("roles/iap.httpsResourceAccessor"),
			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.Iap.WebRegionBackendServiceIamMember("member", new()
    {
        Project = @default.Project,
        Region = @default.Region,
        WebRegionBackendService = @default.Name,
        Role = "roles/iap.httpsResourceAccessor",
        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.iap.WebRegionBackendServiceIamMember;
import com.pulumi.gcp.iap.WebRegionBackendServiceIamMemberArgs;
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 WebRegionBackendServiceIamMember("member", WebRegionBackendServiceIamMemberArgs.builder()
            .project(default_.project())
            .region(default_.region())
            .webRegionBackendService(default_.name())
            .role("roles/iap.httpsResourceAccessor")
            .member("user:jane@example.com")
            .build());

    }
}
resources:
  member:
    type: gcp:iap:WebRegionBackendServiceIamMember
    properties:
      project: ${default.project}
      region: ${default.region}
      webRegionBackendService: ${default.name}
      role: roles/iap.httpsResourceAccessor
      member: user:jane@example.com

The WebRegionBackendServiceIamMember resource adds a single member to a role. Unlike Binding, this resource is non-authoritative: it adds the member without removing others. The member property takes a single identity (user, serviceAccount, or group). You can use multiple Member resources for the same role without conflicts.

Replace the entire IAM policy with a new definition

Some workflows require complete control over the IAM policy, replacing all existing bindings.

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

const admin = gcp.organizations.getIAMPolicy({
    bindings: [{
        role: "roles/iap.httpsResourceAccessor",
        members: ["user:jane@example.com"],
    }],
});
const policy = new gcp.iap.WebRegionBackendServiceIamPolicy("policy", {
    project: _default.project,
    region: _default.region,
    webRegionBackendService: _default.name,
    policyData: admin.then(admin => admin.policyData),
});
import pulumi
import pulumi_gcp as gcp

admin = gcp.organizations.get_iam_policy(bindings=[{
    "role": "roles/iap.httpsResourceAccessor",
    "members": ["user:jane@example.com"],
}])
policy = gcp.iap.WebRegionBackendServiceIamPolicy("policy",
    project=default["project"],
    region=default["region"],
    web_region_backend_service=default["name"],
    policy_data=admin.policy_data)
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		admin, err := organizations.LookupIAMPolicy(ctx, &organizations.LookupIAMPolicyArgs{
			Bindings: []organizations.GetIAMPolicyBinding{
				{
					Role: "roles/iap.httpsResourceAccessor",
					Members: []string{
						"user:jane@example.com",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		_, err = iap.NewWebRegionBackendServiceIamPolicy(ctx, "policy", &iap.WebRegionBackendServiceIamPolicyArgs{
			Project:                 pulumi.Any(_default.Project),
			Region:                  pulumi.Any(_default.Region),
			WebRegionBackendService: pulumi.Any(_default.Name),
			PolicyData:              pulumi.String(admin.PolicyData),
		})
		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 admin = Gcp.Organizations.GetIAMPolicy.Invoke(new()
    {
        Bindings = new[]
        {
            new Gcp.Organizations.Inputs.GetIAMPolicyBindingInputArgs
            {
                Role = "roles/iap.httpsResourceAccessor",
                Members = new[]
                {
                    "user:jane@example.com",
                },
            },
        },
    });

    var policy = new Gcp.Iap.WebRegionBackendServiceIamPolicy("policy", new()
    {
        Project = @default.Project,
        Region = @default.Region,
        WebRegionBackendService = @default.Name,
        PolicyData = admin.Apply(getIAMPolicyResult => getIAMPolicyResult.PolicyData),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetIAMPolicyArgs;
import com.pulumi.gcp.iap.WebRegionBackendServiceIamPolicy;
import com.pulumi.gcp.iap.WebRegionBackendServiceIamPolicyArgs;
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) {
        final var admin = OrganizationsFunctions.getIAMPolicy(GetIAMPolicyArgs.builder()
            .bindings(GetIAMPolicyBindingArgs.builder()
                .role("roles/iap.httpsResourceAccessor")
                .members("user:jane@example.com")
                .build())
            .build());

        var policy = new WebRegionBackendServiceIamPolicy("policy", WebRegionBackendServiceIamPolicyArgs.builder()
            .project(default_.project())
            .region(default_.region())
            .webRegionBackendService(default_.name())
            .policyData(admin.policyData())
            .build());

    }
}
resources:
  policy:
    type: gcp:iap:WebRegionBackendServiceIamPolicy
    properties:
      project: ${default.project}
      region: ${default.region}
      webRegionBackendService: ${default.name}
      policyData: ${admin.policyData}
variables:
  admin:
    fn::invoke:
      function: gcp:organizations:getIAMPolicy
      arguments:
        bindings:
          - role: roles/iap.httpsResourceAccessor
            members:
              - user:jane@example.com

The WebRegionBackendServiceIamPolicy resource sets the complete IAM policy from a data source. The policyData property accepts output from getIAMPolicy, which defines all role bindings. This resource is fully authoritative: it replaces the entire policy, removing any bindings not included in policyData. It cannot coexist with Binding or Member resources for the same backend service.

Apply time-based access with IAM Conditions

Temporary access grants use IAM Conditions to automatically expire permissions after a specific date.

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

const binding = new gcp.iap.WebRegionBackendServiceIamBinding("binding", {
    project: _default.project,
    region: _default.region,
    webRegionBackendService: _default.name,
    role: "roles/iap.httpsResourceAccessor",
    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.iap.WebRegionBackendServiceIamBinding("binding",
    project=default["project"],
    region=default["region"],
    web_region_backend_service=default["name"],
    role="roles/iap.httpsResourceAccessor",
    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/iap"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := iap.NewWebRegionBackendServiceIamBinding(ctx, "binding", &iap.WebRegionBackendServiceIamBindingArgs{
			Project:                 pulumi.Any(_default.Project),
			Region:                  pulumi.Any(_default.Region),
			WebRegionBackendService: pulumi.Any(_default.Name),
			Role:                    pulumi.String("roles/iap.httpsResourceAccessor"),
			Members: pulumi.StringArray{
				pulumi.String("user:jane@example.com"),
			},
			Condition: &iap.WebRegionBackendServiceIamBindingConditionArgs{
				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.Iap.WebRegionBackendServiceIamBinding("binding", new()
    {
        Project = @default.Project,
        Region = @default.Region,
        WebRegionBackendService = @default.Name,
        Role = "roles/iap.httpsResourceAccessor",
        Members = new[]
        {
            "user:jane@example.com",
        },
        Condition = new Gcp.Iap.Inputs.WebRegionBackendServiceIamBindingConditionArgs
        {
            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.iap.WebRegionBackendServiceIamBinding;
import com.pulumi.gcp.iap.WebRegionBackendServiceIamBindingArgs;
import com.pulumi.gcp.iap.inputs.WebRegionBackendServiceIamBindingConditionArgs;
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 WebRegionBackendServiceIamBinding("binding", WebRegionBackendServiceIamBindingArgs.builder()
            .project(default_.project())
            .region(default_.region())
            .webRegionBackendService(default_.name())
            .role("roles/iap.httpsResourceAccessor")
            .members("user:jane@example.com")
            .condition(WebRegionBackendServiceIamBindingConditionArgs.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:iap:WebRegionBackendServiceIamBinding
    properties:
      project: ${default.project}
      region: ${default.region}
      webRegionBackendService: ${default.name}
      role: roles/iap.httpsResourceAccessor
      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 constraints to role bindings. The expression property uses CEL (Common Expression Language) to define when access is valid. Here, request.time checks the current timestamp against a deadline. The title property identifies the condition. IAM Conditions work with Binding and Member resources but have known limitations documented by Google Cloud.

Beyond these examples

These snippets focus on specific IAM management features: authoritative vs non-authoritative IAM management, role binding and member assignment, and time-based access with IAM Conditions. They’re intentionally minimal rather than full access control configurations.

The examples reference pre-existing infrastructure such as regional backend services (webRegionBackendService) and GCP project and region configuration. They focus on IAM policy configuration rather than provisioning the backend services themselves.

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

  • Policy retrieval with data sources
  • Custom IAM roles (requires full role path)
  • Condition descriptions and complex expressions
  • Import syntax for existing IAM resources

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

Let's configure GCP Identity-Aware Proxy IAM Policies

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: Policy, Binding, or Member?
Use WebRegionBackendServiceIamPolicy for full control (replaces entire policy), WebRegionBackendServiceIamBinding to manage all members for a specific role (preserves other roles), or WebRegionBackendServiceIamMember to add individual members without affecting others (non-authoritative).
Why am I getting policy conflicts when using IamPolicy with IamBinding or IamMember?
WebRegionBackendServiceIamPolicy cannot be used with WebRegionBackendServiceIamBinding or WebRegionBackendServiceIamMember because they will conflict over policy management. Use either the authoritative Policy resource alone, or use the non-authoritative Binding/Member resources together.
Can I use IamBinding and IamMember together?
Yes, but only if they manage different roles. WebRegionBackendServiceIamBinding and WebRegionBackendServiceIamMember will conflict if they grant privileges to the same role.
IAM Conditions
How do I add time-based or conditional access to IAM policies?
Add a condition block with title, description, and expression fields. For example, use request.time < timestamp("2020-01-01T00:00:00Z") to expire access at a specific time.
What are the limitations of IAM Conditions?
IAM Conditions are supported but have known limitations. Review the GCP IAM Conditions limitations documentation if you encounter issues.
Configuration & Properties
What properties can't be changed after creation?
The project, region, and webRegionBackendService properties are immutable and cannot be modified after the resource is created.

Using a different cloud?

Explore iam guides for other cloud providers: