The gcp:gkehub/featureMembership:FeatureMembership resource, part of the Pulumi GCP provider, configures GKEHub features for specific cluster memberships. This guide focuses on three capabilities: Config Management with Git and OCI sources, Service Mesh automatic management, and Policy Controller enforcement and tuning.
Feature memberships bind GKE clusters to GKEHub features. You must create the cluster, register it as a membership, and enable the feature before configuring the feature membership. The examples are intentionally small. Combine them with your own cluster infrastructure and feature definitions.
Sync cluster configuration from a Git repository
Teams managing Kubernetes configuration as code store manifests in Git repositories. Config Management syncs these manifests to clusters 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: {
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
When Config Sync is enabled, the controller pulls from syncRepo at regular intervals and applies manifests to the cluster. The version property controls which Config Management release to install. The git block specifies the repository URL; Config Sync handles authentication and sync scheduling.
Sync configuration from an OCI artifact registry
Organizations using container registries can store Kubernetes configuration as OCI images alongside application containers.
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 replaces git for registry-based sync. The syncRepo points to an OCI artifact URL, and policyDir specifies which directory within the artifact contains manifests. The secretType and gcpServiceAccountEmail properties configure authentication to the registry. The syncWaitSecs property controls how long Config Sync waits between sync attempts.
Tune Config Sync resource limits and requests
Large clusters or high-frequency sync operations may need custom resource allocation for Config Sync components.
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.20.1",
configSync: {
enabled: true,
deploymentOverrides: [{
deploymentName: "reconciler-manager",
deploymentNamespace: "config-management-system",
containers: [{
containerName: "reconciler-manager",
cpuRequest: "100m",
memoryRequest: "64Mi",
cpuLimit: "250m",
memoryLimit: "128Mi",
}],
}],
},
},
});
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.20.1",
"config_sync": {
"enabled": True,
"deployment_overrides": [{
"deployment_name": "reconciler-manager",
"deployment_namespace": "config-management-system",
"containers": [{
"container_name": "reconciler-manager",
"cpu_request": "100m",
"memory_request": "64Mi",
"cpu_limit": "250m",
"memory_limit": "128Mi",
}],
}],
},
})
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.20.1"),
ConfigSync: &gkehub.FeatureMembershipConfigmanagementConfigSyncArgs{
Enabled: pulumi.Bool(true),
DeploymentOverrides: gkehub.FeatureMembershipConfigmanagementConfigSyncDeploymentOverrideArray{
&gkehub.FeatureMembershipConfigmanagementConfigSyncDeploymentOverrideArgs{
DeploymentName: pulumi.String("reconciler-manager"),
DeploymentNamespace: pulumi.String("config-management-system"),
Containers: gkehub.FeatureMembershipConfigmanagementConfigSyncDeploymentOverrideContainerArray{
&gkehub.FeatureMembershipConfigmanagementConfigSyncDeploymentOverrideContainerArgs{
ContainerName: pulumi.String("reconciler-manager"),
CpuRequest: pulumi.String("100m"),
MemoryRequest: pulumi.String("64Mi"),
CpuLimit: pulumi.String("250m"),
MemoryLimit: pulumi.String("128Mi"),
},
},
},
},
},
},
})
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.20.1",
ConfigSync = new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementConfigSyncArgs
{
Enabled = true,
DeploymentOverrides = new[]
{
new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementConfigSyncDeploymentOverrideArgs
{
DeploymentName = "reconciler-manager",
DeploymentNamespace = "config-management-system",
Containers = new[]
{
new Gcp.GkeHub.Inputs.FeatureMembershipConfigmanagementConfigSyncDeploymentOverrideContainerArgs
{
ContainerName = "reconciler-manager",
CpuRequest = "100m",
MemoryRequest = "64Mi",
CpuLimit = "250m",
MemoryLimit = "128Mi",
},
},
},
},
},
},
});
});
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()
.version("1.20.1")
.configSync(FeatureMembershipConfigmanagementConfigSyncArgs.builder()
.enabled(true)
.deploymentOverrides(FeatureMembershipConfigmanagementConfigSyncDeploymentOverrideArgs.builder()
.deploymentName("reconciler-manager")
.deploymentNamespace("config-management-system")
.containers(FeatureMembershipConfigmanagementConfigSyncDeploymentOverrideContainerArgs.builder()
.containerName("reconciler-manager")
.cpuRequest("100m")
.memoryRequest("64Mi")
.cpuLimit("250m")
.memoryLimit("128Mi")
.build())
.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.20.1
configSync:
enabled: true
deploymentOverrides:
- deploymentName: reconciler-manager
deploymentNamespace: config-management-system
containers:
- containerName: reconciler-manager
cpuRequest: 100m
memoryRequest: 64Mi
cpuLimit: 250m
memoryLimit: 128Mi
The deploymentOverrides array targets specific Config Sync deployments by name and namespace. Each override specifies containers with custom CPU and memory settings. This prevents throttling or out-of-memory conditions in clusters with many resources or frequent updates.
Enable managed service mesh for a cluster
Applications requiring service-to-service encryption or traffic management can use GKE’s managed service mesh without manual Istio installation.
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 service mesh features for the membership. Setting management to “MANAGEMENT_AUTOMATIC” delegates control plane installation and upgrades to Google, eliminating manual Istio lifecycle management.
Enable policy enforcement with default settings
Teams adopting policy-as-code start by enabling Policy Controller with default constraints.
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 controls Policy Controller installation. Setting installSpec to “INSTALL_SPEC_ENABLED” deploys the admission webhook with default settings. Policy Controller validates resource configurations against constraints at admission time.
Customize policy enforcement behavior and limits
Production environments often need fine-tuned policy settings: custom audit intervals, violation limits, or 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
The installSpec “INSTALL_SPEC_SUSPENDED” disables admission enforcement while keeping audit mode active. The policyContent block controls template library installation. The constraintViolationLimit caps how many violations are reported per constraint. The auditIntervalSeconds property controls how often Policy Controller scans existing resources. Setting mutationEnabled to true allows Policy Controller to automatically remediate violations.
Beyond these examples
These snippets focus on specific feature membership capabilities: Config Management (Git, OCI, resource tuning), Service Mesh with automatic management, and Policy Controller enforcement and customization. They’re intentionally minimal rather than full fleet management solutions.
The examples require pre-existing infrastructure such as GKE clusters, GKEHub memberships, GKEHub features (configmanagement, servicemesh, policycontroller), and OCI registries and service accounts for OCI sync. They focus on feature membership configuration rather than provisioning the underlying clusters and features.
To keep things focused, common feature membership patterns are omitted, including:
- Regional membership configuration (membershipLocation)
- Multi Cluster Service Discovery setup
- Policy template library installation options
- Config Sync source-of-truth selection (git vs oci)
These omissions are intentional: the goal is to illustrate how each feature membership capability is wired, not provide drop-in fleet management modules. See the GKEHub 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
Immutability & Limitations
feature, location, membership, project, and membershipLocation properties are immutable. Changing any of these requires recreating the resource.Configuration & Setup
configmanagement (Config Management), mesh (Service Mesh), and policycontroller (Policy Controller) features for specific memberships.membershipLocation defaults to global if not specified.Use one of these formats:
projects/{{project}}/locations/{{location}}/features/{{feature}}/membershipId/{{membership}}{{project}}/{{location}}/{{feature}}/{{membership}}{{location}}/{{feature}}/{{membership}}
Config Management
configSync.git.syncRepo with a Git repository URL, while OCI sync uses configSync.oci.syncRepo pointing to a container image in a registry like Artifact Registry.membershipLocation to match your membership’s region (e.g., us-central1) instead of using the default global location.configSync.deploymentOverrides to specify cpuRequest, memoryRequest, cpuLimit, and memoryLimit for specific containers like reconciler-manager.Policy Controller
policycontroller.policyControllerHubConfig.installSpec to INSTALL_SPEC_ENABLED.installSpec, policyContent.templateLibrary, constraintViolationLimit, auditIntervalSeconds, referentialRulesEnabled, logDeniesEnabled, mutationEnabled, and version.Using a different cloud?
Explore containers guides for other cloud providers: