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 without service account keys. This guide focuses on three capabilities: AWS IAM role federation, OIDC provider configuration for Azure and GitHub, and X.509 certificate-based authentication.

Providers belong to WorkloadIdentityPool resources and reference external identity systems such as AWS accounts, Azure tenants, or certificate authorities. The examples are intentionally small. Combine them with IAM policy bindings to grant actual access to GCP resources.

Connect AWS workloads with minimal configuration

Teams migrating from AWS often need to grant AWS services access to GCP resources without managing 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 presents credentials, GCP validates the account ID matches before issuing a token.

Filter AWS credentials 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'

The attributeMapping block extracts claims from AWS credentials and maps them to GCP attributes. The google.subject mapping determines the principal identifier in IAM bindings. The attributeCondition filters credentials, accepting only those from a specific assumed role. Custom attributes like attribute.environment enable conditional access based on AWS resource paths.

Authenticate GitHub Actions workflows to GCP

CI/CD pipelines in GitHub Actions can deploy to GCP using OIDC tokens instead of 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 configures GitHub’s token endpoint as the issuer. The attributeCondition restricts access to a specific repository owner, repository, and branch. The attributeMapping extracts GitHub-specific claims like actor and repository for audit logging and conditional access.

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, with the tenant ID embedded in the URL. The attributeMapping maps Azure’s standard sub claim to google.subject. This minimal configuration enables Azure managed identities to exchange tokens for GCP credentials.

Map Azure managed identities with custom attributes

Azure deployments with multiple managed identities need to extract Azure-specific claims for access control.

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

The allowedAudiences property restricts which token audiences are accepted. The attributeMapping extracts Azure tenant ID (tid) and object ID (oid), then uses a dictionary lookup to map object IDs to human-readable workload names. The attributeCondition checks group membership before accepting credentials.

Trust X.509 certificates from a root CA

On-premises workloads with PKI infrastructure can authenticate using X.509 certificates.

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 CA certificates. The trustAnchors array lists PEM-encoded certificates that anchor the trust chain. The attributeMapping extracts the common name from the certificate’s distinguished name as the subject. GCP validates that presented certificates chain to these trust anchors before issuing tokens.

Beyond these examples

These snippets focus on specific provider-level features: AWS, OIDC, SAML, and X.509 identity providers, attribute mapping and conditional access, and audience restrictions and custom claims. They’re intentionally minimal rather than full 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 and SAML providers. They focus on configuring the provider rather than provisioning IAM bindings or service accounts.

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

  • IAM policy bindings to grant provider access to GCP resources
  • Service account impersonation configuration
  • JWKS JSON configuration for custom OIDC providers
  • Intermediate CA certificates for X.509 trust chains

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

Provider Configuration & Types
Can I use multiple provider types on the same resource?
No, the provider types (aws, oidc, saml, x509) are mutually exclusive. You must choose exactly one provider type per WorkloadIdentityPoolProvider resource.
How do I configure a provider for GitHub Actions?
Use the oidc property with issuerUri set to https://token.actions.githubusercontent.com. You can add attributeCondition to restrict access by repository owner, branch, or other assertion fields.
Can I manually upload JWKS keys instead of using the issuer's endpoint?
Yes, use the oidc.jwksJson property to provide your JSON Web Key Set directly instead of relying on automatic discovery.
Attribute Mapping Requirements
Why is my OIDC provider failing to authenticate?
OIDC providers require custom attributeMapping that includes a mapping to google.subject. For example: {"google.subject": "assertion.sub"}. This is mandatory for OIDC providers.
Do I need to configure attributeMapping for AWS providers?
No, AWS providers have a default attributeMapping that maps assertion.arn to google.subject. However, if you define custom mappings, you must include google.subject.
What are the size limits for attribute mappings?
You can define up to 50 custom attributes. Attribute keys have a 100-character limit and can only contain [a-z0-9_]. Each mapping expression has a 2048-character limit, and the total size of all mapped attributes must not exceed 8KB when evaluated.
Immutability & Lifecycle
What properties can't I change after creation?
The workloadIdentityPoolId, workloadIdentityPoolProviderId, and project properties are immutable. Changing these requires recreating the resource.
What happens when I disable a provider?
Disabled providers cannot exchange new tokens, but existing tokens still grant access. You can re-enable a provider by setting disabled to false.
Naming & Character Limits
Why can't I use the 'gcp-' prefix in my provider ID?
The gcp- prefix is reserved for use by Google and cannot be specified in workloadIdentityPoolId or workloadIdentityPoolProviderId.
What are the character limits for provider properties?
Provider IDs must be 4-32 characters using [a-z0-9-]. The displayName has a 32-character limit, description has a 256-character limit, and google.subject cannot exceed 127 characters.

Using a different cloud?

Explore security guides for other cloud providers: