The gcp:container/attachedCluster:AttachedCluster resource, part of the Pulumi GCP provider, registers external Kubernetes clusters with Google Cloud for Anthos Fleet management. This guide focuses on three capabilities: basic cluster registration with OIDC authentication, RBAC and observability configuration, and deletion policy handling.
Attached clusters connect existing Kubernetes infrastructure (EKS, AKS, or generic CNCF-conformant clusters) to Google Cloud. The examples are intentionally small. Combine them with your own cluster infrastructure, OIDC providers, and Fleet projects.
Register an external cluster with minimal configuration
Teams running Kubernetes on AWS EKS, Azure AKS, or other platforms often need to connect those clusters to Google Cloud for unified management through Anthos Fleet.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const project = gcp.organizations.getProject({});
const versions = project.then(project => gcp.container.getAttachedVersions({
location: "us-west1",
project: project.projectId,
}));
const primary = new gcp.container.AttachedCluster("primary", {
name: "basic",
location: "us-west1",
project: project.then(project => project.projectId),
description: "Test cluster",
distribution: "aks",
oidcConfig: {
issuerUrl: "https://oidc.issuer.url",
},
platformVersion: versions.then(versions => versions.validVersions?.[0]),
fleet: {
project: project.then(project => `projects/${project.number}`),
},
});
import pulumi
import pulumi_gcp as gcp
project = gcp.organizations.get_project()
versions = gcp.container.get_attached_versions(location="us-west1",
project=project.project_id)
primary = gcp.container.AttachedCluster("primary",
name="basic",
location="us-west1",
project=project.project_id,
description="Test cluster",
distribution="aks",
oidc_config={
"issuer_url": "https://oidc.issuer.url",
},
platform_version=versions.valid_versions[0],
fleet={
"project": f"projects/{project.number}",
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
versions, err := container.GetAttachedVersions(ctx, &container.GetAttachedVersionsArgs{
Location: "us-west1",
Project: project.ProjectId,
}, nil)
if err != nil {
return err
}
_, err = container.NewAttachedCluster(ctx, "primary", &container.AttachedClusterArgs{
Name: pulumi.String("basic"),
Location: pulumi.String("us-west1"),
Project: pulumi.String(project.ProjectId),
Description: pulumi.String("Test cluster"),
Distribution: pulumi.String("aks"),
OidcConfig: &container.AttachedClusterOidcConfigArgs{
IssuerUrl: pulumi.String("https://oidc.issuer.url"),
},
PlatformVersion: pulumi.String(versions.ValidVersions[0]),
Fleet: &container.AttachedClusterFleetArgs{
Project: pulumi.Sprintf("projects/%v", project.Number),
},
})
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 project = Gcp.Organizations.GetProject.Invoke();
var versions = Gcp.Container.GetAttachedVersions.Invoke(new()
{
Location = "us-west1",
Project = project.Apply(getProjectResult => getProjectResult.ProjectId),
});
var primary = new Gcp.Container.AttachedCluster("primary", new()
{
Name = "basic",
Location = "us-west1",
Project = project.Apply(getProjectResult => getProjectResult.ProjectId),
Description = "Test cluster",
Distribution = "aks",
OidcConfig = new Gcp.Container.Inputs.AttachedClusterOidcConfigArgs
{
IssuerUrl = "https://oidc.issuer.url",
},
PlatformVersion = versions.Apply(getAttachedVersionsResult => getAttachedVersionsResult.ValidVersions[0]),
Fleet = new Gcp.Container.Inputs.AttachedClusterFleetArgs
{
Project = $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}",
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.container.ContainerFunctions;
import com.pulumi.gcp.container.inputs.GetAttachedVersionsArgs;
import com.pulumi.gcp.container.AttachedCluster;
import com.pulumi.gcp.container.AttachedClusterArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterOidcConfigArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterFleetArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
final var versions = ContainerFunctions.getAttachedVersions(GetAttachedVersionsArgs.builder()
.location("us-west1")
.project(project.projectId())
.build());
var primary = new AttachedCluster("primary", AttachedClusterArgs.builder()
.name("basic")
.location("us-west1")
.project(project.projectId())
.description("Test cluster")
.distribution("aks")
.oidcConfig(AttachedClusterOidcConfigArgs.builder()
.issuerUrl("https://oidc.issuer.url")
.build())
.platformVersion(versions.validVersions()[0])
.fleet(AttachedClusterFleetArgs.builder()
.project(String.format("projects/%s", project.number()))
.build())
.build());
}
}
resources:
primary:
type: gcp:container:AttachedCluster
properties:
name: basic
location: us-west1
project: ${project.projectId}
description: Test cluster
distribution: aks
oidcConfig:
issuerUrl: https://oidc.issuer.url
platformVersion: ${versions.validVersions[0]}
fleet:
project: projects/${project.number}
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
versions:
fn::invoke:
function: gcp:container:getAttachedVersions
arguments:
location: us-west1
project: ${project.projectId}
The distribution property identifies the cluster type (eks, aks, or generic for CNCF-conformant clusters). The oidcConfig provides the issuer URL that Google Cloud uses to validate Kubernetes Service Account tokens. The platformVersion specifies the Anthos version, and fleet connects the cluster to a Google Cloud project for centralized management.
Configure RBAC, logging, monitoring, and security policies
Production clusters typically require access controls, observability, and security enforcement beyond basic registration.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";
const project = gcp.organizations.getProject({});
const versions = project.then(project => gcp.container.getAttachedVersions({
location: "us-west1",
project: project.projectId,
}));
const primary = new gcp.container.AttachedCluster("primary", {
name: "basic",
project: project.then(project => project.projectId),
location: "us-west1",
description: "Test cluster",
distribution: "aks",
annotations: {
"label-one": "value-one",
},
authorization: {
adminUsers: [
"user1@example.com",
"user2@example.com",
],
adminGroups: [
"group1@example.com",
"group2@example.com",
],
},
oidcConfig: {
issuerUrl: "https://oidc.issuer.url",
jwks: std.base64encode({
input: "{\"keys\":[{\"use\":\"sig\",\"kty\":\"RSA\",\"kid\":\"testid\",\"alg\":\"RS256\",\"n\":\"somedata\",\"e\":\"AQAB\"}]}",
}).then(invoke => invoke.result),
},
platformVersion: versions.then(versions => versions.validVersions?.[0]),
fleet: {
project: project.then(project => `projects/${project.number}`),
},
loggingConfig: {
componentConfig: {
enableComponents: [
"SYSTEM_COMPONENTS",
"WORKLOADS",
],
},
},
monitoringConfig: {
managedPrometheusConfig: {
enabled: true,
},
},
binaryAuthorization: {
evaluationMode: "PROJECT_SINGLETON_POLICY_ENFORCE",
},
proxyConfig: {
kubernetesSecret: {
name: "proxy-config",
namespace: "default",
},
},
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std
project = gcp.organizations.get_project()
versions = gcp.container.get_attached_versions(location="us-west1",
project=project.project_id)
primary = gcp.container.AttachedCluster("primary",
name="basic",
project=project.project_id,
location="us-west1",
description="Test cluster",
distribution="aks",
annotations={
"label-one": "value-one",
},
authorization={
"admin_users": [
"user1@example.com",
"user2@example.com",
],
"admin_groups": [
"group1@example.com",
"group2@example.com",
],
},
oidc_config={
"issuer_url": "https://oidc.issuer.url",
"jwks": std.base64encode(input="{\"keys\":[{\"use\":\"sig\",\"kty\":\"RSA\",\"kid\":\"testid\",\"alg\":\"RS256\",\"n\":\"somedata\",\"e\":\"AQAB\"}]}").result,
},
platform_version=versions.valid_versions[0],
fleet={
"project": f"projects/{project.number}",
},
logging_config={
"component_config": {
"enable_components": [
"SYSTEM_COMPONENTS",
"WORKLOADS",
],
},
},
monitoring_config={
"managed_prometheus_config": {
"enabled": True,
},
},
binary_authorization={
"evaluation_mode": "PROJECT_SINGLETON_POLICY_ENFORCE",
},
proxy_config={
"kubernetes_secret": {
"name": "proxy-config",
"namespace": "default",
},
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi-std/sdk/go/std"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
versions, err := container.GetAttachedVersions(ctx, &container.GetAttachedVersionsArgs{
Location: "us-west1",
Project: project.ProjectId,
}, nil)
if err != nil {
return err
}
invokeBase64encode, err := std.Base64encode(ctx, &std.Base64encodeArgs{
Input: "{\"keys\":[{\"use\":\"sig\",\"kty\":\"RSA\",\"kid\":\"testid\",\"alg\":\"RS256\",\"n\":\"somedata\",\"e\":\"AQAB\"}]}",
}, nil)
if err != nil {
return err
}
_, err = container.NewAttachedCluster(ctx, "primary", &container.AttachedClusterArgs{
Name: pulumi.String("basic"),
Project: pulumi.String(project.ProjectId),
Location: pulumi.String("us-west1"),
Description: pulumi.String("Test cluster"),
Distribution: pulumi.String("aks"),
Annotations: pulumi.StringMap{
"label-one": pulumi.String("value-one"),
},
Authorization: &container.AttachedClusterAuthorizationArgs{
AdminUsers: pulumi.StringArray{
pulumi.String("user1@example.com"),
pulumi.String("user2@example.com"),
},
AdminGroups: pulumi.StringArray{
pulumi.String("group1@example.com"),
pulumi.String("group2@example.com"),
},
},
OidcConfig: &container.AttachedClusterOidcConfigArgs{
IssuerUrl: pulumi.String("https://oidc.issuer.url"),
Jwks: pulumi.String(invokeBase64encode.Result),
},
PlatformVersion: pulumi.String(versions.ValidVersions[0]),
Fleet: &container.AttachedClusterFleetArgs{
Project: pulumi.Sprintf("projects/%v", project.Number),
},
LoggingConfig: &container.AttachedClusterLoggingConfigArgs{
ComponentConfig: &container.AttachedClusterLoggingConfigComponentConfigArgs{
EnableComponents: pulumi.StringArray{
pulumi.String("SYSTEM_COMPONENTS"),
pulumi.String("WORKLOADS"),
},
},
},
MonitoringConfig: &container.AttachedClusterMonitoringConfigArgs{
ManagedPrometheusConfig: &container.AttachedClusterMonitoringConfigManagedPrometheusConfigArgs{
Enabled: pulumi.Bool(true),
},
},
BinaryAuthorization: &container.AttachedClusterBinaryAuthorizationArgs{
EvaluationMode: pulumi.String("PROJECT_SINGLETON_POLICY_ENFORCE"),
},
ProxyConfig: &container.AttachedClusterProxyConfigArgs{
KubernetesSecret: &container.AttachedClusterProxyConfigKubernetesSecretArgs{
Name: pulumi.String("proxy-config"),
Namespace: pulumi.String("default"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;
return await Deployment.RunAsync(() =>
{
var project = Gcp.Organizations.GetProject.Invoke();
var versions = Gcp.Container.GetAttachedVersions.Invoke(new()
{
Location = "us-west1",
Project = project.Apply(getProjectResult => getProjectResult.ProjectId),
});
var primary = new Gcp.Container.AttachedCluster("primary", new()
{
Name = "basic",
Project = project.Apply(getProjectResult => getProjectResult.ProjectId),
Location = "us-west1",
Description = "Test cluster",
Distribution = "aks",
Annotations =
{
{ "label-one", "value-one" },
},
Authorization = new Gcp.Container.Inputs.AttachedClusterAuthorizationArgs
{
AdminUsers = new[]
{
"user1@example.com",
"user2@example.com",
},
AdminGroups = new[]
{
"group1@example.com",
"group2@example.com",
},
},
OidcConfig = new Gcp.Container.Inputs.AttachedClusterOidcConfigArgs
{
IssuerUrl = "https://oidc.issuer.url",
Jwks = Std.Base64encode.Invoke(new()
{
Input = "{\"keys\":[{\"use\":\"sig\",\"kty\":\"RSA\",\"kid\":\"testid\",\"alg\":\"RS256\",\"n\":\"somedata\",\"e\":\"AQAB\"}]}",
}).Apply(invoke => invoke.Result),
},
PlatformVersion = versions.Apply(getAttachedVersionsResult => getAttachedVersionsResult.ValidVersions[0]),
Fleet = new Gcp.Container.Inputs.AttachedClusterFleetArgs
{
Project = $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}",
},
LoggingConfig = new Gcp.Container.Inputs.AttachedClusterLoggingConfigArgs
{
ComponentConfig = new Gcp.Container.Inputs.AttachedClusterLoggingConfigComponentConfigArgs
{
EnableComponents = new[]
{
"SYSTEM_COMPONENTS",
"WORKLOADS",
},
},
},
MonitoringConfig = new Gcp.Container.Inputs.AttachedClusterMonitoringConfigArgs
{
ManagedPrometheusConfig = new Gcp.Container.Inputs.AttachedClusterMonitoringConfigManagedPrometheusConfigArgs
{
Enabled = true,
},
},
BinaryAuthorization = new Gcp.Container.Inputs.AttachedClusterBinaryAuthorizationArgs
{
EvaluationMode = "PROJECT_SINGLETON_POLICY_ENFORCE",
},
ProxyConfig = new Gcp.Container.Inputs.AttachedClusterProxyConfigArgs
{
KubernetesSecret = new Gcp.Container.Inputs.AttachedClusterProxyConfigKubernetesSecretArgs
{
Name = "proxy-config",
Namespace = "default",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.container.ContainerFunctions;
import com.pulumi.gcp.container.inputs.GetAttachedVersionsArgs;
import com.pulumi.gcp.container.AttachedCluster;
import com.pulumi.gcp.container.AttachedClusterArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterAuthorizationArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterOidcConfigArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterFleetArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterLoggingConfigArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterLoggingConfigComponentConfigArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterMonitoringConfigArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterMonitoringConfigManagedPrometheusConfigArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterBinaryAuthorizationArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterProxyConfigArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterProxyConfigKubernetesSecretArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.Base64encodeArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
final var versions = ContainerFunctions.getAttachedVersions(GetAttachedVersionsArgs.builder()
.location("us-west1")
.project(project.projectId())
.build());
var primary = new AttachedCluster("primary", AttachedClusterArgs.builder()
.name("basic")
.project(project.projectId())
.location("us-west1")
.description("Test cluster")
.distribution("aks")
.annotations(Map.of("label-one", "value-one"))
.authorization(AttachedClusterAuthorizationArgs.builder()
.adminUsers(
"user1@example.com",
"user2@example.com")
.adminGroups(
"group1@example.com",
"group2@example.com")
.build())
.oidcConfig(AttachedClusterOidcConfigArgs.builder()
.issuerUrl("https://oidc.issuer.url")
.jwks(StdFunctions.base64encode(Base64encodeArgs.builder()
.input("{\"keys\":[{\"use\":\"sig\",\"kty\":\"RSA\",\"kid\":\"testid\",\"alg\":\"RS256\",\"n\":\"somedata\",\"e\":\"AQAB\"}]}")
.build()).result())
.build())
.platformVersion(versions.validVersions()[0])
.fleet(AttachedClusterFleetArgs.builder()
.project(String.format("projects/%s", project.number()))
.build())
.loggingConfig(AttachedClusterLoggingConfigArgs.builder()
.componentConfig(AttachedClusterLoggingConfigComponentConfigArgs.builder()
.enableComponents(
"SYSTEM_COMPONENTS",
"WORKLOADS")
.build())
.build())
.monitoringConfig(AttachedClusterMonitoringConfigArgs.builder()
.managedPrometheusConfig(AttachedClusterMonitoringConfigManagedPrometheusConfigArgs.builder()
.enabled(true)
.build())
.build())
.binaryAuthorization(AttachedClusterBinaryAuthorizationArgs.builder()
.evaluationMode("PROJECT_SINGLETON_POLICY_ENFORCE")
.build())
.proxyConfig(AttachedClusterProxyConfigArgs.builder()
.kubernetesSecret(AttachedClusterProxyConfigKubernetesSecretArgs.builder()
.name("proxy-config")
.namespace("default")
.build())
.build())
.build());
}
}
resources:
primary:
type: gcp:container:AttachedCluster
properties:
name: basic
project: ${project.projectId}
location: us-west1
description: Test cluster
distribution: aks
annotations:
label-one: value-one
authorization:
adminUsers:
- user1@example.com
- user2@example.com
adminGroups:
- group1@example.com
- group2@example.com
oidcConfig:
issuerUrl: https://oidc.issuer.url
jwks:
fn::invoke:
function: std:base64encode
arguments:
input: '{"keys":[{"use":"sig","kty":"RSA","kid":"testid","alg":"RS256","n":"somedata","e":"AQAB"}]}'
return: result
platformVersion: ${versions.validVersions[0]}
fleet:
project: projects/${project.number}
loggingConfig:
componentConfig:
enableComponents:
- SYSTEM_COMPONENTS
- WORKLOADS
monitoringConfig:
managedPrometheusConfig:
enabled: true
binaryAuthorization:
evaluationMode: PROJECT_SINGLETON_POLICY_ENFORCE
proxyConfig:
kubernetesSecret:
name: proxy-config
namespace: default
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
versions:
fn::invoke:
function: gcp:container:getAttachedVersions
arguments:
location: us-west1
project: ${project.projectId}
The authorization block grants admin access to specific users and groups. The loggingConfig enables component and workload logging, while monitoringConfig activates managed Prometheus. The binaryAuthorization property enforces image signing policies. For clusters with private OIDC issuers, the jwks field provides the JSON Web Key Set data that Google Cloud uses to validate tokens. The proxyConfig references a Kubernetes secret containing proxy settings.
Handle deletion with error suppression
When decommissioning clusters, deletion operations may encounter errors if the underlying cluster is unreachable.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const project = gcp.organizations.getProject({});
const versions = project.then(project => gcp.container.getAttachedVersions({
location: "us-west1",
project: project.projectId,
}));
const primary = new gcp.container.AttachedCluster("primary", {
name: "basic",
location: "us-west1",
project: project.then(project => project.projectId),
description: "Test cluster",
distribution: "aks",
oidcConfig: {
issuerUrl: "https://oidc.issuer.url",
},
platformVersion: versions.then(versions => versions.validVersions?.[0]),
fleet: {
project: project.then(project => `projects/${project.number}`),
},
deletionPolicy: "DELETE_IGNORE_ERRORS",
});
import pulumi
import pulumi_gcp as gcp
project = gcp.organizations.get_project()
versions = gcp.container.get_attached_versions(location="us-west1",
project=project.project_id)
primary = gcp.container.AttachedCluster("primary",
name="basic",
location="us-west1",
project=project.project_id,
description="Test cluster",
distribution="aks",
oidc_config={
"issuer_url": "https://oidc.issuer.url",
},
platform_version=versions.valid_versions[0],
fleet={
"project": f"projects/{project.number}",
},
deletion_policy="DELETE_IGNORE_ERRORS")
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/container"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
versions, err := container.GetAttachedVersions(ctx, &container.GetAttachedVersionsArgs{
Location: "us-west1",
Project: project.ProjectId,
}, nil)
if err != nil {
return err
}
_, err = container.NewAttachedCluster(ctx, "primary", &container.AttachedClusterArgs{
Name: pulumi.String("basic"),
Location: pulumi.String("us-west1"),
Project: pulumi.String(project.ProjectId),
Description: pulumi.String("Test cluster"),
Distribution: pulumi.String("aks"),
OidcConfig: &container.AttachedClusterOidcConfigArgs{
IssuerUrl: pulumi.String("https://oidc.issuer.url"),
},
PlatformVersion: pulumi.String(versions.ValidVersions[0]),
Fleet: &container.AttachedClusterFleetArgs{
Project: pulumi.Sprintf("projects/%v", project.Number),
},
DeletionPolicy: pulumi.String("DELETE_IGNORE_ERRORS"),
})
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 project = Gcp.Organizations.GetProject.Invoke();
var versions = Gcp.Container.GetAttachedVersions.Invoke(new()
{
Location = "us-west1",
Project = project.Apply(getProjectResult => getProjectResult.ProjectId),
});
var primary = new Gcp.Container.AttachedCluster("primary", new()
{
Name = "basic",
Location = "us-west1",
Project = project.Apply(getProjectResult => getProjectResult.ProjectId),
Description = "Test cluster",
Distribution = "aks",
OidcConfig = new Gcp.Container.Inputs.AttachedClusterOidcConfigArgs
{
IssuerUrl = "https://oidc.issuer.url",
},
PlatformVersion = versions.Apply(getAttachedVersionsResult => getAttachedVersionsResult.ValidVersions[0]),
Fleet = new Gcp.Container.Inputs.AttachedClusterFleetArgs
{
Project = $"projects/{project.Apply(getProjectResult => getProjectResult.Number)}",
},
DeletionPolicy = "DELETE_IGNORE_ERRORS",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.container.ContainerFunctions;
import com.pulumi.gcp.container.inputs.GetAttachedVersionsArgs;
import com.pulumi.gcp.container.AttachedCluster;
import com.pulumi.gcp.container.AttachedClusterArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterOidcConfigArgs;
import com.pulumi.gcp.container.inputs.AttachedClusterFleetArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
final var versions = ContainerFunctions.getAttachedVersions(GetAttachedVersionsArgs.builder()
.location("us-west1")
.project(project.projectId())
.build());
var primary = new AttachedCluster("primary", AttachedClusterArgs.builder()
.name("basic")
.location("us-west1")
.project(project.projectId())
.description("Test cluster")
.distribution("aks")
.oidcConfig(AttachedClusterOidcConfigArgs.builder()
.issuerUrl("https://oidc.issuer.url")
.build())
.platformVersion(versions.validVersions()[0])
.fleet(AttachedClusterFleetArgs.builder()
.project(String.format("projects/%s", project.number()))
.build())
.deletionPolicy("DELETE_IGNORE_ERRORS")
.build());
}
}
resources:
primary:
type: gcp:container:AttachedCluster
properties:
name: basic
location: us-west1
project: ${project.projectId}
description: Test cluster
distribution: aks
oidcConfig:
issuerUrl: https://oidc.issuer.url
platformVersion: ${versions.validVersions[0]}
fleet:
project: projects/${project.number}
deletionPolicy: DELETE_IGNORE_ERRORS
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
versions:
fn::invoke:
function: gcp:container:getAttachedVersions
arguments:
location: us-west1
project: ${project.projectId}
The deletionPolicy property controls cleanup behavior. Setting it to DELETE_IGNORE_ERRORS allows Pulumi to complete the deletion even if the cluster is already gone or network issues prevent normal cleanup operations.
Beyond these examples
These snippets focus on specific attached cluster features: cluster registration and Fleet integration, RBAC and observability configuration, and deletion policy handling. They’re intentionally minimal rather than full cluster deployments.
The examples reference pre-existing infrastructure such as existing Kubernetes clusters (EKS, AKS, or generic CNCF-conformant), OIDC issuer endpoints with optional JWKS data, GCP projects for Fleet registration, and Kubernetes secrets for proxy configuration (when used). They focus on registering the cluster rather than provisioning the underlying Kubernetes infrastructure.
To keep things focused, common attached cluster patterns are omitted, including:
- Annotations for metadata and labels
- Security posture configuration (deprecated)
- Custom proxy configurations beyond basic secret reference
- Error handling and reconciliation monitoring
These omissions are intentional: the goal is to illustrate how each attached cluster feature is wired, not provide drop-in cluster management modules. See the AttachedCluster resource reference for all available configuration options.
Let's deploy GCP Anthos Attached Clusters
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Cluster Configuration & Immutability
distribution, location, name, and project properties are immutable and cannot be changed after cluster creation. Changing these will force recreation of the cluster.eks (AWS EKS), aks (Azure AKS), or generic (any CNCF conformant cluster).OIDC & Authentication
issuerUrl. Clusters with private issuers must provide both issuerUrl and jwks (the JWKS JSON data, typically base64-encoded).Annotations & Metadata
annotations field is non-authoritative and only manages annotations present in your configuration. To see all annotations on the resource, use the effectiveAnnotations output property.Deletion & Lifecycle
deletionPolicy to DELETE_IGNORE_ERRORS. This tells the API to proceed with deletion despite any errors encountered.Deprecated Features
securityPostureConfig is deprecated and will be removed in a future major release. Avoid using it in new configurations.Using a different cloud?
Explore containers guides for other cloud providers: