Manage GCP Identity-Aware Proxy IAM Bindings

The gcp:iap/webForwardingRuleServiceIamBinding:WebForwardingRuleServiceIamBinding resource, part of the Pulumi GCP provider, manages IAM role bindings for Identity-Aware Proxy forwarding rule services. This resource is authoritative for a given role, meaning it controls the complete list of members who have that role. This guide focuses on three capabilities: granting roles to multiple members, adding time-based access with IAM Conditions, and adding individual members non-authoritatively.

IAM bindings reference existing IAP-protected forwarding rule services and require a configured GCP project. The examples are intentionally small. Combine them with your own forwarding rule infrastructure and access policies.

Grant a role to multiple members with Binding

When managing access to IAP-protected load balancers, you often need to grant the same role to multiple users or service accounts as a group.

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

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

binding = gcp.iap.WebForwardingRuleServiceIamBinding("binding",
    project=default["project"],
    forwarding_rule_service_name=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.NewWebForwardingRuleServiceIamBinding(ctx, "binding", &iap.WebForwardingRuleServiceIamBindingArgs{
			Project:                   pulumi.Any(_default.Project),
			ForwardingRuleServiceName: 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.WebForwardingRuleServiceIamBinding("binding", new()
    {
        Project = @default.Project,
        ForwardingRuleServiceName = @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.WebForwardingRuleServiceIamBinding;
import com.pulumi.gcp.iap.WebForwardingRuleServiceIamBindingArgs;
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 WebForwardingRuleServiceIamBinding("binding", WebForwardingRuleServiceIamBindingArgs.builder()
            .project(default_.project())
            .forwardingRuleServiceName(default_.name())
            .role("roles/iap.httpsResourceAccessor")
            .members("user:jane@example.com")
            .build());

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

The Binding resource is authoritative for the specified role. The members array lists all identities that should have the role; any members not in this list will lose access. The forwardingRuleServiceName identifies which IAP-protected service receives the binding. Use this when you want to manage all members of a role together.

Add time-based access with IAM Conditions

Organizations with temporary contractors or time-limited projects can use IAM Conditions to automatically expire permissions.

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

const binding = new gcp.iap.WebForwardingRuleServiceIamBinding("binding", {
    project: _default.project,
    forwardingRuleServiceName: _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.WebForwardingRuleServiceIamBinding("binding",
    project=default["project"],
    forwarding_rule_service_name=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.NewWebForwardingRuleServiceIamBinding(ctx, "binding", &iap.WebForwardingRuleServiceIamBindingArgs{
			Project:                   pulumi.Any(_default.Project),
			ForwardingRuleServiceName: pulumi.Any(_default.Name),
			Role:                      pulumi.String("roles/iap.httpsResourceAccessor"),
			Members: pulumi.StringArray{
				pulumi.String("user:jane@example.com"),
			},
			Condition: &iap.WebForwardingRuleServiceIamBindingConditionArgs{
				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.WebForwardingRuleServiceIamBinding("binding", new()
    {
        Project = @default.Project,
        ForwardingRuleServiceName = @default.Name,
        Role = "roles/iap.httpsResourceAccessor",
        Members = new[]
        {
            "user:jane@example.com",
        },
        Condition = new Gcp.Iap.Inputs.WebForwardingRuleServiceIamBindingConditionArgs
        {
            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.WebForwardingRuleServiceIamBinding;
import com.pulumi.gcp.iap.WebForwardingRuleServiceIamBindingArgs;
import com.pulumi.gcp.iap.inputs.WebForwardingRuleServiceIamBindingConditionArgs;
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 WebForwardingRuleServiceIamBinding("binding", WebForwardingRuleServiceIamBindingArgs.builder()
            .project(default_.project())
            .forwardingRuleServiceName(default_.name())
            .role("roles/iap.httpsResourceAccessor")
            .members("user:jane@example.com")
            .condition(WebForwardingRuleServiceIamBindingConditionArgs.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:WebForwardingRuleServiceIamBinding
    properties:
      project: ${default.project}
      forwardingRuleServiceName: ${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 a time constraint to the role binding. The expression uses CEL (Common Expression Language) to define when access is valid. Here, access expires at midnight on 2020-01-01. The title and description help identify the condition’s purpose in audit logs and the console.

Add a single member to a role with Member

When you need to grant access to one additional user without replacing the entire member list, use the Member resource.

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

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

member = gcp.iap.WebForwardingRuleServiceIamMember("member",
    project=default["project"],
    forwarding_rule_service_name=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.NewWebForwardingRuleServiceIamMember(ctx, "member", &iap.WebForwardingRuleServiceIamMemberArgs{
			Project:                   pulumi.Any(_default.Project),
			ForwardingRuleServiceName: 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.WebForwardingRuleServiceIamMember("member", new()
    {
        Project = @default.Project,
        ForwardingRuleServiceName = @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.WebForwardingRuleServiceIamMember;
import com.pulumi.gcp.iap.WebForwardingRuleServiceIamMemberArgs;
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 WebForwardingRuleServiceIamMember("member", WebForwardingRuleServiceIamMemberArgs.builder()
            .project(default_.project())
            .forwardingRuleServiceName(default_.name())
            .role("roles/iap.httpsResourceAccessor")
            .member("user:jane@example.com")
            .build());

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

The Member resource is non-authoritative, meaning it adds one member to a role without affecting other members. This is useful when multiple teams manage access independently. The member property specifies a single identity using the same format as the members array in Binding (user:, serviceAccount:, group:, etc.).

Beyond these examples

These snippets focus on specific IAM binding features: role-based access control with Binding and Member resources, and time-based access with IAM Conditions. They’re intentionally minimal rather than complete access control policies.

The examples reference pre-existing infrastructure such as IAP-protected forwarding rule services and a GCP project with IAP enabled. They focus on configuring IAM bindings rather than provisioning the underlying IAP infrastructure.

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

  • Full policy replacement (IamPolicy resource)
  • Custom role definitions
  • Attribute-based conditions (resource tags, request attributes)
  • Combining Binding and Member for different roles

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 WebForwardingRuleServiceIamBinding resource reference for all available configuration options.

Let's manage GCP Identity-Aware Proxy 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 Conflicts & Compatibility
Can I use WebForwardingRuleServiceIamPolicy with WebForwardingRuleServiceIamBinding or WebForwardingRuleServiceIamMember?
No, WebForwardingRuleServiceIamPolicy cannot be used with WebForwardingRuleServiceIamBinding or WebForwardingRuleServiceIamMember as they will conflict over policy management. Choose either Policy (authoritative) or Binding/Member (non-authoritative).
Can I use WebForwardingRuleServiceIamBinding and WebForwardingRuleServiceIamMember together?
Yes, but only if they grant different roles. Using both resources for the same role will cause conflicts.
IAM Configuration & Roles
What member identity formats are supported?

You can use:

  • Users: user:alice@example.com
  • Service accounts: serviceAccount:my-app@appspot.gserviceaccount.com
  • Groups: group:admins@example.com
  • Domains: domain:example.com
  • Special identifiers: allUsers, allAuthenticatedUsers
  • Project roles: projectOwner:my-project, projectEditor:my-project, projectViewer:my-project
  • Federated identities: principal://iam.googleapis.com/locations/global/workforcePools/...
How do I specify custom IAM roles?
Custom roles must use the format [projects|organizations]/{parent-name}/roles/{role-name}. For example: projects/my-project/roles/my-custom-role or organizations/my-org/roles/my-custom-role.
What format do I use when importing resources with custom roles?
Use the fully qualified path: [projects/my-project|organizations/my-org]/roles/my-custom-role. Don’t use shortened formats during import.
IAM Conditions
How do I add time-based access restrictions to IAM bindings?
Use the condition property with title, description, and expression fields. Example: expression: "request.time < timestamp(\"2020-01-01T00:00:00Z\")" to expire access at a specific time.
Are there limitations when using IAM Conditions?
Yes, IAM Conditions have known limitations. Review the limitations before implementing conditions in production.
Can I modify IAM Conditions after creating a binding?
No, the condition property is immutable. You must recreate the binding to change conditions.

Using a different cloud?

Explore security guides for other cloud providers: