1. Docs
  2. Pulumi Cloud
  3. OpenID
  4. OpenID Client
  5. Google Kubernetes Engine

Configuring OpenID Connect for Google Kubernetes Engine

    This document outlines the steps required to configure Pulumi to accept Google Kubernetes Engine id_tokens to be exchanged for Organization access tokens. With this configuration, Kubernetes pods authenticate to Pulumi Cloud using OIDC tokens issued by GKE.

    See “Bound Tokens” for more background.


    • You must be an admin of your Pulumi organization.
    • You must have a GKE cluster.
    Please note that this guide provides step-by-step instructions based on the official provider documentation which is subject to change. For the most current and precise information, always refer to the official Bound Service Account Tokens documentation.

    Register the OIDC issuer

    1. Navigate to OIDC Issuers under your Organization’s Settings and click on Register a new issuer.
    2. Name the issuer and complete the url: https://container.googleapis.com/v1/projects/PROJECT_NAME/locations/LOCATION/clusters/CLUSTER_NAME/ Register GKE
    3. Submit the form

    Configure the Authorization Policies

    1. Click on the issuer name
    2. Change the policy decision to Allow
    3. Change the token type to Organization
    4. Add a new rule and configure it to verify namespace and the service name: kubernetes policy example
    5. Click on update


    // Pulumi program to run a bash script in a Kubernetes pod,
    // mount a service account token with an appropriate audience,
    // exchange the token for an ordinary Pulumi access token,
    // then run `pulumi whoami`.
    import * as kubernetes from "@pulumi/kubernetes";
    const tokenParams = {
        "audience": "urn:pulumi:org:ORG_NAME",
        "token_type": "urn:pulumi:token-type:access_token:organization",
        "expiration": 2 * 60 * 60,
    const script = new kubernetes.core.v1.ConfigMap("script", {
        data: {
            "entrypoint.sh": `#!/bin/bash
    apt -qq install -y jq
    echo "OIDC Token:"
    echo $OIDC_GH_TOKEN
    export PULUMI_ACCESS_TOKEN=$(curl -sS -X POST  \
        -H 'Content-Type: application/x-www-form-urlencoded' \
        -d 'audience=${tokenParams.audience}' \
        -d 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
        -d 'subject_token_type=urn:ietf:params:oauth:token-type:id_token' \
        -d 'requested_token_type=${tokenParams.token_type}' \
        -d 'expiration=${tokenParams.expiration}' \
        -d 'scope=${tokenParams.scope}' \
        -d "subject_token=$OIDC_GH_TOKEN" \
        https://api.pulumi.com/api/oauth/token | jq -r '.access_token')
    echo "Access Token:"
    pulumi whoami
    const job = new kubernetes.batch.v1.Job("runner", {
        metadata: {
        spec: {
            template: {
                spec: {
                    containers: [{
                        name: "runner",
                        image: "pulumi/pulumi:latest",
                        command: ["/bin/entrypoint.sh"],
                        volumeMounts: [
                                name: "pulumi-serviceaccounttoken",
                                mountPath: "/var/run/secrets/pulumi",
                                name: "script",
                                mountPath: "/bin/entrypoint.sh",
                                readOnly: true,
                                subPath: "entrypoint.sh",
                    restartPolicy: "Never",
                    volumes: [
                            name: "pulumi-serviceaccounttoken",
                            projected: {
                                sources: [
                                        serviceAccountToken: {
                                            audience: "urn:pulumi:org:ORG_NAME",
                                            expirationSeconds: 3600,
                                            path: "token",
                            name: "script",
                            configMap: {
                                defaultMode: 0o700,
                                name: script.metadata.name,
            backoffLimit: 0,
    export const jobName = job.metadata.name;
      Introducing Drift Detection, TTL Stacks, and Scheduled Deployments. Learn More.