The gcp:pubsub/topicIAMPolicy:TopicIAMPolicy resource, part of the Pulumi GCP provider, manages IAM permissions for Pub/Sub topics using three distinct approaches: complete policy replacement (TopicIAMPolicy), role-level binding (TopicIAMBinding), or individual member grants (TopicIAMMember). This guide focuses on three capabilities: complete policy replacement, role-level member binding, and individual member grants.
All three resources reference existing Pub/Sub topics by project and name. The examples are intentionally small. Combine them with your own topic infrastructure and organizational IAM policies.
Replace the entire IAM policy for a topic
When you need complete control over topic access, you can set the entire IAM policy at once, replacing any existing permissions.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const admin = gcp.organizations.getIAMPolicy({
bindings: [{
role: "roles/viewer",
members: ["user:jane@example.com"],
}],
});
const policy = new gcp.pubsub.TopicIAMPolicy("policy", {
project: example.project,
topic: example.name,
policyData: admin.then(admin => admin.policyData),
});
import pulumi
import pulumi_gcp as gcp
admin = gcp.organizations.get_iam_policy(bindings=[{
"role": "roles/viewer",
"members": ["user:jane@example.com"],
}])
policy = gcp.pubsub.TopicIAMPolicy("policy",
project=example["project"],
topic=example["name"],
policy_data=admin.policy_data)
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/pubsub"
"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/viewer",
Members: []string{
"user:jane@example.com",
},
},
},
}, nil)
if err != nil {
return err
}
_, err = pubsub.NewTopicIAMPolicy(ctx, "policy", &pubsub.TopicIAMPolicyArgs{
Project: pulumi.Any(example.Project),
Topic: pulumi.Any(example.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/viewer",
Members = new[]
{
"user:jane@example.com",
},
},
},
});
var policy = new Gcp.PubSub.TopicIAMPolicy("policy", new()
{
Project = example.Project,
Topic = example.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.pubsub.TopicIAMPolicy;
import com.pulumi.gcp.pubsub.TopicIAMPolicyArgs;
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/viewer")
.members("user:jane@example.com")
.build())
.build());
var policy = new TopicIAMPolicy("policy", TopicIAMPolicyArgs.builder()
.project(example.project())
.topic(example.name())
.policyData(admin.policyData())
.build());
}
}
resources:
policy:
type: gcp:pubsub:TopicIAMPolicy
properties:
project: ${example.project}
topic: ${example.name}
policyData: ${admin.policyData}
variables:
admin:
fn::invoke:
function: gcp:organizations:getIAMPolicy
arguments:
bindings:
- role: roles/viewer
members:
- user:jane@example.com
TopicIAMPolicy is authoritative: it replaces the topic’s entire IAM policy with the policy you provide. The policyData property accepts output from getIAMPolicy, which defines bindings (role-to-members mappings). This approach gives you complete control but requires coordination, since it overwrites any permissions set outside your Pulumi stack. TopicIAMPolicy cannot be used alongside TopicIAMBinding or TopicIAMMember for the same topic, as they would conflict over the policy state.
Grant a role to multiple members at once
Teams often need to grant the same role to several users or service accounts while preserving other role assignments.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const binding = new gcp.pubsub.TopicIAMBinding("binding", {
project: example.project,
topic: example.name,
role: "roles/viewer",
members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp
binding = gcp.pubsub.TopicIAMBinding("binding",
project=example["project"],
topic=example["name"],
role="roles/viewer",
members=["user:jane@example.com"])
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/pubsub"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := pubsub.NewTopicIAMBinding(ctx, "binding", &pubsub.TopicIAMBindingArgs{
Project: pulumi.Any(example.Project),
Topic: pulumi.Any(example.Name),
Role: pulumi.String("roles/viewer"),
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.PubSub.TopicIAMBinding("binding", new()
{
Project = example.Project,
Topic = example.Name,
Role = "roles/viewer",
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.pubsub.TopicIAMBinding;
import com.pulumi.gcp.pubsub.TopicIAMBindingArgs;
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 TopicIAMBinding("binding", TopicIAMBindingArgs.builder()
.project(example.project())
.topic(example.name())
.role("roles/viewer")
.members("user:jane@example.com")
.build());
}
}
resources:
binding:
type: gcp:pubsub:TopicIAMBinding
properties:
project: ${example.project}
topic: ${example.name}
role: roles/viewer
members:
- user:jane@example.com
TopicIAMBinding is authoritative for a single role: it sets the complete member list for that role while leaving other roles untouched. The members property accepts a list of identity strings (users, service accounts, groups). If another stack or process grants the same role, TopicIAMBinding will overwrite those grants. You can use multiple TopicIAMBinding resources for different roles on the same topic, but only one binding per role.
Add a single member to a role incrementally
When managing permissions across multiple stacks or teams, you often need to add one member without coordinating with other permission grants.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const member = new gcp.pubsub.TopicIAMMember("member", {
project: example.project,
topic: example.name,
role: "roles/viewer",
member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp
member = gcp.pubsub.TopicIAMMember("member",
project=example["project"],
topic=example["name"],
role="roles/viewer",
member="user:jane@example.com")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/pubsub"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := pubsub.NewTopicIAMMember(ctx, "member", &pubsub.TopicIAMMemberArgs{
Project: pulumi.Any(example.Project),
Topic: pulumi.Any(example.Name),
Role: pulumi.String("roles/viewer"),
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.PubSub.TopicIAMMember("member", new()
{
Project = example.Project,
Topic = example.Name,
Role = "roles/viewer",
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.pubsub.TopicIAMMember;
import com.pulumi.gcp.pubsub.TopicIAMMemberArgs;
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 TopicIAMMember("member", TopicIAMMemberArgs.builder()
.project(example.project())
.topic(example.name())
.role("roles/viewer")
.member("user:jane@example.com")
.build());
}
}
resources:
member:
type: gcp:pubsub:TopicIAMMember
properties:
project: ${example.project}
topic: ${example.name}
role: roles/viewer
member: user:jane@example.com
TopicIAMMember is non-authoritative: it adds a single member to a role without affecting other members. The member property accepts one identity string. Multiple TopicIAMMember resources can grant the same role to different members, and they coexist peacefully. This approach works well for distributed permission management but makes it harder to see the complete access picture in one place. TopicIAMMember can be used alongside TopicIAMBinding as long as they don’t grant the same role.
Beyond these examples
These snippets focus on specific IAM management features: authoritative vs non-authoritative IAM management, and policy-level, role-level, and member-level grants. They’re intentionally minimal rather than full access control configurations.
The examples reference pre-existing infrastructure such as Pub/Sub topics (by project and name). They focus on IAM binding configuration rather than topic provisioning.
To keep things focused, common IAM patterns are omitted, including:
- IAM conditions for time-based or attribute-based access
- Service account impersonation and domain restrictions
- Audit logging configuration
- Cross-project topic access
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 Pub/Sub TopicIAMPolicy resource reference for all available configuration options.
Let's manage GCP Pub/Sub Topic 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
TopicIAMPolicy is authoritative and replaces the entire IAM policy. TopicIAMBinding is authoritative for a specific role, preserving other roles. TopicIAMMember is non-authoritative, adding individual members while preserving existing ones.TopicIAMPolicy cannot be used with TopicIAMBinding or TopicIAMMember as they’ll conflict over policy state. However, TopicIAMBinding and TopicIAMMember can be used together if they don’t grant the same role.TopicIAMPolicy (authoritative) with TopicIAMBinding or TopicIAMMember. Use TopicIAMPolicy alone, or use only TopicIAMBinding/TopicIAMMember together.Configuration & Setup
gcp.organizations.getIAMPolicy data source with bindings, then pass its policyData output to the TopicIAMPolicy resource.topic and project are immutable. Changing them requires recreating the resource.Import & Migration
projects/{{project}}/topics/{{name}}, {{project}}/{{name}}, or just {{name}}.[projects/my-project|organizations/my-org]/roles/my-custom-role.