The gcp:iam/workloadIdentityPool:WorkloadIdentityPool resource, part of the Pulumi GCP provider, defines a workload identity pool that groups external or Google Cloud workload identities for IAM policy assignment. This guide focuses on three capabilities: basic pool creation for external federation, metadata and state control, and mTLS certificate issuance for Google Cloud workloads.
Pools operate in two modes: FEDERATION_ONLY (for external identities like AWS or GitHub Actions, requires provider resources) or TRUST_DOMAIN (for Google Cloud workloads like GKE or Compute Engine, requires CA pools and trust anchors). The examples are intentionally small. Combine them with WorkloadIdentityPoolProvider resources and IAM bindings for complete authentication flows.
Create a minimal pool for external identity federation
Most deployments start with a basic pool that federates external identities into Google Cloud without additional constraints.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const example = new gcp.iam.WorkloadIdentityPool("example", {workloadIdentityPoolId: "example-pool"});
import pulumi
import pulumi_gcp as gcp
example = gcp.iam.WorkloadIdentityPool("example", workload_identity_pool_id="example-pool")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/iam"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := iam.NewWorkloadIdentityPool(ctx, "example", &iam.WorkloadIdentityPoolArgs{
WorkloadIdentityPoolId: pulumi.String("example-pool"),
})
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 example = new Gcp.Iam.WorkloadIdentityPool("example", new()
{
WorkloadIdentityPoolId = "example-pool",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.iam.WorkloadIdentityPool;
import com.pulumi.gcp.iam.WorkloadIdentityPoolArgs;
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 example = new WorkloadIdentityPool("example", WorkloadIdentityPoolArgs.builder()
.workloadIdentityPoolId("example-pool")
.build());
}
}
resources:
example:
type: gcp:iam:WorkloadIdentityPool
properties:
workloadIdentityPoolId: example-pool
The workloadIdentityPoolId sets the pool’s identifier, which becomes part of the resource name. Without specifying mode, the pool operates in FEDERATION_ONLY mode by default. The pool itself doesn’t grant access; you must create WorkloadIdentityPoolProvider resources to define how external tokens map to Google Cloud identities.
Configure federation with display metadata and state control
Production pools often include display names and descriptions for organization, and may start disabled for testing.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const example = new gcp.iam.WorkloadIdentityPool("example", {
workloadIdentityPoolId: "example-pool",
displayName: "Name of the pool",
description: "Identity pool operates in FEDERATION_ONLY mode",
disabled: true,
mode: "FEDERATION_ONLY",
});
import pulumi
import pulumi_gcp as gcp
example = gcp.iam.WorkloadIdentityPool("example",
workload_identity_pool_id="example-pool",
display_name="Name of the pool",
description="Identity pool operates in FEDERATION_ONLY mode",
disabled=True,
mode="FEDERATION_ONLY")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/iam"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := iam.NewWorkloadIdentityPool(ctx, "example", &iam.WorkloadIdentityPoolArgs{
WorkloadIdentityPoolId: pulumi.String("example-pool"),
DisplayName: pulumi.String("Name of the pool"),
Description: pulumi.String("Identity pool operates in FEDERATION_ONLY mode"),
Disabled: pulumi.Bool(true),
Mode: pulumi.String("FEDERATION_ONLY"),
})
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 example = new Gcp.Iam.WorkloadIdentityPool("example", new()
{
WorkloadIdentityPoolId = "example-pool",
DisplayName = "Name of the pool",
Description = "Identity pool operates in FEDERATION_ONLY mode",
Disabled = true,
Mode = "FEDERATION_ONLY",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.iam.WorkloadIdentityPool;
import com.pulumi.gcp.iam.WorkloadIdentityPoolArgs;
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 example = new WorkloadIdentityPool("example", WorkloadIdentityPoolArgs.builder()
.workloadIdentityPoolId("example-pool")
.displayName("Name of the pool")
.description("Identity pool operates in FEDERATION_ONLY mode")
.disabled(true)
.mode("FEDERATION_ONLY")
.build());
}
}
resources:
example:
type: gcp:iam:WorkloadIdentityPool
properties:
workloadIdentityPoolId: example-pool
displayName: Name of the pool
description: Identity pool operates in FEDERATION_ONLY mode
disabled: true
mode: FEDERATION_ONLY
The displayName and description properties add organizational metadata. The disabled property controls whether the pool can exchange tokens; set it to true during setup, then flip to false when ready. The mode property explicitly sets FEDERATION_ONLY, though this is the default. Note that mode cannot be changed after creation; you must create a new pool to switch modes.
Issue mTLS certificates for Google Cloud workloads
TRUST_DOMAIN mode pools assign identities to Google Cloud workloads and can issue mutual TLS certificates for service-to-service authentication.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";
const example = new gcp.iam.WorkloadIdentityPool("example", {
workloadIdentityPoolId: "example-pool",
displayName: "Name of the pool",
description: "Identity pool operates in TRUST_DOMAIN mode",
disabled: true,
mode: "TRUST_DOMAIN",
inlineCertificateIssuanceConfig: {
caPools: {
"us-central1": "projects/project-bar/locations/us-central1/caPools/ca-pool-bar",
"asia-east2": "projects/project-foo/locations/asia-east2/caPools/ca-pool-foo",
},
lifetime: "86400s",
rotationWindowPercentage: 50,
keyAlgorithm: "ECDSA_P256",
},
inlineTrustConfig: {
additionalTrustBundles: [
{
trustDomain: "example.com",
trustAnchors: [
{
pemCertificate: std.file({
input: "test-fixtures/trust_anchor_1.pem",
}).then(invoke => invoke.result),
},
{
pemCertificate: std.file({
input: "test-fixtures/trust_anchor_2.pem",
}).then(invoke => invoke.result),
},
],
},
{
trustDomain: "example.net",
trustAnchors: [
{
pemCertificate: std.file({
input: "test-fixtures/trust_anchor_3.pem",
}).then(invoke => invoke.result),
},
{
pemCertificate: std.file({
input: "test-fixtures/trust_anchor_4.pem",
}).then(invoke => invoke.result),
},
],
},
],
},
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std
example = gcp.iam.WorkloadIdentityPool("example",
workload_identity_pool_id="example-pool",
display_name="Name of the pool",
description="Identity pool operates in TRUST_DOMAIN mode",
disabled=True,
mode="TRUST_DOMAIN",
inline_certificate_issuance_config={
"ca_pools": {
"us-central1": "projects/project-bar/locations/us-central1/caPools/ca-pool-bar",
"asia-east2": "projects/project-foo/locations/asia-east2/caPools/ca-pool-foo",
},
"lifetime": "86400s",
"rotation_window_percentage": 50,
"key_algorithm": "ECDSA_P256",
},
inline_trust_config={
"additional_trust_bundles": [
{
"trust_domain": "example.com",
"trust_anchors": [
{
"pem_certificate": std.file(input="test-fixtures/trust_anchor_1.pem").result,
},
{
"pem_certificate": std.file(input="test-fixtures/trust_anchor_2.pem").result,
},
],
},
{
"trust_domain": "example.net",
"trust_anchors": [
{
"pem_certificate": std.file(input="test-fixtures/trust_anchor_3.pem").result,
},
{
"pem_certificate": std.file(input="test-fixtures/trust_anchor_4.pem").result,
},
],
},
],
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/iam"
"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 {
invokeFile, err := std.File(ctx, &std.FileArgs{
Input: "test-fixtures/trust_anchor_1.pem",
}, nil)
if err != nil {
return err
}
invokeFile1, err := std.File(ctx, &std.FileArgs{
Input: "test-fixtures/trust_anchor_2.pem",
}, nil)
if err != nil {
return err
}
invokeFile2, err := std.File(ctx, &std.FileArgs{
Input: "test-fixtures/trust_anchor_3.pem",
}, nil)
if err != nil {
return err
}
invokeFile3, err := std.File(ctx, &std.FileArgs{
Input: "test-fixtures/trust_anchor_4.pem",
}, nil)
if err != nil {
return err
}
_, err = iam.NewWorkloadIdentityPool(ctx, "example", &iam.WorkloadIdentityPoolArgs{
WorkloadIdentityPoolId: pulumi.String("example-pool"),
DisplayName: pulumi.String("Name of the pool"),
Description: pulumi.String("Identity pool operates in TRUST_DOMAIN mode"),
Disabled: pulumi.Bool(true),
Mode: pulumi.String("TRUST_DOMAIN"),
InlineCertificateIssuanceConfig: &iam.WorkloadIdentityPoolInlineCertificateIssuanceConfigArgs{
CaPools: pulumi.StringMap{
"us-central1": pulumi.String("projects/project-bar/locations/us-central1/caPools/ca-pool-bar"),
"asia-east2": pulumi.String("projects/project-foo/locations/asia-east2/caPools/ca-pool-foo"),
},
Lifetime: pulumi.String("86400s"),
RotationWindowPercentage: pulumi.Int(50),
KeyAlgorithm: pulumi.String("ECDSA_P256"),
},
InlineTrustConfig: &iam.WorkloadIdentityPoolInlineTrustConfigArgs{
AdditionalTrustBundles: iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleArray{
&iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleArgs{
TrustDomain: pulumi.String("example.com"),
TrustAnchors: iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArray{
&iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs{
PemCertificate: pulumi.String(invokeFile.Result),
},
&iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs{
PemCertificate: pulumi.String(invokeFile1.Result),
},
},
},
&iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleArgs{
TrustDomain: pulumi.String("example.net"),
TrustAnchors: iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArray{
&iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs{
PemCertificate: pulumi.String(invokeFile2.Result),
},
&iam.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs{
PemCertificate: pulumi.String(invokeFile3.Result),
},
},
},
},
},
})
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 example = new Gcp.Iam.WorkloadIdentityPool("example", new()
{
WorkloadIdentityPoolId = "example-pool",
DisplayName = "Name of the pool",
Description = "Identity pool operates in TRUST_DOMAIN mode",
Disabled = true,
Mode = "TRUST_DOMAIN",
InlineCertificateIssuanceConfig = new Gcp.Iam.Inputs.WorkloadIdentityPoolInlineCertificateIssuanceConfigArgs
{
CaPools =
{
{ "us-central1", "projects/project-bar/locations/us-central1/caPools/ca-pool-bar" },
{ "asia-east2", "projects/project-foo/locations/asia-east2/caPools/ca-pool-foo" },
},
Lifetime = "86400s",
RotationWindowPercentage = 50,
KeyAlgorithm = "ECDSA_P256",
},
InlineTrustConfig = new Gcp.Iam.Inputs.WorkloadIdentityPoolInlineTrustConfigArgs
{
AdditionalTrustBundles = new[]
{
new Gcp.Iam.Inputs.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleArgs
{
TrustDomain = "example.com",
TrustAnchors = new[]
{
new Gcp.Iam.Inputs.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs
{
PemCertificate = Std.File.Invoke(new()
{
Input = "test-fixtures/trust_anchor_1.pem",
}).Apply(invoke => invoke.Result),
},
new Gcp.Iam.Inputs.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs
{
PemCertificate = Std.File.Invoke(new()
{
Input = "test-fixtures/trust_anchor_2.pem",
}).Apply(invoke => invoke.Result),
},
},
},
new Gcp.Iam.Inputs.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleArgs
{
TrustDomain = "example.net",
TrustAnchors = new[]
{
new Gcp.Iam.Inputs.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs
{
PemCertificate = Std.File.Invoke(new()
{
Input = "test-fixtures/trust_anchor_3.pem",
}).Apply(invoke => invoke.Result),
},
new Gcp.Iam.Inputs.WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs
{
PemCertificate = Std.File.Invoke(new()
{
Input = "test-fixtures/trust_anchor_4.pem",
}).Apply(invoke => invoke.Result),
},
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.iam.WorkloadIdentityPool;
import com.pulumi.gcp.iam.WorkloadIdentityPoolArgs;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolInlineCertificateIssuanceConfigArgs;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolInlineTrustConfigArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.FileArgs;
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 example = new WorkloadIdentityPool("example", WorkloadIdentityPoolArgs.builder()
.workloadIdentityPoolId("example-pool")
.displayName("Name of the pool")
.description("Identity pool operates in TRUST_DOMAIN mode")
.disabled(true)
.mode("TRUST_DOMAIN")
.inlineCertificateIssuanceConfig(WorkloadIdentityPoolInlineCertificateIssuanceConfigArgs.builder()
.caPools(Map.ofEntries(
Map.entry("us-central1", "projects/project-bar/locations/us-central1/caPools/ca-pool-bar"),
Map.entry("asia-east2", "projects/project-foo/locations/asia-east2/caPools/ca-pool-foo")
))
.lifetime("86400s")
.rotationWindowPercentage(50)
.keyAlgorithm("ECDSA_P256")
.build())
.inlineTrustConfig(WorkloadIdentityPoolInlineTrustConfigArgs.builder()
.additionalTrustBundles(
WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleArgs.builder()
.trustDomain("example.com")
.trustAnchors(
WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs.builder()
.pemCertificate(StdFunctions.file(FileArgs.builder()
.input("test-fixtures/trust_anchor_1.pem")
.build()).result())
.build(),
WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs.builder()
.pemCertificate(StdFunctions.file(FileArgs.builder()
.input("test-fixtures/trust_anchor_2.pem")
.build()).result())
.build())
.build(),
WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleArgs.builder()
.trustDomain("example.net")
.trustAnchors(
WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs.builder()
.pemCertificate(StdFunctions.file(FileArgs.builder()
.input("test-fixtures/trust_anchor_3.pem")
.build()).result())
.build(),
WorkloadIdentityPoolInlineTrustConfigAdditionalTrustBundleTrustAnchorArgs.builder()
.pemCertificate(StdFunctions.file(FileArgs.builder()
.input("test-fixtures/trust_anchor_4.pem")
.build()).result())
.build())
.build())
.build())
.build());
}
}
resources:
example:
type: gcp:iam:WorkloadIdentityPool
properties:
workloadIdentityPoolId: example-pool
displayName: Name of the pool
description: Identity pool operates in TRUST_DOMAIN mode
disabled: true
mode: TRUST_DOMAIN
inlineCertificateIssuanceConfig:
caPools:
us-central1: projects/project-bar/locations/us-central1/caPools/ca-pool-bar
asia-east2: projects/project-foo/locations/asia-east2/caPools/ca-pool-foo
lifetime: 86400s
rotationWindowPercentage: 50
keyAlgorithm: ECDSA_P256
inlineTrustConfig:
additionalTrustBundles:
- trustDomain: example.com
trustAnchors:
- pemCertificate:
fn::invoke:
function: std:file
arguments:
input: test-fixtures/trust_anchor_1.pem
return: result
- pemCertificate:
fn::invoke:
function: std:file
arguments:
input: test-fixtures/trust_anchor_2.pem
return: result
- trustDomain: example.net
trustAnchors:
- pemCertificate:
fn::invoke:
function: std:file
arguments:
input: test-fixtures/trust_anchor_3.pem
return: result
- pemCertificate:
fn::invoke:
function: std:file
arguments:
input: test-fixtures/trust_anchor_4.pem
return: result
In TRUST_DOMAIN mode, all identities must follow the format ns/<namespace>/sa/<workload_identifier>. The inlineCertificateIssuanceConfig property defines CA pools by region for certificate issuance, along with certificate lifetime and rotation settings. The inlineTrustConfig property establishes trust relationships with other domains by specifying trust anchors (PEM certificates). Each trust domain can have multiple trust anchors, allowing the pool to recognize certificates from those domains.
Beyond these examples
These snippets focus on specific pool-level features: federation and trust domain modes, mTLS certificate issuance, and trust anchor configuration. They’re intentionally minimal rather than full authentication systems.
The examples may reference pre-existing infrastructure such as CA pool resources in specified regions (for TRUST_DOMAIN mode) and PEM certificate files for trust anchors. They focus on configuring the pool rather than provisioning the complete identity federation workflow.
To keep things focused, common pool patterns are omitted, including:
- WorkloadIdentityPoolProvider resources (required for federation)
- IAM policy bindings to grant resource access
- Certificate rotation and lifecycle management
- Namespace and workload identifier conventions
These omissions are intentional: the goal is to illustrate how each pool feature is wired, not provide drop-in identity federation modules. See the Workload Identity Pool resource reference for all available configuration options.
Let's configure GCP Workload Identity Pools
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Pool Configuration & Modes
FEDERATION_ONLY (the default) is for federating external workload identities like AWS or Azure into Google Cloud, with no format constraints and support for creating providers. TRUST_DOMAIN is for assigning identities to Google Cloud workloads, requires identities to follow the ns/<namespace>/sa/<workload_identifier> format, and does not allow creating gcp.iam.WorkloadIdentityPoolProvider resources.gcp.iam.WorkloadIdentityPoolProvider resources cannot be created within TRUST_DOMAIN mode pools. Providers are only supported in FEDERATION_ONLY mode.Immutability & Lifecycle
mode field is immutable after pool creation. While pulumi preview may show an update, pulumi up will fail with an API error like “Error 400: Attempted to update an immutable field.” You must create a new Workload Identity Pool resource with the desired mode instead.project, workloadIdentityPoolId, and mode fields are immutable after creation. Changing these requires creating a new pool.UndeleteWorkloadIdentityPool.Identity & Access Control
Naming & Constraints
gcp- is reserved for Google and cannot be used.description field cannot exceed 256 characters, and the displayName field cannot exceed 32 characters.TRUST_DOMAIN mode pool must conform to the format ns/<namespace>/sa/<workload_identifier>, consisting of a single namespace and individual workload identifier.