The gcp:iap/tunnelDestGroupIamMember:TunnelDestGroupIamMember resource, part of the Pulumi GCP provider, grants IAM permissions to individual users or service accounts for accessing resources through Identity-Aware Proxy tunnel destination groups. This guide focuses on three capabilities: single-member access grants, time-limited permissions with IAM Conditions, and multi-member role bindings.
This resource references existing IAP tunnel destination groups and requires valid project and region identifiers. The examples are intentionally small. Combine them with your own tunnel groups and identity management strategy.
Grant tunnel access to a single user
Most IAP tunnel access management starts by granting individual users permission to connect through specific destination groups.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const member = new gcp.iap.TunnelDestGroupIamMember("member", {
project: destGroup.project,
region: destGroup.region,
destGroup: destGroup.groupName,
role: "roles/iap.tunnelResourceAccessor",
member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp
member = gcp.iap.TunnelDestGroupIamMember("member",
project=dest_group["project"],
region=dest_group["region"],
dest_group=dest_group["groupName"],
role="roles/iap.tunnelResourceAccessor",
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.NewTunnelDestGroupIamMember(ctx, "member", &iap.TunnelDestGroupIamMemberArgs{
Project: pulumi.Any(destGroup.Project),
Region: pulumi.Any(destGroup.Region),
DestGroup: pulumi.Any(destGroup.GroupName),
Role: pulumi.String("roles/iap.tunnelResourceAccessor"),
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.TunnelDestGroupIamMember("member", new()
{
Project = destGroup.Project,
Region = destGroup.Region,
DestGroup = destGroup.GroupName,
Role = "roles/iap.tunnelResourceAccessor",
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.TunnelDestGroupIamMember;
import com.pulumi.gcp.iap.TunnelDestGroupIamMemberArgs;
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 TunnelDestGroupIamMember("member", TunnelDestGroupIamMemberArgs.builder()
.project(destGroup.project())
.region(destGroup.region())
.destGroup(destGroup.groupName())
.role("roles/iap.tunnelResourceAccessor")
.member("user:jane@example.com")
.build());
}
}
resources:
member:
type: gcp:iap:TunnelDestGroupIamMember
properties:
project: ${destGroup.project}
region: ${destGroup.region}
destGroup: ${destGroup.groupName}
role: roles/iap.tunnelResourceAccessor
member: user:jane@example.com
The member property specifies the identity receiving access, using formats like “user:email@example.com” for Google accounts or “serviceAccount:email@project.iam.gserviceaccount.com” for service accounts. The role property defines the permission level; “roles/iap.tunnelResourceAccessor” allows connecting through the tunnel. The destGroup, project, and region properties identify which tunnel destination group receives the permission grant.
Add time-limited access with IAM Conditions
Organizations implementing temporary access or compliance requirements use IAM Conditions to automatically expire permissions.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const member = new gcp.iap.TunnelDestGroupIamMember("member", {
project: destGroup.project,
region: destGroup.region,
destGroup: destGroup.groupName,
role: "roles/iap.tunnelResourceAccessor",
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.iap.TunnelDestGroupIamMember("member",
project=dest_group["project"],
region=dest_group["region"],
dest_group=dest_group["groupName"],
role="roles/iap.tunnelResourceAccessor",
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/iap"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := iap.NewTunnelDestGroupIamMember(ctx, "member", &iap.TunnelDestGroupIamMemberArgs{
Project: pulumi.Any(destGroup.Project),
Region: pulumi.Any(destGroup.Region),
DestGroup: pulumi.Any(destGroup.GroupName),
Role: pulumi.String("roles/iap.tunnelResourceAccessor"),
Member: pulumi.String("user:jane@example.com"),
Condition: &iap.TunnelDestGroupIamMemberConditionArgs{
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.Iap.TunnelDestGroupIamMember("member", new()
{
Project = destGroup.Project,
Region = destGroup.Region,
DestGroup = destGroup.GroupName,
Role = "roles/iap.tunnelResourceAccessor",
Member = "user:jane@example.com",
Condition = new Gcp.Iap.Inputs.TunnelDestGroupIamMemberConditionArgs
{
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.TunnelDestGroupIamMember;
import com.pulumi.gcp.iap.TunnelDestGroupIamMemberArgs;
import com.pulumi.gcp.iap.inputs.TunnelDestGroupIamMemberConditionArgs;
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 TunnelDestGroupIamMember("member", TunnelDestGroupIamMemberArgs.builder()
.project(destGroup.project())
.region(destGroup.region())
.destGroup(destGroup.groupName())
.role("roles/iap.tunnelResourceAccessor")
.member("user:jane@example.com")
.condition(TunnelDestGroupIamMemberConditionArgs.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:iap:TunnelDestGroupIamMember
properties:
project: ${destGroup.project}
region: ${destGroup.region}
destGroup: ${destGroup.groupName}
role: roles/iap.tunnelResourceAccessor
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 restrictions to the member grant. The expression property uses CEL (Common Expression Language) to define when access is valid; here, access expires at midnight on 2020-01-01. The title and description properties document the condition’s purpose. When the condition evaluates to false, the member loses access automatically without manual revocation.
Grant tunnel access to multiple users at once
When multiple users need the same access level, binding a role to a list of members ensures consistent permissions.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const binding = new gcp.iap.TunnelDestGroupIamBinding("binding", {
project: destGroup.project,
region: destGroup.region,
destGroup: destGroup.groupName,
role: "roles/iap.tunnelResourceAccessor",
members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp
binding = gcp.iap.TunnelDestGroupIamBinding("binding",
project=dest_group["project"],
region=dest_group["region"],
dest_group=dest_group["groupName"],
role="roles/iap.tunnelResourceAccessor",
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.NewTunnelDestGroupIamBinding(ctx, "binding", &iap.TunnelDestGroupIamBindingArgs{
Project: pulumi.Any(destGroup.Project),
Region: pulumi.Any(destGroup.Region),
DestGroup: pulumi.Any(destGroup.GroupName),
Role: pulumi.String("roles/iap.tunnelResourceAccessor"),
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.TunnelDestGroupIamBinding("binding", new()
{
Project = destGroup.Project,
Region = destGroup.Region,
DestGroup = destGroup.GroupName,
Role = "roles/iap.tunnelResourceAccessor",
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.TunnelDestGroupIamBinding;
import com.pulumi.gcp.iap.TunnelDestGroupIamBindingArgs;
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 TunnelDestGroupIamBinding("binding", TunnelDestGroupIamBindingArgs.builder()
.project(destGroup.project())
.region(destGroup.region())
.destGroup(destGroup.groupName())
.role("roles/iap.tunnelResourceAccessor")
.members("user:jane@example.com")
.build());
}
}
resources:
binding:
type: gcp:iap:TunnelDestGroupIamBinding
properties:
project: ${destGroup.project}
region: ${destGroup.region}
destGroup: ${destGroup.groupName}
role: roles/iap.tunnelResourceAccessor
members:
- user:jane@example.com
The TunnelDestGroupIamBinding resource grants a role to multiple members simultaneously. The members property accepts a list of identities in the same formats as the single-member resource. This approach is authoritative for the specified role: it replaces any existing member list for that role on the tunnel destination group.
Beyond these examples
These snippets focus on specific IAM member features: individual and group member grants, and time-based access expiration. They’re intentionally minimal rather than full access control policies.
The examples reference pre-existing infrastructure such as IAP tunnel destination groups, and GCP projects and regions. They focus on granting permissions rather than provisioning the tunnel infrastructure itself.
To keep things focused, common IAM patterns are omitted, including:
- Full policy replacement (TunnelDestGroupIamPolicy)
- Custom role definitions
- Federated identity configuration
- Workload identity pool integration
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 TunnelDestGroupIamMember resource reference for all available configuration options.
Let's manage GCP Identity-Aware Proxy IAM Permissions
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
Choose based on your needs:
gcp.iap.TunnelDestGroupIamPolicy: Authoritative, replaces the entire IAM policygcp.iap.TunnelDestGroupIamBinding: Authoritative for a specific role, preserves other rolesgcp.iap.TunnelDestGroupIamMember: Non-authoritative, adds individual members without affecting others
gcp.iap.TunnelDestGroupIamPolicy cannot be used with gcp.iap.TunnelDestGroupIamBinding or gcp.iap.TunnelDestGroupIamMember because they’ll conflict over policy management. Use IamPolicy alone, or use IamBinding/IamMember together.IAM Configuration
Supported formats include:
allUsersorallAuthenticatedUsersfor public/authenticated accessuser:{email},serviceAccount:{email},group:{email}for specific identitiesdomain:{domain}for G Suite domainsprojectOwner:,projectEditor:,projectViewer:with project ID- Federated identities using
principal://format
[projects|organizations]/{parent-name}/roles/{role-name}, for example projects/my-project/roles/customRole.condition property with a title, description, and expression. Note that IAM Conditions have known limitations you should review before use.Resource Lifecycle
destGroup, member, project, region, role, and condition. Changes require resource replacement.region must be the same as the network resources in the tunnel destination group to ensure proper connectivity.