The gcp:container/attachedCluster:AttachedCluster resource, part of the Pulumi GCP provider, registers external Kubernetes clusters with Google Cloud Anthos for unified management. This guide focuses on three capabilities: basic cluster registration with Fleet, RBAC and observability configuration, and deletion policy handling.
Attached clusters must already exist on their respective platforms (EKS, AKS, or generic CNCF-conformant) and have accessible OIDC issuer URLs. The examples are intentionally small. Combine them with your own cluster infrastructure and access policies.
Register an external cluster with minimal configuration
Teams running Kubernetes on AWS EKS, Azure AKS, or other platforms can register those clusters with Anthos to gain unified management and observability across environments.
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 (
"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). The oidcConfig provides the issuer URL that GCP uses to validate Kubernetes Service Account tokens, enabling system workloads to authenticate back to Google Cloud. The platformVersion specifies the Anthos version, and fleet connects the cluster to a Fleet project for centralized management.
Configure RBAC, logging, and monitoring for production
Production clusters typically need access controls, observability, and security policies 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 (
"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 cluster-admin permissions to specific users and groups. The loggingConfig enables system component and workload logs, while monitoringConfig activates managed Prometheus for metrics collection. The binaryAuthorization property enforces image signing policies. For clusters with private OIDC issuers, the jwks field provides the JSON Web Key Set data that GCP uses to validate tokens. The proxyConfig references a Kubernetes secret in the attached cluster that contains proxy settings.
Handle deletion when cluster is already removed
When the underlying Kubernetes cluster has been deleted outside of Pulumi, attempting to clean up the Anthos registration can fail with errors.
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 (
"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 how Pulumi handles deletion failures. Setting it to DELETE_IGNORE_ERRORS allows the resource to be removed from Pulumi state even if the cluster is unreachable or already deleted, preventing stuck deployments during cleanup.
Beyond these examples
These snippets focus on specific attached cluster features: cluster registration and Fleet integration, RBAC, logging, and monitoring configuration, and deletion policy handling. They’re intentionally minimal rather than full multi-cloud deployments.
The examples assume pre-existing infrastructure such as Kubernetes clusters running on EKS, AKS, or other platforms, OIDC issuer URLs accessible from the cluster, and a GCP project with Fleet API enabled. 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
- Workload Identity setup and configuration
These omissions are intentional: the goal is to illustrate how each attached cluster feature is wired, not provide drop-in multi-cloud 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 & Setup
eks (Amazon EKS), aks (Azure AKS), and generic (any CNCF conformant cluster). The distribution property is immutable, so changing it requires recreating the cluster.gcp.container.getAttachedVersions data source with your location and project to retrieve available versions. The examples show accessing validVersions[0] for the latest version.distribution, location, name, and project. Modifying any of these requires recreating the cluster.Authentication & Authorization
issuerUrl in oidcConfig. Clusters with private issuers must provide both issuerUrl and jwks (base64-encoded JWKS JSON data), as shown in the full example.authorization block to specify adminUsers (email addresses) and adminGroups (group email addresses) who can administer the cluster.Resource Management
annotations field is non-authoritative and only manages annotations defined in your configuration. To see all annotations on the resource (including those set by other clients), use the effectiveAnnotations output property.deletionPolicy to DELETE_IGNORE_ERRORS instead of the default DELETE. This is useful when the underlying cluster may already be deleted or unreachable.Deprecations & Warnings
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: