OIDC is configured in two places: on the cloud provider side, where you create the trust link, and on the workflow side, which requests the token. This guide covers both for AWS, Azure and GCP.
What you will learn
- Create the OIDC identity provider on AWS, Azure and GCP
- Configure the role or federated identity that trusts GitHub
- Restrict the trust policy to the right repository and the right branch
- Write the workflow that requests and uses the token
- Choose between console, CLI and Terraform for each step
If how OIDC works is not familiar yet, start with OIDC: authentication without secrets.
AWS
On AWS, OIDC rests on an IAM OIDC provider and an IAM role whose trust policy trusts GitHub tokens. Setting it up takes four steps.
Step 1: create the identity provider
The identity provider declares GitHub as a trusted issuer to IAM.
- Go to IAM > Identity providers > Add provider
- Select OpenID Connect
- Provider URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com - Click Add provider
resource "aws_iam_openid_connect_provider" "github" { url = "https://token.actions.githubusercontent.com" client_id_list = ["sts.amazonaws.com"] thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]}aws iam create-open-id-connect-provider \ --url https://token.actions.githubusercontent.com \ --client-id-list sts.amazonaws.com \ --thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1Step 2: create the IAM role
The IAM role carries the permissions granted to the workflow and accepts being assumed through Web Identity.
- Go to IAM > Roles > Create role
- Select Web identity
- Identity provider:
token.actions.githubusercontent.com - Audience:
sts.amazonaws.com - Add the permissions you need
- Name the role (for example
github-actions-deploy)
data "aws_iam_policy_document" "github_actions_trust" { statement { actions = ["sts:AssumeRoleWithWebIdentity"] effect = "Allow"
principals { type = "Federated" identifiers = [aws_iam_openid_connect_provider.github.arn] }
condition { test = "StringEquals" variable = "token.actions.githubusercontent.com:aud" values = ["sts.amazonaws.com"] }
condition { test = "StringLike" variable = "token.actions.githubusercontent.com:sub" values = ["repo:owner/repo:*"] } }}
resource "aws_iam_role" "github_actions" { name = "github-actions-deploy" assume_role_policy = data.aws_iam_policy_document.github_actions_trust.json}
resource "aws_iam_role_policy_attachment" "s3_access" { role = aws_iam_role.github_actions.name # Example: in production, narrow the scope with a dedicated policy policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"}Step 3: configure the trust policy
The trust policy restricts who may assume the role. The sub condition is
the most important one: it bounds the repository, the branch or the
environment.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike": { "token.actions.githubusercontent.com:sub": "repo:owner/repo:ref:refs/heads/main" } } } ]}Restriction patterns:
| Pattern | Allows |
|---|---|
repo:owner/repo:* | Every branch and pull request |
repo:owner/repo:ref:refs/heads/main | The main branch only |
repo:owner/repo:environment:production | The production environment |
repo:owner/*:* | Every repository of the organisation |
Step 4: the GitHub workflow
On the GitHub side, the workflow requests the token with id-token: write and
presents it to AWS through the official action.
name: Deploy to AWS
on: push: branches: [main]
permissions: id-token: write contents: read
jobs: deploy: runs-on: ubuntu-24.04 steps: - name: Check out the code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Configure the AWS credentials through OIDC uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 with: role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy aws-region: eu-west-1
- name: Sync to S3 run: aws s3 sync ./dist s3://my-bucketAzure
On Azure, OIDC goes through an App Registration and federated credentials that tie a precise GitHub subject to the application.
Step 1: create the App Registration
The App Registration is the application identity the workflow will assume.
- Go to Azure Active Directory > App registrations > New registration
- Name:
github-actions - Click Register
- Note the Application (client) ID and the Directory (tenant) ID
az ad app create --display-name github-actions# Note the appId returnedStep 2: add the federated credentials
The federated credentials declare which GitHub subject (repository, branch, environment) may authenticate as that application.
- In the App Registration, go to Certificates & secrets
- Federated credentials tab, then Add credential
- Scenario: GitHub Actions deploying Azure resources
- Organization: your GitHub organisation
- Repository: your repository
- Entity type:
Branch,Environment, orTag - Based on selection:
main(or the environment name)
# Create the federated credentialaz ad app federated-credential create \ --id <APP_ID> \ --parameters '{ "name": "github-main", "issuer": "https://token.actions.githubusercontent.com", "subject": "repo:owner/repo:ref:refs/heads/main", "audiences": ["api://AzureADTokenExchange"] }'Step 3: assign the permissions
The application needs a service principal and an Azure role at the narrowest possible scope.
# Create a service principalaz ad sp create --id <APP_ID>
# Assign a role (example: Contributor on a resource group)az role assignment create \ --assignee <APP_ID> \ --role Contributor \ --scope /subscriptions/<SUB_ID>/resourceGroups/<RG_NAME>Step 4: the GitHub workflow
The workflow authenticates with azure/login, passing the application
identifiers: no secret, only vars.
name: Deploy to Azure
on: push: branches: [main]
permissions: id-token: write contents: read
jobs: deploy: runs-on: ubuntu-24.04 steps: - name: Check out the code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Log in to Azure through OIDC uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 with: client-id: ${{ vars.AZURE_CLIENT_ID }} tenant-id: ${{ vars.AZURE_TENANT_ID }} subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
- name: Deploy the web app run: az webapp deploy --resource-group my-rg --name my-app --src-path ./distGCP
On GCP, OIDC relies on Workload Identity Federation: a pool, an OIDC provider, and a service account the workflow impersonates.
Step 1: create the Workload Identity Pool
The pool gathers the external identities allowed to reach the GCP project.
- Go to IAM & Admin > Workload Identity Federation
- Click Create Pool
- Name:
github-pool - Click Continue
gcloud iam workload-identity-pools create github-pool \ --location="global" \ --description="GitHub Actions pool" \ --display-name="GitHub Pool"Step 2: add the provider
The OIDC provider links the pool to the GitHub issuer and maps the claims of the JWT onto GCP attributes.
- In the pool, click Add Provider
- Select OpenID Connect (OIDC)
- Provider name:
github-provider - Issuer URL:
https://token.actions.githubusercontent.com - Audiences: Default audience
- Attribute mapping:
google.subject=assertion.subattribute.repository=assertion.repositoryattribute.actor=assertion.actor
gcloud iam workload-identity-pools providers create-oidc github-provider \ --location="global" \ --workload-identity-pool="github-pool" \ --issuer-uri="https://token.actions.githubusercontent.com" \ --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.actor=assertion.actor"Step 3: authorise the service account
The service account carries the GCP permissions; the pool is authorised to impersonate it only for the repository you target.
# Create a service accountgcloud iam service-accounts create github-actions \ --display-name="GitHub Actions"
# Allow the workload identity to impersonate the service accountgcloud iam service-accounts add-iam-policy-binding \ github-actions@PROJECT_ID.iam.gserviceaccount.com \ --role="roles/iam.workloadIdentityUser" \ --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/attribute.repository/owner/repo"
# Grant the service account the permissions it needsgcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:github-actions@PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/storage.admin"Step 4: the GitHub workflow
The workflow obtains a token, authenticates to GCP, then installs the gcloud
CLI for the deployment commands.
name: Deploy to GCP
on: push: branches: [main]
permissions: id-token: write contents: read
jobs: deploy: runs-on: ubuntu-24.04 steps: - name: Check out the code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Authenticate to GCP through OIDC uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 with: workload_identity_provider: projects/123456789/locations/global/workloadIdentityPools/github-pool/providers/github-provider service_account: github-actions@project-id.iam.gserviceaccount.com
- name: Install the gcloud CLI uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
- name: Sync to Cloud Storage run: gsutil rsync -r ./dist gs://my-bucketSummary of the configurations
The three clouds follow the same logic, an identity provider, a trust link, an official action, each with its own terminology.
| Provider | Identity provider | Action |
|---|---|---|
| AWS | IAM OIDC provider | aws-actions/configure-aws-credentials |
| Azure | App Registration plus federated credentials | azure/login |
| GCP | Workload Identity Federation | google-github-actions/auth |
Key points
- OIDC is configured in two places: a trust link on the cloud side, a token request on the workflow side.
- AWS: an IAM OIDC provider plus an IAM role assumable through
sts:AssumeRoleWithWebIdentity. - Azure: an App Registration and federated credentials tied to a precise GitHub subject.
- GCP: a Workload Identity Pool, an OIDC provider and an impersonated service account.
- In all three cases, the trust policy (the
subcondition) restricts which repository, branch or environment is allowed.
Official documentation per provider: AWS, Azure and GCP.
Next steps
- Verifying attestations: checking the provenance of an artefact before deploying it with the cloud rights you have just opened.
- Security checklist: the recap that verifies the move to OIDC left no static credential behind.
- Supply chain attacks on GitHub Actions: what an attacker does with a workflow that holds
id-token: write.