The gcp:gkehub/membershipIamPolicy:MembershipIamPolicy resource, part of the Pulumi GCP provider, manages IAM policies for GKE Hub memberships, controlling who can access and manage registered clusters. This guide focuses on three approaches: authoritative policy replacement (MembershipIamPolicy), role-level binding management (MembershipIamBinding), and individual member grants (MembershipIamMember).
All three resources reference an existing GKE Hub membership by project, location, and membershipId. The examples are intentionally small. Combine them with your own membership infrastructure and access requirements.
Replace the entire IAM policy for a membership
When you need complete control over membership 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.gkehub.MembershipIamPolicy("policy", {
project: membership.project,
location: membership.location,
membershipId: membership.membershipId,
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.gkehub.MembershipIamPolicy("policy",
project=membership["project"],
location=membership["location"],
membership_id=membership["membershipId"],
policy_data=admin.policy_data)
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkehub"
"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/viewer",
Members: []string{
"user:jane@example.com",
},
},
},
}, nil)
if err != nil {
return err
}
_, err = gkehub.NewMembershipIamPolicy(ctx, "policy", &gkehub.MembershipIamPolicyArgs{
Project: pulumi.Any(membership.Project),
Location: pulumi.Any(membership.Location),
MembershipId: pulumi.Any(membership.MembershipId),
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.GkeHub.MembershipIamPolicy("policy", new()
{
Project = membership.Project,
Location = membership.Location,
MembershipId = membership.MembershipId,
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.gkehub.MembershipIamPolicy;
import com.pulumi.gcp.gkehub.MembershipIamPolicyArgs;
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 MembershipIamPolicy("policy", MembershipIamPolicyArgs.builder()
.project(membership.project())
.location(membership.location())
.membershipId(membership.membershipId())
.policyData(admin.policyData())
.build());
}
}
resources:
policy:
type: gcp:gkehub:MembershipIamPolicy
properties:
project: ${membership.project}
location: ${membership.location}
membershipId: ${membership.membershipId}
policyData: ${admin.policyData}
variables:
admin:
fn::invoke:
function: gcp:organizations:getIAMPolicy
arguments:
bindings:
- role: roles/viewer
members:
- user:jane@example.com
The MembershipIamPolicy resource is authoritative: it replaces all existing IAM bindings with the policy you provide. The policyData comes from the getIAMPolicy data source, which defines roles and members. Any permissions not included in policyData are removed when you apply this resource.
Grant a role to multiple members at once
Teams often need to assign the same role to several users without affecting other roles already on the membership.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const binding = new gcp.gkehub.MembershipIamBinding("binding", {
project: membership.project,
location: membership.location,
membershipId: membership.membershipId,
role: "roles/viewer",
members: ["user:jane@example.com"],
});
import pulumi
import pulumi_gcp as gcp
binding = gcp.gkehub.MembershipIamBinding("binding",
project=membership["project"],
location=membership["location"],
membership_id=membership["membershipId"],
role="roles/viewer",
members=["user:jane@example.com"])
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkehub"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := gkehub.NewMembershipIamBinding(ctx, "binding", &gkehub.MembershipIamBindingArgs{
Project: pulumi.Any(membership.Project),
Location: pulumi.Any(membership.Location),
MembershipId: pulumi.Any(membership.MembershipId),
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.GkeHub.MembershipIamBinding("binding", new()
{
Project = membership.Project,
Location = membership.Location,
MembershipId = membership.MembershipId,
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.gkehub.MembershipIamBinding;
import com.pulumi.gcp.gkehub.MembershipIamBindingArgs;
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 MembershipIamBinding("binding", MembershipIamBindingArgs.builder()
.project(membership.project())
.location(membership.location())
.membershipId(membership.membershipId())
.role("roles/viewer")
.members("user:jane@example.com")
.build());
}
}
resources:
binding:
type: gcp:gkehub:MembershipIamBinding
properties:
project: ${membership.project}
location: ${membership.location}
membershipId: ${membership.membershipId}
role: roles/viewer
members:
- user:jane@example.com
The MembershipIamBinding resource is authoritative for a single role: it replaces all members for that role but preserves other roles. The members property lists all identities that should have this role. This approach works well when you manage role membership as a group rather than adding members individually.
Add a single member to a role incrementally
When you need to grant access to one user without disturbing existing permissions, you can add individual members to roles.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const member = new gcp.gkehub.MembershipIamMember("member", {
project: membership.project,
location: membership.location,
membershipId: membership.membershipId,
role: "roles/viewer",
member: "user:jane@example.com",
});
import pulumi
import pulumi_gcp as gcp
member = gcp.gkehub.MembershipIamMember("member",
project=membership["project"],
location=membership["location"],
membership_id=membership["membershipId"],
role="roles/viewer",
member="user:jane@example.com")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/gkehub"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := gkehub.NewMembershipIamMember(ctx, "member", &gkehub.MembershipIamMemberArgs{
Project: pulumi.Any(membership.Project),
Location: pulumi.Any(membership.Location),
MembershipId: pulumi.Any(membership.MembershipId),
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.GkeHub.MembershipIamMember("member", new()
{
Project = membership.Project,
Location = membership.Location,
MembershipId = membership.MembershipId,
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.gkehub.MembershipIamMember;
import com.pulumi.gcp.gkehub.MembershipIamMemberArgs;
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 MembershipIamMember("member", MembershipIamMemberArgs.builder()
.project(membership.project())
.location(membership.location())
.membershipId(membership.membershipId())
.role("roles/viewer")
.member("user:jane@example.com")
.build());
}
}
resources:
member:
type: gcp:gkehub:MembershipIamMember
properties:
project: ${membership.project}
location: ${membership.location}
membershipId: ${membership.membershipId}
role: roles/viewer
member: user:jane@example.com
The MembershipIamMember resource is non-authoritative: it adds one member to one role without removing others. You can use multiple MembershipIamMember resources safely, even for the same role, as long as they grant access to different members. This is the safest option when multiple teams manage access independently.
Beyond these examples
These snippets focus on specific IAM management approaches: authoritative vs non-authoritative IAM management, and policy-level, role-level, and member-level control. They’re intentionally minimal rather than full access control systems.
The examples reference pre-existing infrastructure such as GKE Hub membership (project, location, membershipId). They focus on IAM policy configuration rather than provisioning the membership itself.
To keep things focused, common IAM patterns are omitted, including:
- Conditional IAM bindings (conditions)
- Audit logging configuration (auditConfigs)
- Custom role definitions
- Service account impersonation
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 GKE Hub MembershipIamPolicy resource reference for all available configuration options.
Let's manage GCP GKE Hub Membership 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
MembershipIamPolicy is authoritative and replaces the entire IAM policy. MembershipIamBinding is authoritative for a specific role but preserves other roles. MembershipIamMember is non-authoritative and adds individual members while preserving existing members for that role.MembershipIamPolicy with MembershipIamBinding or MembershipIamMember, as they will conflict. However, you can use MembershipIamBinding and MembershipIamMember together only if they manage different roles.MembershipIamPolicy (authoritative) with MembershipIamBinding or MembershipIamMember (granular) causes conflicts. Choose one approach: either use MembershipIamPolicy alone, or use MembershipIamBinding/MembershipIamMember without MembershipIamPolicy.Configuration & Setup
gcp.organizations.getIAMPolicy data source with your desired bindings, then pass its policyData output to the MembershipIamPolicy resource.global. If not specified, it’s parsed from the parent resource identifier or taken from the provider configuration.membershipId and location are immutable after creation. Changing them requires recreating the resource.Import & Migration
Import syntax varies by resource type:
- Policy:
pulumi import gcp:gkehub/membershipIamPolicy:MembershipIamPolicy editor projects/{{project}}/locations/{{location}}/memberships/{{membership_id}} - Binding: Add the role:
...{{membership_id}} roles/viewer - Member: Add role and member:
...{{membership_id}} roles/viewer user:jane@example.com
[projects/my-project|organizations/my-org]/roles/my-custom-role, not just the short role name.