The gcp:gkehub/featureMembership:FeatureMembership resource, part of the Pulumi GCP provider, configures GKE Hub (Fleet) features for specific cluster memberships. This guide focuses on three capabilities: Config Sync with Git and OCI sources, managed service mesh enablement, and Policy Controller enforcement.
Feature memberships bind Fleet features to individual clusters. They require existing GKE clusters, Fleet memberships, and Fleet features to be created first. The examples are intentionally small. Combine them with your own cluster infrastructure and feature definitions.
Enable Config Sync with automatic version management
Teams managing multiple clusters often start with Config Sync auto-upgrades, where Google handles version updates and component lifecycle automatically.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-cluster",
location: "us-central1-a",
initialNodeCount: 1,
});
const membership = new gcp.gkehub.Membership("membership", {
membershipId: "my-membership",
endpoint: {
gkeCluster: {
resourceLink: pulumi.interpolate`//container.googleapis.com/${cluster.id}`,
},
},
});
const feature = new gcp.gkehub.Feature("feature", {
name: "configmanagement",
location: "global",
labels: {
foo: "bar",
},
});
const featureMember = new gcp.gkehub.FeatureMembership("feature_member", {
location: "global",
feature: feature.name,
membership: membership.membershipId,
configmanagement: {
management: "MANAGEMENT_AUTOMATIC",
configSync: {
enabled: true,
},
},
});
import pulumi
import pulumi_gcp as gcp
cluster = gcp.container.Cluster("cluster",
name="my-cluster",
location="us-central1-a",
initial_node_count=1)
membership = gcp.gkehub.Membership("membership",
membership_id="my-membership",
endpoint={
"gke_cluster": {
"resource_link": cluster.id.apply(lambda id: f"//container.googleapis.com/{id}"),
},
})
feature = gcp.gkehub.Feature("feature",
name="configmanagement",
location="global",
labels={
"foo": "bar",
})
feature_member = gcp.gkehub.FeatureMembership("feature_member",
location="global",
feature=feature.name,
membership=membership.membership_id,
configmanagement={
"management": "MANAGEMENT_AUTOMATIC",
"config_sync": {
"enabled": True,
},
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"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 {
cluster, err := container.NewCluster(ctx, "cluster", &container.ClusterArgs{
Name: pulumi.String("my-cluster"),
Location: pulumi.String("us-central1-a"),
InitialNodeCount: pulumi.Int(1),
})
if err != nil {
return err
}
membership, err := gkehub.NewMembership(ctx, "membership", &gkehub.MembershipArgs{
MembershipId: pulumi.String("my-membership"),
Endpoint: &gkehub.MembershipEndpointArgs{
GkeCluster: &gkehub.MembershipEndpointGkeClusterArgs{
ResourceLink: cluster.ID().ApplyT(func(id string) (string, error) {
return fmt.Sprintf("//container.googleapis.com/%v", id), nil
}).(pulumi.StringOutput),
},
},
})
if err != nil {
return err
}
feature, err := gkehub.NewFeature(ctx, "feature", &gkehub.FeatureArgs{
Name: pulumi.String("configmanagement"),
Location: pulumi.String("global"),
Labels: pulumi.StringMap{
"foo": pulumi.String("bar"),
},
})
if err != nil {
return err
}
_, err = gkehub.NewFeatureMembership(ctx, "feature_member", &gkehub.FeatureMembershipArgs{
Location: pulumi.String("global"),
Feature: feature.Name,
Membership: membership.MembershipId,
Configmanagement: &gkehub.FeatureMembershipConfigmanagementArgs{
Management: pulumi.String("MANAGEMENT_AUTOMATIC"),
ConfigSync: &gkehub.FeatureMembershipConfigmanagementConfigSyncArgs{
Enabled: pulumi.Bool(true),
},
},
})
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 cluster = new Gcp.Container.Cluster("cluster", new()
{
Name = "my-cluster",
Location = "us-central1-a",
InitialNodeCount = 1,
});
var membership = new Gcp.GkeHub.Membership("membership", new()
{
MembershipId = "my-membership",
Endpoint = new Gcp.GkeHub.Inputs.MembershipEndpointArgs
{
GkeCluster = new Gcp.GkeHub.Inputs.MembershipEndpointGkeClusterArgs
{
ResourceLink = cluster.Id.Apply(id => $"//container.googleapis.com/{id}"),
},
},
});
var feature = new Gcp.GkeHub.Feature("feature", new()
{
Name = "configmanagement",
Location = "global",
Labels =
{
{ "foo", "bar" },
},
});
var featureMember = new Gcp.GkeHub.FeatureMembership("feature_member", new()
{
Location = "global",
Feature = feature.Name,
Membership = membership.MembershipId,
Configmanagement = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementArgs
{
Management = "MANAGEMENT_AUTOMATIC",
ConfigSync = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementConfigSyncArgs
{
Enabled = true,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.gkehub.Membership;
import com.pulumi.gcp.gkehub.MembershipArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointGkeClusterArgs;
import com.pulumi.gcp.gkehub.Feature;
import com.pulumi.gcp.gkehub.FeatureArgs;
import com.pulumi.gcp.gkehub.FeatureMembership;
import com.pulumi.gcp.gkehub.FeatureMembershipArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipConfigmanagementArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipConfigmanagementConfigSyncArgs;
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 cluster = new Cluster("cluster", ClusterArgs.builder()
.name("my-cluster")
.location("us-central1-a")
.initialNodeCount(1)
.build());
var membership = new Membership("membership", MembershipArgs.builder()
.membershipId("my-membership")
.endpoint(MembershipEndpointArgs.builder()
.gkeCluster(MembershipEndpointGkeClusterArgs.builder()
.resourceLink(cluster.id().applyValue(_id -> String.format("//container.googleapis.com/%s", _id)))
.build())
.build())
.build());
var feature = new Feature("feature", FeatureArgs.builder()
.name("configmanagement")
.location("global")
.labels(Map.of("foo", "bar"))
.build());
var featureMember = new FeatureMembership("featureMember", FeatureMembershipArgs.builder()
.location("global")
.feature(feature.name())
.membership(membership.membershipId())
.configmanagement(FeatureMembershipConfigmanagementArgs.builder()
.management("MANAGEMENT_AUTOMATIC")
.configSync(FeatureMembershipConfigmanagementConfigSyncArgs.builder()
.enabled(true)
.build())
.build())
.build());
}
}
resources:
cluster:
type: gcp:container:Cluster
properties:
name: my-cluster
location: us-central1-a
initialNodeCount: 1
membership:
type: gcp:gkehub:Membership
properties:
membershipId: my-membership
endpoint:
gkeCluster:
resourceLink: //container.googleapis.com/${cluster.id}
feature:
type: gcp:gkehub:Feature
properties:
name: configmanagement
location: global
labels:
foo: bar
featureMember:
type: gcp:gkehub:FeatureMembership
name: feature_member
properties:
location: global
feature: ${feature.name}
membership: ${membership.membershipId}
configmanagement:
management: MANAGEMENT_AUTOMATIC
configSync:
enabled: true
When management is set to MANAGEMENT_AUTOMATIC, Google controls Config Sync versions and upgrades. The configSync block enables the feature without specifying a Git or OCI source, allowing you to configure sources later or use other sync methods.
Sync cluster configuration from a Git repository
Most Config Management deployments sync Kubernetes manifests from a Git repository, enabling GitOps workflows where infrastructure changes follow code review processes.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-cluster",
location: "us-central1-a",
initialNodeCount: 1,
});
const membership = new gcp.gkehub.Membership("membership", {
membershipId: "my-membership",
endpoint: {
gkeCluster: {
resourceLink: pulumi.interpolate`//container.googleapis.com/${cluster.id}`,
},
},
});
const feature = new gcp.gkehub.Feature("feature", {
name: "configmanagement",
location: "global",
labels: {
foo: "bar",
},
});
const featureMember = new gcp.gkehub.FeatureMembership("feature_member", {
location: "global",
feature: feature.name,
membership: membership.membershipId,
configmanagement: {
version: "1.19.0",
configSync: {
enabled: true,
git: {
syncRepo: "https://github.com/hashicorp/terraform",
},
},
},
});
import pulumi
import pulumi_gcp as gcp
cluster = gcp.container.Cluster("cluster",
name="my-cluster",
location="us-central1-a",
initial_node_count=1)
membership = gcp.gkehub.Membership("membership",
membership_id="my-membership",
endpoint={
"gke_cluster": {
"resource_link": cluster.id.apply(lambda id: f"//container.googleapis.com/{id}"),
},
})
feature = gcp.gkehub.Feature("feature",
name="configmanagement",
location="global",
labels={
"foo": "bar",
})
feature_member = gcp.gkehub.FeatureMembership("feature_member",
location="global",
feature=feature.name,
membership=membership.membership_id,
configmanagement={
"version": "1.19.0",
"config_sync": {
"enabled": True,
"git": {
"sync_repo": "https://github.com/hashicorp/terraform",
},
},
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"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 {
cluster, err := container.NewCluster(ctx, "cluster", &container.ClusterArgs{
Name: pulumi.String("my-cluster"),
Location: pulumi.String("us-central1-a"),
InitialNodeCount: pulumi.Int(1),
})
if err != nil {
return err
}
membership, err := gkehub.NewMembership(ctx, "membership", &gkehub.MembershipArgs{
MembershipId: pulumi.String("my-membership"),
Endpoint: &gkehub.MembershipEndpointArgs{
GkeCluster: &gkehub.MembershipEndpointGkeClusterArgs{
ResourceLink: cluster.ID().ApplyT(func(id string) (string, error) {
return fmt.Sprintf("//container.googleapis.com/%v", id), nil
}).(pulumi.StringOutput),
},
},
})
if err != nil {
return err
}
feature, err := gkehub.NewFeature(ctx, "feature", &gkehub.FeatureArgs{
Name: pulumi.String("configmanagement"),
Location: pulumi.String("global"),
Labels: pulumi.StringMap{
"foo": pulumi.String("bar"),
},
})
if err != nil {
return err
}
_, err = gkehub.NewFeatureMembership(ctx, "feature_member", &gkehub.FeatureMembershipArgs{
Location: pulumi.String("global"),
Feature: feature.Name,
Membership: membership.MembershipId,
Configmanagement: &gkehub.FeatureMembershipConfigmanagementArgs{
Version: pulumi.String("1.19.0"),
ConfigSync: &gkehub.FeatureMembershipConfigmanagementConfigSyncArgs{
Enabled: pulumi.Bool(true),
Git: &gkehub.FeatureMembershipConfigmanagementConfigSyncGitArgs{
SyncRepo: pulumi.String("https://github.com/hashicorp/terraform"),
},
},
},
})
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 cluster = new Gcp.Container.Cluster("cluster", new()
{
Name = "my-cluster",
Location = "us-central1-a",
InitialNodeCount = 1,
});
var membership = new Gcp.GkeHub.Membership("membership", new()
{
MembershipId = "my-membership",
Endpoint = new Gcp.GkeHub.Inputs.MembershipEndpointArgs
{
GkeCluster = new Gcp.GkeHub.Inputs.MembershipEndpointGkeClusterArgs
{
ResourceLink = cluster.Id.Apply(id => $"//container.googleapis.com/{id}"),
},
},
});
var feature = new Gcp.GkeHub.Feature("feature", new()
{
Name = "configmanagement",
Location = "global",
Labels =
{
{ "foo", "bar" },
},
});
var featureMember = new Gcp.GkeHub.FeatureMembership("feature_member", new()
{
Location = "global",
Feature = feature.Name,
Membership = membership.MembershipId,
Configmanagement = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementArgs
{
Version = "1.19.0",
ConfigSync = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementConfigSyncArgs
{
Enabled = true,
Git = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementConfigSyncGitArgs
{
SyncRepo = "https://github.com/hashicorp/terraform",
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.gkehub.Membership;
import com.pulumi.gcp.gkehub.MembershipArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointGkeClusterArgs;
import com.pulumi.gcp.gkehub.Feature;
import com.pulumi.gcp.gkehub.FeatureArgs;
import com.pulumi.gcp.gkehub.FeatureMembership;
import com.pulumi.gcp.gkehub.FeatureMembershipArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipConfigmanagementArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipConfigmanagementConfigSyncArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipConfigmanagementConfigSyncGitArgs;
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 cluster = new Cluster("cluster", ClusterArgs.builder()
.name("my-cluster")
.location("us-central1-a")
.initialNodeCount(1)
.build());
var membership = new Membership("membership", MembershipArgs.builder()
.membershipId("my-membership")
.endpoint(MembershipEndpointArgs.builder()
.gkeCluster(MembershipEndpointGkeClusterArgs.builder()
.resourceLink(cluster.id().applyValue(_id -> String.format("//container.googleapis.com/%s", _id)))
.build())
.build())
.build());
var feature = new Feature("feature", FeatureArgs.builder()
.name("configmanagement")
.location("global")
.labels(Map.of("foo", "bar"))
.build());
var featureMember = new FeatureMembership("featureMember", FeatureMembershipArgs.builder()
.location("global")
.feature(feature.name())
.membership(membership.membershipId())
.configmanagement(FeatureMembershipConfigmanagementArgs.builder()
.version("1.19.0")
.configSync(FeatureMembershipConfigmanagementConfigSyncArgs.builder()
.enabled(true)
.git(FeatureMembershipConfigmanagementConfigSyncGitArgs.builder()
.syncRepo("https://github.com/hashicorp/terraform")
.build())
.build())
.build())
.build());
}
}
resources:
cluster:
type: gcp:container:Cluster
properties:
name: my-cluster
location: us-central1-a
initialNodeCount: 1
membership:
type: gcp:gkehub:Membership
properties:
membershipId: my-membership
endpoint:
gkeCluster:
resourceLink: //container.googleapis.com/${cluster.id}
feature:
type: gcp:gkehub:Feature
properties:
name: configmanagement
location: global
labels:
foo: bar
featureMember:
type: gcp:gkehub:FeatureMembership
name: feature_member
properties:
location: global
feature: ${feature.name}
membership: ${membership.membershipId}
configmanagement:
version: 1.19.0
configSync:
enabled: true
git:
syncRepo: https://github.com/hashicorp/terraform
Config Sync pulls manifests from the specified Git repository and applies them to the cluster. The version property pins Config Sync to a specific release, and syncRepo points to your Git repository URL. The cluster continuously reconciles its state against the repository contents.
Sync cluster configuration from an OCI artifact
Organizations using Artifact Registry or other OCI registries can store configuration as container images, providing versioning and access control through existing registry infrastructure.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-cluster",
location: "us-central1-a",
initialNodeCount: 1,
});
const membership = new gcp.gkehub.Membership("membership", {
membershipId: "my-membership",
endpoint: {
gkeCluster: {
resourceLink: pulumi.interpolate`//container.googleapis.com/${cluster.id}`,
},
},
});
const feature = new gcp.gkehub.Feature("feature", {
name: "configmanagement",
location: "global",
labels: {
foo: "bar",
},
});
const featureMember = new gcp.gkehub.FeatureMembership("feature_member", {
location: "global",
feature: feature.name,
membership: membership.membershipId,
configmanagement: {
version: "1.19.0",
configSync: {
enabled: true,
oci: {
syncRepo: "us-central1-docker.pkg.dev/sample-project/config-repo/config-sync-gke:latest",
policyDir: "config-connector",
syncWaitSecs: "20",
secretType: "gcpserviceaccount",
gcpServiceAccountEmail: "sa@project-id.iam.gserviceaccount.com",
},
},
},
});
import pulumi
import pulumi_gcp as gcp
cluster = gcp.container.Cluster("cluster",
name="my-cluster",
location="us-central1-a",
initial_node_count=1)
membership = gcp.gkehub.Membership("membership",
membership_id="my-membership",
endpoint={
"gke_cluster": {
"resource_link": cluster.id.apply(lambda id: f"//container.googleapis.com/{id}"),
},
})
feature = gcp.gkehub.Feature("feature",
name="configmanagement",
location="global",
labels={
"foo": "bar",
})
feature_member = gcp.gkehub.FeatureMembership("feature_member",
location="global",
feature=feature.name,
membership=membership.membership_id,
configmanagement={
"version": "1.19.0",
"config_sync": {
"enabled": True,
"oci": {
"sync_repo": "us-central1-docker.pkg.dev/sample-project/config-repo/config-sync-gke:latest",
"policy_dir": "config-connector",
"sync_wait_secs": "20",
"secret_type": "gcpserviceaccount",
"gcp_service_account_email": "sa@project-id.iam.gserviceaccount.com",
},
},
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"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 {
cluster, err := container.NewCluster(ctx, "cluster", &container.ClusterArgs{
Name: pulumi.String("my-cluster"),
Location: pulumi.String("us-central1-a"),
InitialNodeCount: pulumi.Int(1),
})
if err != nil {
return err
}
membership, err := gkehub.NewMembership(ctx, "membership", &gkehub.MembershipArgs{
MembershipId: pulumi.String("my-membership"),
Endpoint: &gkehub.MembershipEndpointArgs{
GkeCluster: &gkehub.MembershipEndpointGkeClusterArgs{
ResourceLink: cluster.ID().ApplyT(func(id string) (string, error) {
return fmt.Sprintf("//container.googleapis.com/%v", id), nil
}).(pulumi.StringOutput),
},
},
})
if err != nil {
return err
}
feature, err := gkehub.NewFeature(ctx, "feature", &gkehub.FeatureArgs{
Name: pulumi.String("configmanagement"),
Location: pulumi.String("global"),
Labels: pulumi.StringMap{
"foo": pulumi.String("bar"),
},
})
if err != nil {
return err
}
_, err = gkehub.NewFeatureMembership(ctx, "feature_member", &gkehub.FeatureMembershipArgs{
Location: pulumi.String("global"),
Feature: feature.Name,
Membership: membership.MembershipId,
Configmanagement: &gkehub.FeatureMembershipConfigmanagementArgs{
Version: pulumi.String("1.19.0"),
ConfigSync: &gkehub.FeatureMembershipConfigmanagementConfigSyncArgs{
Enabled: pulumi.Bool(true),
Oci: &gkehub.FeatureMembershipConfigmanagementConfigSyncOciArgs{
SyncRepo: pulumi.String("us-central1-docker.pkg.dev/sample-project/config-repo/config-sync-gke:latest"),
PolicyDir: pulumi.String("config-connector"),
SyncWaitSecs: pulumi.String("20"),
SecretType: pulumi.String("gcpserviceaccount"),
GcpServiceAccountEmail: pulumi.String("sa@project-id.iam.gserviceaccount.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 cluster = new Gcp.Container.Cluster("cluster", new()
{
Name = "my-cluster",
Location = "us-central1-a",
InitialNodeCount = 1,
});
var membership = new Gcp.GkeHub.Membership("membership", new()
{
MembershipId = "my-membership",
Endpoint = new Gcp.GkeHub.Inputs.MembershipEndpointArgs
{
GkeCluster = new Gcp.GkeHub.Inputs.MembershipEndpointGkeClusterArgs
{
ResourceLink = cluster.Id.Apply(id => $"//container.googleapis.com/{id}"),
},
},
});
var feature = new Gcp.GkeHub.Feature("feature", new()
{
Name = "configmanagement",
Location = "global",
Labels =
{
{ "foo", "bar" },
},
});
var featureMember = new Gcp.GkeHub.FeatureMembership("feature_member", new()
{
Location = "global",
Feature = feature.Name,
Membership = membership.MembershipId,
Configmanagement = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementArgs
{
Version = "1.19.0",
ConfigSync = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementConfigSyncArgs
{
Enabled = true,
Oci = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementConfigSyncOciArgs
{
SyncRepo = "us-central1-docker.pkg.dev/sample-project/config-repo/config-sync-gke:latest",
PolicyDir = "config-connector",
SyncWaitSecs = "20",
SecretType = "gcpserviceaccount",
GcpServiceAccountEmail = "sa@project-id.iam.gserviceaccount.com",
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.gkehub.Membership;
import com.pulumi.gcp.gkehub.MembershipArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointGkeClusterArgs;
import com.pulumi.gcp.gkehub.Feature;
import com.pulumi.gcp.gkehub.FeatureArgs;
import com.pulumi.gcp.gkehub.FeatureMembership;
import com.pulumi.gcp.gkehub.FeatureMembershipArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipConfigmanagementArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipConfigmanagementConfigSyncArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipConfigmanagementConfigSyncOciArgs;
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 cluster = new Cluster("cluster", ClusterArgs.builder()
.name("my-cluster")
.location("us-central1-a")
.initialNodeCount(1)
.build());
var membership = new Membership("membership", MembershipArgs.builder()
.membershipId("my-membership")
.endpoint(MembershipEndpointArgs.builder()
.gkeCluster(MembershipEndpointGkeClusterArgs.builder()
.resourceLink(cluster.id().applyValue(_id -> String.format("//container.googleapis.com/%s", _id)))
.build())
.build())
.build());
var feature = new Feature("feature", FeatureArgs.builder()
.name("configmanagement")
.location("global")
.labels(Map.of("foo", "bar"))
.build());
var featureMember = new FeatureMembership("featureMember", FeatureMembershipArgs.builder()
.location("global")
.feature(feature.name())
.membership(membership.membershipId())
.configmanagement(FeatureMembershipConfigmanagementArgs.builder()
.version("1.19.0")
.configSync(FeatureMembershipConfigmanagementConfigSyncArgs.builder()
.enabled(true)
.oci(FeatureMembershipConfigmanagementConfigSyncOciArgs.builder()
.syncRepo("us-central1-docker.pkg.dev/sample-project/config-repo/config-sync-gke:latest")
.policyDir("config-connector")
.syncWaitSecs("20")
.secretType("gcpserviceaccount")
.gcpServiceAccountEmail("sa@project-id.iam.gserviceaccount.com")
.build())
.build())
.build())
.build());
}
}
resources:
cluster:
type: gcp:container:Cluster
properties:
name: my-cluster
location: us-central1-a
initialNodeCount: 1
membership:
type: gcp:gkehub:Membership
properties:
membershipId: my-membership
endpoint:
gkeCluster:
resourceLink: //container.googleapis.com/${cluster.id}
feature:
type: gcp:gkehub:Feature
properties:
name: configmanagement
location: global
labels:
foo: bar
featureMember:
type: gcp:gkehub:FeatureMembership
name: feature_member
properties:
location: global
feature: ${feature.name}
membership: ${membership.membershipId}
configmanagement:
version: 1.19.0
configSync:
enabled: true
oci:
syncRepo: us-central1-docker.pkg.dev/sample-project/config-repo/config-sync-gke:latest
policyDir: config-connector
syncWaitSecs: '20'
secretType: gcpserviceaccount
gcpServiceAccountEmail: sa@project-id.iam.gserviceaccount.com
The oci block configures Config Sync to pull from an OCI registry instead of Git. The syncRepo specifies the full image path, policyDir identifies the directory within the artifact containing manifests, and gcpServiceAccountEmail provides authentication. The secretType of “gcpserviceaccount” uses Workload Identity for secure access.
Enable managed service mesh for traffic control
Service mesh provides traffic management, security, and observability for microservices without requiring application code changes.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-cluster",
location: "us-central1-a",
initialNodeCount: 1,
});
const membership = new gcp.gkehub.Membership("membership", {
membershipId: "my-membership",
endpoint: {
gkeCluster: {
resourceLink: pulumi.interpolate`//container.googleapis.com/${cluster.id}`,
},
},
});
const feature = new gcp.gkehub.Feature("feature", {
name: "servicemesh",
location: "global",
});
const featureMember = new gcp.gkehub.FeatureMembership("feature_member", {
location: "global",
feature: feature.name,
membership: membership.membershipId,
mesh: {
management: "MANAGEMENT_AUTOMATIC",
},
});
import pulumi
import pulumi_gcp as gcp
cluster = gcp.container.Cluster("cluster",
name="my-cluster",
location="us-central1-a",
initial_node_count=1)
membership = gcp.gkehub.Membership("membership",
membership_id="my-membership",
endpoint={
"gke_cluster": {
"resource_link": cluster.id.apply(lambda id: f"//container.googleapis.com/{id}"),
},
})
feature = gcp.gkehub.Feature("feature",
name="servicemesh",
location="global")
feature_member = gcp.gkehub.FeatureMembership("feature_member",
location="global",
feature=feature.name,
membership=membership.membership_id,
mesh={
"management": "MANAGEMENT_AUTOMATIC",
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"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 {
cluster, err := container.NewCluster(ctx, "cluster", &container.ClusterArgs{
Name: pulumi.String("my-cluster"),
Location: pulumi.String("us-central1-a"),
InitialNodeCount: pulumi.Int(1),
})
if err != nil {
return err
}
membership, err := gkehub.NewMembership(ctx, "membership", &gkehub.MembershipArgs{
MembershipId: pulumi.String("my-membership"),
Endpoint: &gkehub.MembershipEndpointArgs{
GkeCluster: &gkehub.MembershipEndpointGkeClusterArgs{
ResourceLink: cluster.ID().ApplyT(func(id string) (string, error) {
return fmt.Sprintf("//container.googleapis.com/%v", id), nil
}).(pulumi.StringOutput),
},
},
})
if err != nil {
return err
}
feature, err := gkehub.NewFeature(ctx, "feature", &gkehub.FeatureArgs{
Name: pulumi.String("servicemesh"),
Location: pulumi.String("global"),
})
if err != nil {
return err
}
_, err = gkehub.NewFeatureMembership(ctx, "feature_member", &gkehub.FeatureMembershipArgs{
Location: pulumi.String("global"),
Feature: feature.Name,
Membership: membership.MembershipId,
Mesh: &gkehub.FeatureMembershipMeshArgs{
Management: pulumi.String("MANAGEMENT_AUTOMATIC"),
},
})
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 cluster = new Gcp.Container.Cluster("cluster", new()
{
Name = "my-cluster",
Location = "us-central1-a",
InitialNodeCount = 1,
});
var membership = new Gcp.GkeHub.Membership("membership", new()
{
MembershipId = "my-membership",
Endpoint = new Gcp.GkeHub.Inputs.MembershipEndpointArgs
{
GkeCluster = new Gcp.GkeHub.Inputs.MembershipEndpointGkeClusterArgs
{
ResourceLink = cluster.Id.Apply(id => $"//container.googleapis.com/{id}"),
},
},
});
var feature = new Gcp.GkeHub.Feature("feature", new()
{
Name = "servicemesh",
Location = "global",
});
var featureMember = new Gcp.GkeHub.FeatureMembership("feature_member", new()
{
Location = "global",
Feature = feature.Name,
Membership = membership.MembershipId,
Mesh = new Gcp.GkeHub.Inputs.FeatureMembershipMeshArgs
{
Management = "MANAGEMENT_AUTOMATIC",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.gkehub.Membership;
import com.pulumi.gcp.gkehub.MembershipArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointGkeClusterArgs;
import com.pulumi.gcp.gkehub.Feature;
import com.pulumi.gcp.gkehub.FeatureArgs;
import com.pulumi.gcp.gkehub.FeatureMembership;
import com.pulumi.gcp.gkehub.FeatureMembershipArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipMeshArgs;
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 cluster = new Cluster("cluster", ClusterArgs.builder()
.name("my-cluster")
.location("us-central1-a")
.initialNodeCount(1)
.build());
var membership = new Membership("membership", MembershipArgs.builder()
.membershipId("my-membership")
.endpoint(MembershipEndpointArgs.builder()
.gkeCluster(MembershipEndpointGkeClusterArgs.builder()
.resourceLink(cluster.id().applyValue(_id -> String.format("//container.googleapis.com/%s", _id)))
.build())
.build())
.build());
var feature = new Feature("feature", FeatureArgs.builder()
.name("servicemesh")
.location("global")
.build());
var featureMember = new FeatureMembership("featureMember", FeatureMembershipArgs.builder()
.location("global")
.feature(feature.name())
.membership(membership.membershipId())
.mesh(FeatureMembershipMeshArgs.builder()
.management("MANAGEMENT_AUTOMATIC")
.build())
.build());
}
}
resources:
cluster:
type: gcp:container:Cluster
properties:
name: my-cluster
location: us-central1-a
initialNodeCount: 1
membership:
type: gcp:gkehub:Membership
properties:
membershipId: my-membership
endpoint:
gkeCluster:
resourceLink: //container.googleapis.com/${cluster.id}
feature:
type: gcp:gkehub:Feature
properties:
name: servicemesh
location: global
featureMember:
type: gcp:gkehub:FeatureMembership
name: feature_member
properties:
location: global
feature: ${feature.name}
membership: ${membership.membershipId}
mesh:
management: MANAGEMENT_AUTOMATIC
The mesh block enables Anthos Service Mesh on the cluster. Setting management to MANAGEMENT_AUTOMATIC lets Google handle mesh version upgrades and component lifecycle, similar to Config Sync auto-upgrades.
Enable policy enforcement with default settings
Policy Controller enforces organizational policies across clusters, validating resources against constraints before they’re admitted to the cluster.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-cluster",
location: "us-central1-a",
initialNodeCount: 1,
});
const membership = new gcp.gkehub.Membership("membership", {
membershipId: "my-membership",
endpoint: {
gkeCluster: {
resourceLink: pulumi.interpolate`//container.googleapis.com/${cluster.id}`,
},
},
});
const feature = new gcp.gkehub.Feature("feature", {
name: "policycontroller",
location: "global",
});
const featureMember = new gcp.gkehub.FeatureMembership("feature_member", {
location: "global",
feature: feature.name,
membership: membership.membershipId,
policycontroller: {
policyControllerHubConfig: {
installSpec: "INSTALL_SPEC_ENABLED",
},
},
});
import pulumi
import pulumi_gcp as gcp
cluster = gcp.container.Cluster("cluster",
name="my-cluster",
location="us-central1-a",
initial_node_count=1)
membership = gcp.gkehub.Membership("membership",
membership_id="my-membership",
endpoint={
"gke_cluster": {
"resource_link": cluster.id.apply(lambda id: f"//container.googleapis.com/{id}"),
},
})
feature = gcp.gkehub.Feature("feature",
name="policycontroller",
location="global")
feature_member = gcp.gkehub.FeatureMembership("feature_member",
location="global",
feature=feature.name,
membership=membership.membership_id,
policycontroller={
"policy_controller_hub_config": {
"install_spec": "INSTALL_SPEC_ENABLED",
},
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"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 {
cluster, err := container.NewCluster(ctx, "cluster", &container.ClusterArgs{
Name: pulumi.String("my-cluster"),
Location: pulumi.String("us-central1-a"),
InitialNodeCount: pulumi.Int(1),
})
if err != nil {
return err
}
membership, err := gkehub.NewMembership(ctx, "membership", &gkehub.MembershipArgs{
MembershipId: pulumi.String("my-membership"),
Endpoint: &gkehub.MembershipEndpointArgs{
GkeCluster: &gkehub.MembershipEndpointGkeClusterArgs{
ResourceLink: cluster.ID().ApplyT(func(id string) (string, error) {
return fmt.Sprintf("//container.googleapis.com/%v", id), nil
}).(pulumi.StringOutput),
},
},
})
if err != nil {
return err
}
feature, err := gkehub.NewFeature(ctx, "feature", &gkehub.FeatureArgs{
Name: pulumi.String("policycontroller"),
Location: pulumi.String("global"),
})
if err != nil {
return err
}
_, err = gkehub.NewFeatureMembership(ctx, "feature_member", &gkehub.FeatureMembershipArgs{
Location: pulumi.String("global"),
Feature: feature.Name,
Membership: membership.MembershipId,
Policycontroller: &gkehub.FeatureMembershipPolicycontrollerArgs{
PolicyControllerHubConfig: &gkehub.FeatureMembershipPolicycontrollerPolicyControllerHubConfigArgs{
InstallSpec: pulumi.String("INSTALL_SPEC_ENABLED"),
},
},
})
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 cluster = new Gcp.Container.Cluster("cluster", new()
{
Name = "my-cluster",
Location = "us-central1-a",
InitialNodeCount = 1,
});
var membership = new Gcp.GkeHub.Membership("membership", new()
{
MembershipId = "my-membership",
Endpoint = new Gcp.GkeHub.Inputs.MembershipEndpointArgs
{
GkeCluster = new Gcp.GkeHub.Inputs.MembershipEndpointGkeClusterArgs
{
ResourceLink = cluster.Id.Apply(id => $"//container.googleapis.com/{id}"),
},
},
});
var feature = new Gcp.GkeHub.Feature("feature", new()
{
Name = "policycontroller",
Location = "global",
});
var featureMember = new Gcp.GkeHub.FeatureMembership("feature_member", new()
{
Location = "global",
Feature = feature.Name,
Membership = membership.MembershipId,
Policycontroller = new Gcp.GkeHub.Inputs.FeatureMembershipPolicycontrollerArgs
{
PolicyControllerHubConfig = new Gcp.GkeHub.Inputs.FeatureMembershipPolicycontrollerPolicyControllerHubConfigArgs
{
InstallSpec = "INSTALL_SPEC_ENABLED",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.gkehub.Membership;
import com.pulumi.gcp.gkehub.MembershipArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointGkeClusterArgs;
import com.pulumi.gcp.gkehub.Feature;
import com.pulumi.gcp.gkehub.FeatureArgs;
import com.pulumi.gcp.gkehub.FeatureMembership;
import com.pulumi.gcp.gkehub.FeatureMembershipArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipPolicycontrollerArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipPolicycontrollerPolicyControllerHubConfigArgs;
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 cluster = new Cluster("cluster", ClusterArgs.builder()
.name("my-cluster")
.location("us-central1-a")
.initialNodeCount(1)
.build());
var membership = new Membership("membership", MembershipArgs.builder()
.membershipId("my-membership")
.endpoint(MembershipEndpointArgs.builder()
.gkeCluster(MembershipEndpointGkeClusterArgs.builder()
.resourceLink(cluster.id().applyValue(_id -> String.format("//container.googleapis.com/%s", _id)))
.build())
.build())
.build());
var feature = new Feature("feature", FeatureArgs.builder()
.name("policycontroller")
.location("global")
.build());
var featureMember = new FeatureMembership("featureMember", FeatureMembershipArgs.builder()
.location("global")
.feature(feature.name())
.membership(membership.membershipId())
.policycontroller(FeatureMembershipPolicycontrollerArgs.builder()
.policyControllerHubConfig(FeatureMembershipPolicycontrollerPolicyControllerHubConfigArgs.builder()
.installSpec("INSTALL_SPEC_ENABLED")
.build())
.build())
.build());
}
}
resources:
cluster:
type: gcp:container:Cluster
properties:
name: my-cluster
location: us-central1-a
initialNodeCount: 1
membership:
type: gcp:gkehub:Membership
properties:
membershipId: my-membership
endpoint:
gkeCluster:
resourceLink: //container.googleapis.com/${cluster.id}
feature:
type: gcp:gkehub:Feature
properties:
name: policycontroller
location: global
featureMember:
type: gcp:gkehub:FeatureMembership
name: feature_member
properties:
location: global
feature: ${feature.name}
membership: ${membership.membershipId}
policycontroller:
policyControllerHubConfig:
installSpec: INSTALL_SPEC_ENABLED
The policyControllerHubConfig block configures Policy Controller behavior. Setting installSpec to INSTALL_SPEC_ENABLED activates the controller with default settings, which include standard audit intervals and violation limits.
Configure policy enforcement with custom settings
Production deployments often need fine-grained control over policy behavior, including audit frequency, violation limits, and mutation capabilities.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const cluster = new gcp.container.Cluster("cluster", {
name: "my-cluster",
location: "us-central1-a",
initialNodeCount: 1,
});
const membership = new gcp.gkehub.Membership("membership", {
membershipId: "my-membership",
endpoint: {
gkeCluster: {
resourceLink: pulumi.interpolate`//container.googleapis.com/${cluster.id}`,
},
},
});
const feature = new gcp.gkehub.Feature("feature", {
name: "policycontroller",
location: "global",
});
const featureMember = new gcp.gkehub.FeatureMembership("feature_member", {
location: "global",
feature: feature.name,
membership: membership.membershipId,
policycontroller: {
policyControllerHubConfig: {
installSpec: "INSTALL_SPEC_SUSPENDED",
policyContent: {
templateLibrary: {
installation: "NOT_INSTALLED",
},
},
constraintViolationLimit: 50,
auditIntervalSeconds: 120,
referentialRulesEnabled: true,
logDeniesEnabled: true,
mutationEnabled: true,
},
version: "1.17.0",
},
});
import pulumi
import pulumi_gcp as gcp
cluster = gcp.container.Cluster("cluster",
name="my-cluster",
location="us-central1-a",
initial_node_count=1)
membership = gcp.gkehub.Membership("membership",
membership_id="my-membership",
endpoint={
"gke_cluster": {
"resource_link": cluster.id.apply(lambda id: f"//container.googleapis.com/{id}"),
},
})
feature = gcp.gkehub.Feature("feature",
name="policycontroller",
location="global")
feature_member = gcp.gkehub.FeatureMembership("feature_member",
location="global",
feature=feature.name,
membership=membership.membership_id,
policycontroller={
"policy_controller_hub_config": {
"install_spec": "INSTALL_SPEC_SUSPENDED",
"policy_content": {
"template_library": {
"installation": "NOT_INSTALLED",
},
},
"constraint_violation_limit": 50,
"audit_interval_seconds": 120,
"referential_rules_enabled": True,
"log_denies_enabled": True,
"mutation_enabled": True,
},
"version": "1.17.0",
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"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 {
cluster, err := container.NewCluster(ctx, "cluster", &container.ClusterArgs{
Name: pulumi.String("my-cluster"),
Location: pulumi.String("us-central1-a"),
InitialNodeCount: pulumi.Int(1),
})
if err != nil {
return err
}
membership, err := gkehub.NewMembership(ctx, "membership", &gkehub.MembershipArgs{
MembershipId: pulumi.String("my-membership"),
Endpoint: &gkehub.MembershipEndpointArgs{
GkeCluster: &gkehub.MembershipEndpointGkeClusterArgs{
ResourceLink: cluster.ID().ApplyT(func(id string) (string, error) {
return fmt.Sprintf("//container.googleapis.com/%v", id), nil
}).(pulumi.StringOutput),
},
},
})
if err != nil {
return err
}
feature, err := gkehub.NewFeature(ctx, "feature", &gkehub.FeatureArgs{
Name: pulumi.String("policycontroller"),
Location: pulumi.String("global"),
})
if err != nil {
return err
}
_, err = gkehub.NewFeatureMembership(ctx, "feature_member", &gkehub.FeatureMembershipArgs{
Location: pulumi.String("global"),
Feature: feature.Name,
Membership: membership.MembershipId,
Policycontroller: &gkehub.FeatureMembershipPolicycontrollerArgs{
PolicyControllerHubConfig: &gkehub.FeatureMembershipPolicycontrollerPolicyControllerHubConfigArgs{
InstallSpec: pulumi.String("INSTALL_SPEC_SUSPENDED"),
PolicyContent: &gkehub.FeatureMembershipPolicycontrollerPolicyControllerHubConfigPolicyContentArgs{
TemplateLibrary: &gkehub.FeatureMembershipPolicycontrollerPolicyControllerHubConfigPolicyContentTemplateLibraryArgs{
Installation: pulumi.String("NOT_INSTALLED"),
},
},
ConstraintViolationLimit: pulumi.Int(50),
AuditIntervalSeconds: pulumi.Int(120),
ReferentialRulesEnabled: pulumi.Bool(true),
LogDeniesEnabled: pulumi.Bool(true),
MutationEnabled: pulumi.Bool(true),
},
Version: pulumi.String("1.17.0"),
},
})
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 cluster = new Gcp.Container.Cluster("cluster", new()
{
Name = "my-cluster",
Location = "us-central1-a",
InitialNodeCount = 1,
});
var membership = new Gcp.GkeHub.Membership("membership", new()
{
MembershipId = "my-membership",
Endpoint = new Gcp.GkeHub.Inputs.MembershipEndpointArgs
{
GkeCluster = new Gcp.GkeHub.Inputs.MembershipEndpointGkeClusterArgs
{
ResourceLink = cluster.Id.Apply(id => $"//container.googleapis.com/{id}"),
},
},
});
var feature = new Gcp.GkeHub.Feature("feature", new()
{
Name = "policycontroller",
Location = "global",
});
var featureMember = new Gcp.GkeHub.FeatureMembership("feature_member", new()
{
Location = "global",
Feature = feature.Name,
Membership = membership.MembershipId,
Policycontroller = new Gcp.GkeHub.Inputs.FeatureMembershipPolicycontrollerArgs
{
PolicyControllerHubConfig = new Gcp.GkeHub.Inputs.FeatureMembershipPolicycontrollerPolicyControllerHubConfigArgs
{
InstallSpec = "INSTALL_SPEC_SUSPENDED",
PolicyContent = new Gcp.GkeHub.Inputs.FeatureMembershipPolicycontrollerPolicyControllerHubConfigPolicyContentArgs
{
TemplateLibrary = new Gcp.GkeHub.Inputs.FeatureMembershipPolicycontrollerPolicyControllerHubConfigPolicyContentTemplateLibraryArgs
{
Installation = "NOT_INSTALLED",
},
},
ConstraintViolationLimit = 50,
AuditIntervalSeconds = 120,
ReferentialRulesEnabled = true,
LogDeniesEnabled = true,
MutationEnabled = true,
},
Version = "1.17.0",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.container.Cluster;
import com.pulumi.gcp.container.ClusterArgs;
import com.pulumi.gcp.gkehub.Membership;
import com.pulumi.gcp.gkehub.MembershipArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointArgs;
import com.pulumi.gcp.gkehub.inputs.MembershipEndpointGkeClusterArgs;
import com.pulumi.gcp.gkehub.Feature;
import com.pulumi.gcp.gkehub.FeatureArgs;
import com.pulumi.gcp.gkehub.FeatureMembership;
import com.pulumi.gcp.gkehub.FeatureMembershipArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipPolicycontrollerArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipPolicycontrollerPolicyControllerHubConfigArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipPolicycontrollerPolicyControllerHubConfigPolicyContentArgs;
import com.pulumi.gcp.gkehub.inputs.FeatureMembershipPolicycontrollerPolicyControllerHubConfigPolicyContentTemplateLibraryArgs;
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 cluster = new Cluster("cluster", ClusterArgs.builder()
.name("my-cluster")
.location("us-central1-a")
.initialNodeCount(1)
.build());
var membership = new Membership("membership", MembershipArgs.builder()
.membershipId("my-membership")
.endpoint(MembershipEndpointArgs.builder()
.gkeCluster(MembershipEndpointGkeClusterArgs.builder()
.resourceLink(cluster.id().applyValue(_id -> String.format("//container.googleapis.com/%s", _id)))
.build())
.build())
.build());
var feature = new Feature("feature", FeatureArgs.builder()
.name("policycontroller")
.location("global")
.build());
var featureMember = new FeatureMembership("featureMember", FeatureMembershipArgs.builder()
.location("global")
.feature(feature.name())
.membership(membership.membershipId())
.policycontroller(FeatureMembershipPolicycontrollerArgs.builder()
.policyControllerHubConfig(FeatureMembershipPolicycontrollerPolicyControllerHubConfigArgs.builder()
.installSpec("INSTALL_SPEC_SUSPENDED")
.policyContent(FeatureMembershipPolicycontrollerPolicyControllerHubConfigPolicyContentArgs.builder()
.templateLibrary(FeatureMembershipPolicycontrollerPolicyControllerHubConfigPolicyContentTemplateLibraryArgs.builder()
.installation("NOT_INSTALLED")
.build())
.build())
.constraintViolationLimit(50)
.auditIntervalSeconds(120)
.referentialRulesEnabled(true)
.logDeniesEnabled(true)
.mutationEnabled(true)
.build())
.version("1.17.0")
.build())
.build());
}
}
resources:
cluster:
type: gcp:container:Cluster
properties:
name: my-cluster
location: us-central1-a
initialNodeCount: 1
membership:
type: gcp:gkehub:Membership
properties:
membershipId: my-membership
endpoint:
gkeCluster:
resourceLink: //container.googleapis.com/${cluster.id}
feature:
type: gcp:gkehub:Feature
properties:
name: policycontroller
location: global
featureMember:
type: gcp:gkehub:FeatureMembership
name: feature_member
properties:
location: global
feature: ${feature.name}
membership: ${membership.membershipId}
policycontroller:
policyControllerHubConfig:
installSpec: INSTALL_SPEC_SUSPENDED
policyContent:
templateLibrary:
installation: NOT_INSTALLED
constraintViolationLimit: 50
auditIntervalSeconds: 120
referentialRulesEnabled: true
logDeniesEnabled: true
mutationEnabled: true
version: 1.17.0
This configuration extends basic Policy Controller setup with custom tuning. The auditIntervalSeconds controls how often the controller scans existing resources, constraintViolationLimit caps the number of violations reported per constraint, and mutationEnabled allows the controller to modify resources to meet policy requirements. Setting installSpec to INSTALL_SPEC_SUSPENDED disables enforcement while keeping the controller installed for testing.
Beyond these examples
These snippets focus on specific feature membership capabilities: Config Sync with Git and OCI sources, service mesh management, and Policy Controller enforcement. They’re intentionally minimal rather than full Fleet configurations.
The examples require pre-existing infrastructure such as GKE clusters, Fleet memberships, Fleet features, and Git repositories or OCI registries for Config Sync. They focus on binding features to memberships rather than provisioning the underlying infrastructure.
To keep things focused, common feature membership patterns are omitted, including:
- Deployment resource overrides (CPU/memory limits)
- Regional membership configuration
- Template library installation options
- Multi-cluster service discovery setup
These omissions are intentional: the goal is to illustrate how each feature is wired to a membership, not provide drop-in Fleet modules. See the GKE Hub FeatureMembership resource reference for all available configuration options.
Let's configure GCP GKE Hub Feature Memberships
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Resource Configuration & Immutability
feature, location, membership, project, and membershipLocation are all immutable. Changing any of these requires destroying and recreating the resource.membershipLocation to match your membership’s location. For example, if your membership is in us-central1, set membershipLocation: "us-central1". The default is global.Config Management & Sync
management: "MANAGEMENT_AUTOMATIC" for auto-upgrades, where Google manages Config Sync versions. For manual control, omit management and specify a version like 1.19.0. Don’t specify both.git.syncRepo with a Git repository URL. OCI sync uses oci.syncRepo with a container registry path and requires additional configuration like policyDir, syncWaitSecs, secretType, and gcpServiceAccountEmail.Feature Types & Configuration
configmanagement (Config Sync), mesh (Service Mesh), policycontroller (Policy Controller), and multiclusterservicediscovery. Each feature has its own configuration block.policycontroller.policyControllerHubConfig.installSpec to INSTALL_SPEC_ENABLED. This enables Policy Controller with default settings.Using a different cloud?
Explore containers guides for other cloud providers: