The gcp:iap/webIamPolicy:WebIamPolicy resource, part of the Pulumi GCP provider, manages IAM policies for Identity-Aware Proxy web resources, controlling who can access IAP-protected applications. This guide focuses on four capabilities: authoritative policy replacement (WebIamPolicy), role-level grants (WebIamBinding), individual member grants (WebIamMember), and time-based access with IAM Conditions.
These resources reference a GCP project with IAP enabled and existing IAP-protected web resources. The examples are intentionally small. Combine them with your own project configuration and member identities.
Grant a role to multiple members with WebIamBinding
Teams managing IAP access for a specific role grant that role to multiple users or service accounts while preserving other roles in the policy.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const binding = new gcp.iap.WebIamBinding("binding", {
project: projectService.project,
role: "roles/iap.httpsResourceAccessor",
members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp
binding = gcp.iap.WebIamBinding("binding",
project=project_service["project"],
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.NewWebIamBinding(ctx, "binding", &iap.WebIamBindingArgs{
Project: pulumi.Any(projectService.Project),
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.WebIamBinding("binding", new()
{
Project = projectService.Project,
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.WebIamBinding;
import com.pulumi.gcp.iap.WebIamBindingArgs;
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 WebIamBinding("binding", WebIamBindingArgs.builder()
.project(projectService.project())
.role("roles/iap.httpsResourceAccessor")
.members("user:jane@example.com")
.build());
}
}
resources:
binding:
type: gcp:iap:WebIamBinding
properties:
project: ${projectService.project}
role: roles/iap.httpsResourceAccessor
members:
- user:jane@example.com
WebIamBinding is authoritative for a single role. It replaces all members for that role but leaves other roles untouched. The members array accepts identity strings like “user:jane@example.com”, “serviceAccount:…”, or “group:…”. This approach works well when you manage all members for a role together.
Add a single member to a role with WebIamMember
When onboarding individual users or service accounts, you add them one at a time without disrupting existing access.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const member = new gcp.iap.WebIamMember("member", {
project: projectService.project,
role: "roles/iap.httpsResourceAccessor",
member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp
member = gcp.iap.WebIamMember("member",
project=project_service["project"],
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.NewWebIamMember(ctx, "member", &iap.WebIamMemberArgs{
Project: pulumi.Any(projectService.Project),
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.WebIamMember("member", new()
{
Project = projectService.Project,
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.WebIamMember;
import com.pulumi.gcp.iap.WebIamMemberArgs;
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 WebIamMember("member", WebIamMemberArgs.builder()
.project(projectService.project())
.role("roles/iap.httpsResourceAccessor")
.member("user:jane@example.com")
.build());
}
}
resources:
member:
type: gcp:iap:WebIamMember
properties:
project: ${projectService.project}
role: roles/iap.httpsResourceAccessor
member: user:jane@example.com
WebIamMember is non-authoritative. It adds one member to a role without affecting other members who already have that role. Use this when different teams or processes manage access for the same role independently. You can combine multiple WebIamMember resources for the same role, or mix WebIamMember with WebIamBinding for different roles.
Replace the entire IAM policy with WebIamPolicy
Organizations that manage IAP access through external systems or need complete control use WebIamPolicy to define all bindings in one place.
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.WebIamPolicy("policy", {
project: projectService.project,
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.WebIamPolicy("policy",
project=project_service["project"],
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.NewWebIamPolicy(ctx, "policy", &iap.WebIamPolicyArgs{
Project: pulumi.Any(projectService.Project),
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.WebIamPolicy("policy", new()
{
Project = projectService.Project,
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.WebIamPolicy;
import com.pulumi.gcp.iap.WebIamPolicyArgs;
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 WebIamPolicy("policy", WebIamPolicyArgs.builder()
.project(projectService.project())
.policyData(admin.policyData())
.build());
}
}
resources:
policy:
type: gcp:iap:WebIamPolicy
properties:
project: ${projectService.project}
policyData: ${admin.policyData}
variables:
admin:
fn::invoke:
function: gcp:organizations:getIAMPolicy
arguments:
bindings:
- role: roles/iap.httpsResourceAccessor
members:
- user:jane@example.com
WebIamPolicy is fully authoritative. It replaces the entire IAM policy with the bindings you specify. The policyData property comes from the getIAMPolicy data source, which constructs policy JSON from a bindings array. Any role not listed in your bindings will be removed from the policy. WebIamPolicy cannot be used alongside WebIamBinding or WebIamMember; they conflict over policy ownership.
Apply time-based access with IAM Conditions
Temporary access grants, such as contractor access or time-limited testing, require conditions that expire automatically.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const binding = new gcp.iap.WebIamBinding("binding", {
project: projectService.project,
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.WebIamBinding("binding",
project=project_service["project"],
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.NewWebIamBinding(ctx, "binding", &iap.WebIamBindingArgs{
Project: pulumi.Any(projectService.Project),
Role: pulumi.String("roles/iap.httpsResourceAccessor"),
Members: pulumi.StringArray{
pulumi.String("user:jane@example.com"),
},
Condition: &iap.WebIamBindingConditionArgs{
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.WebIamBinding("binding", new()
{
Project = projectService.Project,
Role = "roles/iap.httpsResourceAccessor",
Members = new[]
{
"user:jane@example.com",
},
Condition = new Gcp.Iap.Inputs.WebIamBindingConditionArgs
{
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.WebIamBinding;
import com.pulumi.gcp.iap.WebIamBindingArgs;
import com.pulumi.gcp.iap.inputs.WebIamBindingConditionArgs;
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 WebIamBinding("binding", WebIamBindingArgs.builder()
.project(projectService.project())
.role("roles/iap.httpsResourceAccessor")
.members("user:jane@example.com")
.condition(WebIamBindingConditionArgs.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:WebIamBinding
properties:
project: ${projectService.project}
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")
IAM Conditions add temporal or contextual constraints to role bindings. The condition block requires a title and expression; description is optional. The expression uses Common Expression Language (CEL) to compare request.time against a timestamp. When the condition evaluates to false, the binding no longer grants access. Conditions work with WebIamBinding, WebIamMember, and WebIamPolicy, but have known limitations documented in the GCP IAM Conditions overview.
Beyond these examples
These snippets focus on specific IAM policy features: authoritative vs non-authoritative IAM management, role-based and member-based access grants, and time-based access with IAM Conditions. They’re intentionally minimal rather than full access control systems.
The examples may reference pre-existing infrastructure such as a GCP project with IAP enabled, and IAP-protected web resources (App Engine, Cloud Run, or load balancer backends). They focus on configuring IAM policies rather than provisioning the underlying IAP infrastructure.
To keep things focused, common IAM patterns are omitted, including:
- Service account and group member types (serviceAccount:, group:)
- Domain-wide access grants (domain:)
- Complex condition expressions (resource attributes, request context)
- Combining WebIamBinding and WebIamMember for different roles
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 IAP WebIamPolicy 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 FREEFrequently Asked Questions
Resource Selection & Conflicts
WebIamPolicy is authoritative and replaces the entire IAM policy. WebIamBinding is authoritative for a specific role, preserving other roles. WebIamMember is non-authoritative, adding a single member while preserving other members for that role.WebIamPolicy cannot be used with WebIamBinding or WebIamMember because they will conflict over the policy. Use WebIamPolicy alone or use WebIamBinding/WebIamMember together.Configuration & Setup
gcp.organizations.getIAMPolicy data source with your desired bindings, then pass its policyData output to the WebIamPolicy resource.project property is immutable and cannot be changed after creation.IAM Conditions
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.