Configure GCP Workload Identity Pool Providers

The gcp:iam/workloadIdentityPoolProvider:WorkloadIdentityPoolProvider resource, part of the Pulumi GCP provider, configures external identity providers that can authenticate to Google Cloud via Workload Identity Federation. This guide focuses on four capabilities: AWS IAM role federation, OIDC providers (Azure AD and GitHub Actions), attribute mapping and conditional access, and X.509 certificate validation.

Providers belong to WorkloadIdentityPools and reference external identity systems such as AWS accounts, Azure tenants, GitHub repositories, or certificate authorities. The examples are intentionally small. Combine them with your own IAM policy bindings to grant actual resource access.

Connect AWS workloads with minimal configuration

Teams migrating from AWS often need to grant AWS services access to GCP resources without managing long-lived credentials.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const pool = new gcp.iam.WorkloadIdentityPool("pool", {workloadIdentityPoolId: "example-pool"});
const example = new gcp.iam.WorkloadIdentityPoolProvider("example", {
    workloadIdentityPoolId: pool.workloadIdentityPoolId,
    workloadIdentityPoolProviderId: "example-prvdr",
    aws: {
        accountId: "999999999999",
    },
});
import pulumi
import pulumi_gcp as gcp

pool = gcp.iam.WorkloadIdentityPool("pool", workload_identity_pool_id="example-pool")
example = gcp.iam.WorkloadIdentityPoolProvider("example",
    workload_identity_pool_id=pool.workload_identity_pool_id,
    workload_identity_pool_provider_id="example-prvdr",
    aws={
        "account_id": "999999999999",
    })
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 {
		pool, err := iam.NewWorkloadIdentityPool(ctx, "pool", &iam.WorkloadIdentityPoolArgs{
			WorkloadIdentityPoolId: pulumi.String("example-pool"),
		})
		if err != nil {
			return err
		}
		_, err = iam.NewWorkloadIdentityPoolProvider(ctx, "example", &iam.WorkloadIdentityPoolProviderArgs{
			WorkloadIdentityPoolId:         pool.WorkloadIdentityPoolId,
			WorkloadIdentityPoolProviderId: pulumi.String("example-prvdr"),
			Aws: &iam.WorkloadIdentityPoolProviderAwsArgs{
				AccountId: pulumi.String("999999999999"),
			},
		})
		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 pool = new Gcp.Iam.WorkloadIdentityPool("pool", new()
    {
        WorkloadIdentityPoolId = "example-pool",
    });

    var example = new Gcp.Iam.WorkloadIdentityPoolProvider("example", new()
    {
        WorkloadIdentityPoolId = pool.WorkloadIdentityPoolId,
        WorkloadIdentityPoolProviderId = "example-prvdr",
        Aws = new Gcp.Iam.Inputs.WorkloadIdentityPoolProviderAwsArgs
        {
            AccountId = "999999999999",
        },
    });

});
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.WorkloadIdentityPoolProvider;
import com.pulumi.gcp.iam.WorkloadIdentityPoolProviderArgs;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolProviderAwsArgs;
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 pool = new WorkloadIdentityPool("pool", WorkloadIdentityPoolArgs.builder()
            .workloadIdentityPoolId("example-pool")
            .build());

        var example = new WorkloadIdentityPoolProvider("example", WorkloadIdentityPoolProviderArgs.builder()
            .workloadIdentityPoolId(pool.workloadIdentityPoolId())
            .workloadIdentityPoolProviderId("example-prvdr")
            .aws(WorkloadIdentityPoolProviderAwsArgs.builder()
                .accountId("999999999999")
                .build())
            .build());

    }
}
resources:
  pool:
    type: gcp:iam:WorkloadIdentityPool
    properties:
      workloadIdentityPoolId: example-pool
  example:
    type: gcp:iam:WorkloadIdentityPoolProvider
    properties:
      workloadIdentityPoolId: ${pool.workloadIdentityPoolId}
      workloadIdentityPoolProviderId: example-prvdr
      aws:
        accountId: '999999999999'

The aws block identifies the AWS account whose IAM roles can authenticate. The workloadIdentityPoolId references the pool this provider belongs to. When an AWS service assumes a role in the specified account, it can exchange AWS credentials for a GCP token.

Filter AWS identities with attribute mapping and conditions

Production environments need fine-grained control over which AWS identities can access GCP resources.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const pool = new gcp.iam.WorkloadIdentityPool("pool", {workloadIdentityPoolId: "example-pool"});
const example = new gcp.iam.WorkloadIdentityPoolProvider("example", {
    workloadIdentityPoolId: pool.workloadIdentityPoolId,
    workloadIdentityPoolProviderId: "example-prvdr",
    displayName: "Name of provider",
    description: "AWS identity pool provider for automated test",
    disabled: true,
    attributeCondition: "attribute.aws_role==\"arn:aws:sts::999999999999:assumed-role/stack-eu-central-1-lambdaRole\"",
    attributeMapping: {
        "google.subject": "assertion.arn",
        "attribute.aws_account": "assertion.account",
        "attribute.environment": "assertion.arn.contains(\":instance-profile/Production\") ? \"prod\" : \"test\"",
    },
    aws: {
        accountId: "999999999999",
    },
});
import pulumi
import pulumi_gcp as gcp

pool = gcp.iam.WorkloadIdentityPool("pool", workload_identity_pool_id="example-pool")
example = gcp.iam.WorkloadIdentityPoolProvider("example",
    workload_identity_pool_id=pool.workload_identity_pool_id,
    workload_identity_pool_provider_id="example-prvdr",
    display_name="Name of provider",
    description="AWS identity pool provider for automated test",
    disabled=True,
    attribute_condition="attribute.aws_role==\"arn:aws:sts::999999999999:assumed-role/stack-eu-central-1-lambdaRole\"",
    attribute_mapping={
        "google.subject": "assertion.arn",
        "attribute.aws_account": "assertion.account",
        "attribute.environment": "assertion.arn.contains(\":instance-profile/Production\") ? \"prod\" : \"test\"",
    },
    aws={
        "account_id": "999999999999",
    })
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 {
		pool, err := iam.NewWorkloadIdentityPool(ctx, "pool", &iam.WorkloadIdentityPoolArgs{
			WorkloadIdentityPoolId: pulumi.String("example-pool"),
		})
		if err != nil {
			return err
		}
		_, err = iam.NewWorkloadIdentityPoolProvider(ctx, "example", &iam.WorkloadIdentityPoolProviderArgs{
			WorkloadIdentityPoolId:         pool.WorkloadIdentityPoolId,
			WorkloadIdentityPoolProviderId: pulumi.String("example-prvdr"),
			DisplayName:                    pulumi.String("Name of provider"),
			Description:                    pulumi.String("AWS identity pool provider for automated test"),
			Disabled:                       pulumi.Bool(true),
			AttributeCondition:             pulumi.String("attribute.aws_role==\"arn:aws:sts::999999999999:assumed-role/stack-eu-central-1-lambdaRole\""),
			AttributeMapping: pulumi.StringMap{
				"google.subject":        pulumi.String("assertion.arn"),
				"attribute.aws_account": pulumi.String("assertion.account"),
				"attribute.environment": pulumi.String("assertion.arn.contains(\":instance-profile/Production\") ? \"prod\" : \"test\""),
			},
			Aws: &iam.WorkloadIdentityPoolProviderAwsArgs{
				AccountId: pulumi.String("999999999999"),
			},
		})
		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 pool = new Gcp.Iam.WorkloadIdentityPool("pool", new()
    {
        WorkloadIdentityPoolId = "example-pool",
    });

    var example = new Gcp.Iam.WorkloadIdentityPoolProvider("example", new()
    {
        WorkloadIdentityPoolId = pool.WorkloadIdentityPoolId,
        WorkloadIdentityPoolProviderId = "example-prvdr",
        DisplayName = "Name of provider",
        Description = "AWS identity pool provider for automated test",
        Disabled = true,
        AttributeCondition = "attribute.aws_role==\"arn:aws:sts::999999999999:assumed-role/stack-eu-central-1-lambdaRole\"",
        AttributeMapping = 
        {
            { "google.subject", "assertion.arn" },
            { "attribute.aws_account", "assertion.account" },
            { "attribute.environment", "assertion.arn.contains(\":instance-profile/Production\") ? \"prod\" : \"test\"" },
        },
        Aws = new Gcp.Iam.Inputs.WorkloadIdentityPoolProviderAwsArgs
        {
            AccountId = "999999999999",
        },
    });

});
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.WorkloadIdentityPoolProvider;
import com.pulumi.gcp.iam.WorkloadIdentityPoolProviderArgs;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolProviderAwsArgs;
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 pool = new WorkloadIdentityPool("pool", WorkloadIdentityPoolArgs.builder()
            .workloadIdentityPoolId("example-pool")
            .build());

        var example = new WorkloadIdentityPoolProvider("example", WorkloadIdentityPoolProviderArgs.builder()
            .workloadIdentityPoolId(pool.workloadIdentityPoolId())
            .workloadIdentityPoolProviderId("example-prvdr")
            .displayName("Name of provider")
            .description("AWS identity pool provider for automated test")
            .disabled(true)
            .attributeCondition("attribute.aws_role==\"arn:aws:sts::999999999999:assumed-role/stack-eu-central-1-lambdaRole\"")
            .attributeMapping(Map.ofEntries(
                Map.entry("google.subject", "assertion.arn"),
                Map.entry("attribute.aws_account", "assertion.account"),
                Map.entry("attribute.environment", "assertion.arn.contains(\":instance-profile/Production\") ? \"prod\" : \"test\"")
            ))
            .aws(WorkloadIdentityPoolProviderAwsArgs.builder()
                .accountId("999999999999")
                .build())
            .build());

    }
}
resources:
  pool:
    type: gcp:iam:WorkloadIdentityPool
    properties:
      workloadIdentityPoolId: example-pool
  example:
    type: gcp:iam:WorkloadIdentityPoolProvider
    properties:
      workloadIdentityPoolId: ${pool.workloadIdentityPoolId}
      workloadIdentityPoolProviderId: example-prvdr
      displayName: Name of provider
      description: AWS identity pool provider for automated test
      disabled: true
      attributeCondition: attribute.aws_role=="arn:aws:sts::999999999999:assumed-role/stack-eu-central-1-lambdaRole"
      attributeMapping:
        google.subject: assertion.arn
        attribute.aws_account: assertion.account
        attribute.environment: 'assertion.arn.contains(":instance-profile/Production") ? "prod" : "test"'
      aws:
        accountId: '999999999999'

Attribute mapping extracts claims from AWS credentials into GCP principals. The google.subject mapping creates a principal identifier from the AWS role ARN. The attributeCondition restricts access to a specific assumed role. Custom attributes like attribute.environment use CEL expressions to classify workloads based on their ARN patterns. The disabled property prevents token exchange while preserving the configuration.

Authenticate GitHub Actions workflows to GCP

CI/CD pipelines running in GitHub Actions can deploy to GCP without storing service account keys.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const pool = new gcp.iam.WorkloadIdentityPool("pool", {workloadIdentityPoolId: "example-pool"});
const example = new gcp.iam.WorkloadIdentityPoolProvider("example", {
    workloadIdentityPoolId: pool.workloadIdentityPoolId,
    workloadIdentityPoolProviderId: "example-prvdr",
    displayName: "Name of provider",
    description: "GitHub Actions identity pool provider for automated test",
    disabled: true,
    attributeCondition: `    assertion.repository_owner_id == \\"123456789\\" &&
    attribute.repository == \\"gh-org/gh-repo\\" &&
    assertion.ref == \\"refs/heads/main\\" &&
    assertion.ref_type == \\"branch\\"
`,
    attributeMapping: {
        "google.subject": "assertion.sub",
        "attribute.actor": "assertion.actor",
        "attribute.aud": "assertion.aud",
        "attribute.repository": "assertion.repository",
    },
    oidc: {
        issuerUri: "https://token.actions.githubusercontent.com",
    },
});
import pulumi
import pulumi_gcp as gcp

pool = gcp.iam.WorkloadIdentityPool("pool", workload_identity_pool_id="example-pool")
example = gcp.iam.WorkloadIdentityPoolProvider("example",
    workload_identity_pool_id=pool.workload_identity_pool_id,
    workload_identity_pool_provider_id="example-prvdr",
    display_name="Name of provider",
    description="GitHub Actions identity pool provider for automated test",
    disabled=True,
    attribute_condition="""    assertion.repository_owner_id == \"123456789\" &&
    attribute.repository == \"gh-org/gh-repo\" &&
    assertion.ref == \"refs/heads/main\" &&
    assertion.ref_type == \"branch\"
""",
    attribute_mapping={
        "google.subject": "assertion.sub",
        "attribute.actor": "assertion.actor",
        "attribute.aud": "assertion.aud",
        "attribute.repository": "assertion.repository",
    },
    oidc={
        "issuer_uri": "https://token.actions.githubusercontent.com",
    })
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 {
		pool, err := iam.NewWorkloadIdentityPool(ctx, "pool", &iam.WorkloadIdentityPoolArgs{
			WorkloadIdentityPoolId: pulumi.String("example-pool"),
		})
		if err != nil {
			return err
		}
		_, err = iam.NewWorkloadIdentityPoolProvider(ctx, "example", &iam.WorkloadIdentityPoolProviderArgs{
			WorkloadIdentityPoolId:         pool.WorkloadIdentityPoolId,
			WorkloadIdentityPoolProviderId: pulumi.String("example-prvdr"),
			DisplayName:                    pulumi.String("Name of provider"),
			Description:                    pulumi.String("GitHub Actions identity pool provider for automated test"),
			Disabled:                       pulumi.Bool(true),
			AttributeCondition:             pulumi.String("    assertion.repository_owner_id == \\\"123456789\\\" &&\n    attribute.repository == \\\"gh-org/gh-repo\\\" &&\n    assertion.ref == \\\"refs/heads/main\\\" &&\n    assertion.ref_type == \\\"branch\\\"\n"),
			AttributeMapping: pulumi.StringMap{
				"google.subject":       pulumi.String("assertion.sub"),
				"attribute.actor":      pulumi.String("assertion.actor"),
				"attribute.aud":        pulumi.String("assertion.aud"),
				"attribute.repository": pulumi.String("assertion.repository"),
			},
			Oidc: &iam.WorkloadIdentityPoolProviderOidcArgs{
				IssuerUri: pulumi.String("https://token.actions.githubusercontent.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 pool = new Gcp.Iam.WorkloadIdentityPool("pool", new()
    {
        WorkloadIdentityPoolId = "example-pool",
    });

    var example = new Gcp.Iam.WorkloadIdentityPoolProvider("example", new()
    {
        WorkloadIdentityPoolId = pool.WorkloadIdentityPoolId,
        WorkloadIdentityPoolProviderId = "example-prvdr",
        DisplayName = "Name of provider",
        Description = "GitHub Actions identity pool provider for automated test",
        Disabled = true,
        AttributeCondition = @"    assertion.repository_owner_id == \""123456789\"" &&
    attribute.repository == \""gh-org/gh-repo\"" &&
    assertion.ref == \""refs/heads/main\"" &&
    assertion.ref_type == \""branch\""
",
        AttributeMapping = 
        {
            { "google.subject", "assertion.sub" },
            { "attribute.actor", "assertion.actor" },
            { "attribute.aud", "assertion.aud" },
            { "attribute.repository", "assertion.repository" },
        },
        Oidc = new Gcp.Iam.Inputs.WorkloadIdentityPoolProviderOidcArgs
        {
            IssuerUri = "https://token.actions.githubusercontent.com",
        },
    });

});
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.WorkloadIdentityPoolProvider;
import com.pulumi.gcp.iam.WorkloadIdentityPoolProviderArgs;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolProviderOidcArgs;
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 pool = new WorkloadIdentityPool("pool", WorkloadIdentityPoolArgs.builder()
            .workloadIdentityPoolId("example-pool")
            .build());

        var example = new WorkloadIdentityPoolProvider("example", WorkloadIdentityPoolProviderArgs.builder()
            .workloadIdentityPoolId(pool.workloadIdentityPoolId())
            .workloadIdentityPoolProviderId("example-prvdr")
            .displayName("Name of provider")
            .description("GitHub Actions identity pool provider for automated test")
            .disabled(true)
            .attributeCondition("""
    assertion.repository_owner_id == \"123456789\" &&
    attribute.repository == \"gh-org/gh-repo\" &&
    assertion.ref == \"refs/heads/main\" &&
    assertion.ref_type == \"branch\"
            """)
            .attributeMapping(Map.ofEntries(
                Map.entry("google.subject", "assertion.sub"),
                Map.entry("attribute.actor", "assertion.actor"),
                Map.entry("attribute.aud", "assertion.aud"),
                Map.entry("attribute.repository", "assertion.repository")
            ))
            .oidc(WorkloadIdentityPoolProviderOidcArgs.builder()
                .issuerUri("https://token.actions.githubusercontent.com")
                .build())
            .build());

    }
}
resources:
  pool:
    type: gcp:iam:WorkloadIdentityPool
    properties:
      workloadIdentityPoolId: example-pool
  example:
    type: gcp:iam:WorkloadIdentityPoolProvider
    properties:
      workloadIdentityPoolId: ${pool.workloadIdentityPoolId}
      workloadIdentityPoolProviderId: example-prvdr
      displayName: Name of provider
      description: GitHub Actions identity pool provider for automated test
      disabled: true
      attributeCondition: |2
            assertion.repository_owner_id == \"123456789\" &&
            attribute.repository == \"gh-org/gh-repo\" &&
            assertion.ref == \"refs/heads/main\" &&
            assertion.ref_type == \"branch\"
      attributeMapping:
        google.subject: assertion.sub
        attribute.actor: assertion.actor
        attribute.aud: assertion.aud
        attribute.repository: assertion.repository
      oidc:
        issuerUri: https://token.actions.githubusercontent.com

The oidc block points to GitHub’s token endpoint. Attribute mapping extracts repository and actor information from GitHub’s JWT tokens. The attributeCondition enforces that only workflows from a specific repository owner, repository, and branch can authenticate. This enables keyless deployment from GitHub Actions to GCP.

Connect Azure AD with minimal OIDC configuration

Azure workloads authenticate to GCP using Azure AD’s OIDC endpoint.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const pool = new gcp.iam.WorkloadIdentityPool("pool", {workloadIdentityPoolId: "example-pool"});
const example = new gcp.iam.WorkloadIdentityPoolProvider("example", {
    workloadIdentityPoolId: pool.workloadIdentityPoolId,
    workloadIdentityPoolProviderId: "example-prvdr",
    attributeMapping: {
        "google.subject": "assertion.sub",
    },
    oidc: {
        issuerUri: "https://sts.windows.net/azure-tenant-id",
    },
});
import pulumi
import pulumi_gcp as gcp

pool = gcp.iam.WorkloadIdentityPool("pool", workload_identity_pool_id="example-pool")
example = gcp.iam.WorkloadIdentityPoolProvider("example",
    workload_identity_pool_id=pool.workload_identity_pool_id,
    workload_identity_pool_provider_id="example-prvdr",
    attribute_mapping={
        "google.subject": "assertion.sub",
    },
    oidc={
        "issuer_uri": "https://sts.windows.net/azure-tenant-id",
    })
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 {
		pool, err := iam.NewWorkloadIdentityPool(ctx, "pool", &iam.WorkloadIdentityPoolArgs{
			WorkloadIdentityPoolId: pulumi.String("example-pool"),
		})
		if err != nil {
			return err
		}
		_, err = iam.NewWorkloadIdentityPoolProvider(ctx, "example", &iam.WorkloadIdentityPoolProviderArgs{
			WorkloadIdentityPoolId:         pool.WorkloadIdentityPoolId,
			WorkloadIdentityPoolProviderId: pulumi.String("example-prvdr"),
			AttributeMapping: pulumi.StringMap{
				"google.subject": pulumi.String("assertion.sub"),
			},
			Oidc: &iam.WorkloadIdentityPoolProviderOidcArgs{
				IssuerUri: pulumi.String("https://sts.windows.net/azure-tenant-id"),
			},
		})
		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 pool = new Gcp.Iam.WorkloadIdentityPool("pool", new()
    {
        WorkloadIdentityPoolId = "example-pool",
    });

    var example = new Gcp.Iam.WorkloadIdentityPoolProvider("example", new()
    {
        WorkloadIdentityPoolId = pool.WorkloadIdentityPoolId,
        WorkloadIdentityPoolProviderId = "example-prvdr",
        AttributeMapping = 
        {
            { "google.subject", "assertion.sub" },
        },
        Oidc = new Gcp.Iam.Inputs.WorkloadIdentityPoolProviderOidcArgs
        {
            IssuerUri = "https://sts.windows.net/azure-tenant-id",
        },
    });

});
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.WorkloadIdentityPoolProvider;
import com.pulumi.gcp.iam.WorkloadIdentityPoolProviderArgs;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolProviderOidcArgs;
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 pool = new WorkloadIdentityPool("pool", WorkloadIdentityPoolArgs.builder()
            .workloadIdentityPoolId("example-pool")
            .build());

        var example = new WorkloadIdentityPoolProvider("example", WorkloadIdentityPoolProviderArgs.builder()
            .workloadIdentityPoolId(pool.workloadIdentityPoolId())
            .workloadIdentityPoolProviderId("example-prvdr")
            .attributeMapping(Map.of("google.subject", "assertion.sub"))
            .oidc(WorkloadIdentityPoolProviderOidcArgs.builder()
                .issuerUri("https://sts.windows.net/azure-tenant-id")
                .build())
            .build());

    }
}
resources:
  pool:
    type: gcp:iam:WorkloadIdentityPool
    properties:
      workloadIdentityPoolId: example-pool
  example:
    type: gcp:iam:WorkloadIdentityPoolProvider
    properties:
      workloadIdentityPoolId: ${pool.workloadIdentityPoolId}
      workloadIdentityPoolProviderId: example-prvdr
      attributeMapping:
        google.subject: assertion.sub
      oidc:
        issuerUri: https://sts.windows.net/azure-tenant-id

The issuerUri points to Azure AD’s token endpoint for your tenant. The attributeMapping maps Azure’s standard subject claim (assertion.sub) to the GCP principal identifier (google.subject). This minimal configuration enables Azure managed identities to access GCP resources.

Map Azure managed identities with custom attributes

Azure managed identities often need meaningful names in GCP IAM policies rather than opaque object IDs.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const pool = new gcp.iam.WorkloadIdentityPool("pool", {workloadIdentityPoolId: "example-pool"});
const example = new gcp.iam.WorkloadIdentityPoolProvider("example", {
    workloadIdentityPoolId: pool.workloadIdentityPoolId,
    workloadIdentityPoolProviderId: "example-prvdr",
    displayName: "Name of provider",
    description: "OIDC identity pool provider for automated test",
    disabled: true,
    attributeCondition: "\"e968c2ef-047c-498d-8d79-16ca1b61e77e\" in assertion.groups",
    attributeMapping: {
        "google.subject": "\"azure::\" + assertion.tid + \"::\" + assertion.sub",
        "attribute.tid": "assertion.tid",
        "attribute.managed_identity_name": `      {
        \\"8bb39bdb-1cc5-4447-b7db-a19e920eb111\\":\\"workload1\\",
        \\"55d36609-9bcf-48e0-a366-a3cf19027d2a\\":\\"workload2\\"
      }[assertion.oid]
`,
    },
    oidc: {
        allowedAudiences: [
            "https://example.com/gcp-oidc-federation",
            "example.com/gcp-oidc-federation",
        ],
        issuerUri: "https://sts.windows.net/azure-tenant-id",
    },
});
import pulumi
import pulumi_gcp as gcp

pool = gcp.iam.WorkloadIdentityPool("pool", workload_identity_pool_id="example-pool")
example = gcp.iam.WorkloadIdentityPoolProvider("example",
    workload_identity_pool_id=pool.workload_identity_pool_id,
    workload_identity_pool_provider_id="example-prvdr",
    display_name="Name of provider",
    description="OIDC identity pool provider for automated test",
    disabled=True,
    attribute_condition="\"e968c2ef-047c-498d-8d79-16ca1b61e77e\" in assertion.groups",
    attribute_mapping={
        "google.subject": "\"azure::\" + assertion.tid + \"::\" + assertion.sub",
        "attribute.tid": "assertion.tid",
        "attribute.managed_identity_name": """      {
        \"8bb39bdb-1cc5-4447-b7db-a19e920eb111\":\"workload1\",
        \"55d36609-9bcf-48e0-a366-a3cf19027d2a\":\"workload2\"
      }[assertion.oid]
""",
    },
    oidc={
        "allowed_audiences": [
            "https://example.com/gcp-oidc-federation",
            "example.com/gcp-oidc-federation",
        ],
        "issuer_uri": "https://sts.windows.net/azure-tenant-id",
    })
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 {
		pool, err := iam.NewWorkloadIdentityPool(ctx, "pool", &iam.WorkloadIdentityPoolArgs{
			WorkloadIdentityPoolId: pulumi.String("example-pool"),
		})
		if err != nil {
			return err
		}
		_, err = iam.NewWorkloadIdentityPoolProvider(ctx, "example", &iam.WorkloadIdentityPoolProviderArgs{
			WorkloadIdentityPoolId:         pool.WorkloadIdentityPoolId,
			WorkloadIdentityPoolProviderId: pulumi.String("example-prvdr"),
			DisplayName:                    pulumi.String("Name of provider"),
			Description:                    pulumi.String("OIDC identity pool provider for automated test"),
			Disabled:                       pulumi.Bool(true),
			AttributeCondition:             pulumi.String("\"e968c2ef-047c-498d-8d79-16ca1b61e77e\" in assertion.groups"),
			AttributeMapping: pulumi.StringMap{
				"google.subject":                  pulumi.String("\"azure::\" + assertion.tid + \"::\" + assertion.sub"),
				"attribute.tid":                   pulumi.String("assertion.tid"),
				"attribute.managed_identity_name": pulumi.String("      {\n        \\\"8bb39bdb-1cc5-4447-b7db-a19e920eb111\\\":\\\"workload1\\\",\n        \\\"55d36609-9bcf-48e0-a366-a3cf19027d2a\\\":\\\"workload2\\\"\n      }[assertion.oid]\n"),
			},
			Oidc: &iam.WorkloadIdentityPoolProviderOidcArgs{
				AllowedAudiences: pulumi.StringArray{
					pulumi.String("https://example.com/gcp-oidc-federation"),
					pulumi.String("example.com/gcp-oidc-federation"),
				},
				IssuerUri: pulumi.String("https://sts.windows.net/azure-tenant-id"),
			},
		})
		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 pool = new Gcp.Iam.WorkloadIdentityPool("pool", new()
    {
        WorkloadIdentityPoolId = "example-pool",
    });

    var example = new Gcp.Iam.WorkloadIdentityPoolProvider("example", new()
    {
        WorkloadIdentityPoolId = pool.WorkloadIdentityPoolId,
        WorkloadIdentityPoolProviderId = "example-prvdr",
        DisplayName = "Name of provider",
        Description = "OIDC identity pool provider for automated test",
        Disabled = true,
        AttributeCondition = "\"e968c2ef-047c-498d-8d79-16ca1b61e77e\" in assertion.groups",
        AttributeMapping = 
        {
            { "google.subject", "\"azure::\" + assertion.tid + \"::\" + assertion.sub" },
            { "attribute.tid", "assertion.tid" },
            { "attribute.managed_identity_name", @"      {
        \""8bb39bdb-1cc5-4447-b7db-a19e920eb111\"":\""workload1\"",
        \""55d36609-9bcf-48e0-a366-a3cf19027d2a\"":\""workload2\""
      }[assertion.oid]
" },
        },
        Oidc = new Gcp.Iam.Inputs.WorkloadIdentityPoolProviderOidcArgs
        {
            AllowedAudiences = new[]
            {
                "https://example.com/gcp-oidc-federation",
                "example.com/gcp-oidc-federation",
            },
            IssuerUri = "https://sts.windows.net/azure-tenant-id",
        },
    });

});
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.WorkloadIdentityPoolProvider;
import com.pulumi.gcp.iam.WorkloadIdentityPoolProviderArgs;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolProviderOidcArgs;
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 pool = new WorkloadIdentityPool("pool", WorkloadIdentityPoolArgs.builder()
            .workloadIdentityPoolId("example-pool")
            .build());

        var example = new WorkloadIdentityPoolProvider("example", WorkloadIdentityPoolProviderArgs.builder()
            .workloadIdentityPoolId(pool.workloadIdentityPoolId())
            .workloadIdentityPoolProviderId("example-prvdr")
            .displayName("Name of provider")
            .description("OIDC identity pool provider for automated test")
            .disabled(true)
            .attributeCondition("\"e968c2ef-047c-498d-8d79-16ca1b61e77e\" in assertion.groups")
            .attributeMapping(Map.ofEntries(
                Map.entry("google.subject", "\"azure::\" + assertion.tid + \"::\" + assertion.sub"),
                Map.entry("attribute.tid", "assertion.tid"),
                Map.entry("attribute.managed_identity_name", """
      {
        \"8bb39bdb-1cc5-4447-b7db-a19e920eb111\":\"workload1\",
        \"55d36609-9bcf-48e0-a366-a3cf19027d2a\":\"workload2\"
      }[assertion.oid]
                """)
            ))
            .oidc(WorkloadIdentityPoolProviderOidcArgs.builder()
                .allowedAudiences(                
                    "https://example.com/gcp-oidc-federation",
                    "example.com/gcp-oidc-federation")
                .issuerUri("https://sts.windows.net/azure-tenant-id")
                .build())
            .build());

    }
}
resources:
  pool:
    type: gcp:iam:WorkloadIdentityPool
    properties:
      workloadIdentityPoolId: example-pool
  example:
    type: gcp:iam:WorkloadIdentityPoolProvider
    properties:
      workloadIdentityPoolId: ${pool.workloadIdentityPoolId}
      workloadIdentityPoolProviderId: example-prvdr
      displayName: Name of provider
      description: OIDC identity pool provider for automated test
      disabled: true
      attributeCondition: '"e968c2ef-047c-498d-8d79-16ca1b61e77e" in assertion.groups'
      attributeMapping:
        google.subject: '"azure::" + assertion.tid + "::" + assertion.sub'
        attribute.tid: assertion.tid
        attribute.managed_identity_name: |2
                {
                  \"8bb39bdb-1cc5-4447-b7db-a19e920eb111\":\"workload1\",
                  \"55d36609-9bcf-48e0-a366-a3cf19027d2a\":\"workload2\"
                }[assertion.oid]
      oidc:
        allowedAudiences:
          - https://example.com/gcp-oidc-federation
          - example.com/gcp-oidc-federation
        issuerUri: https://sts.windows.net/azure-tenant-id

Custom attribute mapping translates Azure object IDs into human-readable workload names using a lookup table. The google.subject combines tenant ID and subscription ID for uniqueness. The attributeCondition filters by Azure AD group membership. The allowedAudiences property restricts which token audiences are accepted, preventing token reuse across different systems.

Trust X.509 certificates from a root CA

On-premises workloads or IoT devices often use X.509 certificates for authentication.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const pool = new gcp.iam.WorkloadIdentityPool("pool", {workloadIdentityPoolId: "example-pool"});
const example = new gcp.iam.WorkloadIdentityPoolProvider("example", {
    workloadIdentityPoolId: pool.workloadIdentityPoolId,
    workloadIdentityPoolProviderId: "example-prvdr",
    attributeMapping: {
        "google.subject": "assertion.subject.dn.cn",
    },
    x509: {
        trustStore: {
            trustAnchors: [{
                pemCertificate: std.file({
                    input: "test-fixtures/trust_anchor.pem",
                }).then(invoke => invoke.result),
            }],
        },
    },
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

pool = gcp.iam.WorkloadIdentityPool("pool", workload_identity_pool_id="example-pool")
example = gcp.iam.WorkloadIdentityPoolProvider("example",
    workload_identity_pool_id=pool.workload_identity_pool_id,
    workload_identity_pool_provider_id="example-prvdr",
    attribute_mapping={
        "google.subject": "assertion.subject.dn.cn",
    },
    x509={
        "trust_store": {
            "trust_anchors": [{
                "pem_certificate": std.file(input="test-fixtures/trust_anchor.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 {
		pool, err := iam.NewWorkloadIdentityPool(ctx, "pool", &iam.WorkloadIdentityPoolArgs{
			WorkloadIdentityPoolId: pulumi.String("example-pool"),
		})
		if err != nil {
			return err
		}
		invokeFile, err := std.File(ctx, &std.FileArgs{
			Input: "test-fixtures/trust_anchor.pem",
		}, nil)
		if err != nil {
			return err
		}
		_, err = iam.NewWorkloadIdentityPoolProvider(ctx, "example", &iam.WorkloadIdentityPoolProviderArgs{
			WorkloadIdentityPoolId:         pool.WorkloadIdentityPoolId,
			WorkloadIdentityPoolProviderId: pulumi.String("example-prvdr"),
			AttributeMapping: pulumi.StringMap{
				"google.subject": pulumi.String("assertion.subject.dn.cn"),
			},
			X509: &iam.WorkloadIdentityPoolProviderX509Args{
				TrustStore: &iam.WorkloadIdentityPoolProviderX509TrustStoreArgs{
					TrustAnchors: iam.WorkloadIdentityPoolProviderX509TrustStoreTrustAnchorArray{
						&iam.WorkloadIdentityPoolProviderX509TrustStoreTrustAnchorArgs{
							PemCertificate: pulumi.String(invokeFile.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 pool = new Gcp.Iam.WorkloadIdentityPool("pool", new()
    {
        WorkloadIdentityPoolId = "example-pool",
    });

    var example = new Gcp.Iam.WorkloadIdentityPoolProvider("example", new()
    {
        WorkloadIdentityPoolId = pool.WorkloadIdentityPoolId,
        WorkloadIdentityPoolProviderId = "example-prvdr",
        AttributeMapping = 
        {
            { "google.subject", "assertion.subject.dn.cn" },
        },
        X509 = new Gcp.Iam.Inputs.WorkloadIdentityPoolProviderX509Args
        {
            TrustStore = new Gcp.Iam.Inputs.WorkloadIdentityPoolProviderX509TrustStoreArgs
            {
                TrustAnchors = new[]
                {
                    new Gcp.Iam.Inputs.WorkloadIdentityPoolProviderX509TrustStoreTrustAnchorArgs
                    {
                        PemCertificate = Std.File.Invoke(new()
                        {
                            Input = "test-fixtures/trust_anchor.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.WorkloadIdentityPoolProvider;
import com.pulumi.gcp.iam.WorkloadIdentityPoolProviderArgs;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolProviderX509Args;
import com.pulumi.gcp.iam.inputs.WorkloadIdentityPoolProviderX509TrustStoreArgs;
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 pool = new WorkloadIdentityPool("pool", WorkloadIdentityPoolArgs.builder()
            .workloadIdentityPoolId("example-pool")
            .build());

        var example = new WorkloadIdentityPoolProvider("example", WorkloadIdentityPoolProviderArgs.builder()
            .workloadIdentityPoolId(pool.workloadIdentityPoolId())
            .workloadIdentityPoolProviderId("example-prvdr")
            .attributeMapping(Map.of("google.subject", "assertion.subject.dn.cn"))
            .x509(WorkloadIdentityPoolProviderX509Args.builder()
                .trustStore(WorkloadIdentityPoolProviderX509TrustStoreArgs.builder()
                    .trustAnchors(WorkloadIdentityPoolProviderX509TrustStoreTrustAnchorArgs.builder()
                        .pemCertificate(StdFunctions.file(FileArgs.builder()
                            .input("test-fixtures/trust_anchor.pem")
                            .build()).result())
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  pool:
    type: gcp:iam:WorkloadIdentityPool
    properties:
      workloadIdentityPoolId: example-pool
  example:
    type: gcp:iam:WorkloadIdentityPoolProvider
    properties:
      workloadIdentityPoolId: ${pool.workloadIdentityPoolId}
      workloadIdentityPoolProviderId: example-prvdr
      attributeMapping:
        google.subject: assertion.subject.dn.cn
      x509:
        trustStore:
          trustAnchors:
            - pemCertificate:
                fn::invoke:
                  function: std:file
                  arguments:
                    input: test-fixtures/trust_anchor.pem
                  return: result

The x509 block defines a trust store with root certificate anchors. The trustAnchors array contains PEM-encoded certificates that serve as trust roots. The attributeMapping extracts the Common Name from the certificate’s Distinguished Name (assertion.subject.dn.cn) and maps it to the GCP principal. Any certificate that chains to these trust anchors can authenticate.

Beyond these examples

These snippets focus on specific provider-level features: AWS, Azure, and GitHub OIDC federation, attribute mapping and conditional access, and X.509 certificate-based authentication. They’re intentionally minimal rather than complete federation configurations.

The examples reference pre-existing infrastructure such as WorkloadIdentityPool resources, external identity provider configurations (AWS accounts, Azure tenants, GitHub repos), and certificate files for X.509 providers. They focus on configuring the provider rather than provisioning the surrounding IAM infrastructure.

To keep things focused, common provider patterns are omitted, including:

  • SAML 2.0 identity providers (saml block)
  • Custom JWKS upload for OIDC (jwksJson)
  • Intermediate CAs for X.509 trust chains
  • IAM policy bindings to grant actual resource access

These omissions are intentional: the goal is to illustrate how each provider type is wired, not provide drop-in federation modules. See the Workload Identity Pool Provider resource reference for all available configuration options.

Let's configure GCP Workload Identity Pool Providers

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Setup
Can I use multiple provider types in one provider?
No, the provider types (aws, oidc, saml, x509) are mutually exclusive. You must choose exactly one provider type per WorkloadIdentityPoolProvider resource.
What are the naming requirements for provider IDs?
Both workloadIdentityPoolId and workloadIdentityPoolProviderId must be 4-32 characters, contain only [a-z0-9-], and cannot use the gcp- prefix (reserved for Google).
What properties are immutable after creation?
The workloadIdentityPoolId, workloadIdentityPoolProviderId, and project properties cannot be changed after creation. Modifying these requires recreating the resource.
Attribute Mapping & Identity Federation
Do I need to configure attributeMapping for all provider types?
It depends on the provider type. For OIDC providers, custom attributeMapping is required and must include google.subject. For AWS providers, a default mapping is applied if none is defined, but custom mappings must also include google.subject.
How does attributeMapping differ between AWS and OIDC providers?
AWS providers have a default mapping if none is defined (mapping assertion.arn to google.subject). OIDC providers require you to explicitly define custom mappings, and they must include google.subject (e.g., {"google.subject": "assertion.sub"}).
What are the limits for custom attributes in attributeMapping?
You can define up to 50 custom attributes. Attribute keys can be up to 100 characters (only [a-z0-9_]), expressions can be up to 2048 characters, and the total size of all mapped attributes must not exceed 8KB.
How do I configure a GitHub Actions OIDC provider?
Set oidc.issuerUri to https://token.actions.githubusercontent.com and define attributeMapping with google.subject mapped to assertion.sub. You can also map GitHub-specific claims like assertion.actor and assertion.repository.
Provider Lifecycle & Behavior
What happens when I disable a provider?
Disabling a provider prevents new token exchanges, but existing tokens continue to grant access until they expire.
Advanced Configuration
How do I use SAML with a metadata XML file?
Configure the saml property with idpMetadataXml, loading the XML content from a file using std.file() (e.g., std.file({input: "metadata.xml"}).then(invoke => invoke.result)).
Can I manually upload JWKS keys for OIDC providers?
Yes, use the oidc.jwksJson property to provide the JSON Web Key Set directly instead of relying on the issuer’s JWKS endpoint.

Using a different cloud?

Explore security guides for other cloud providers: