GitHub Actions can deploy to AWS, Azure, or Google Cloud without keeping a reusable cloud access key in GitHub secrets. With OpenID Connect (OIDC), a workflow requests a signed identity token from GitHub, and the cloud provider exchanges it for short-lived credentials after checking that the workflow meets a trust policy.
That exchange is only one part of deployment security. Your workflow must be allowed to request a token, the cloud role must trust only the intended repository and deployment context, and the role's own permissions must be limited to the resources it needs.
This guide shows an AWS example and explains what changes for Azure and Google Cloud. It uses official documentation rather than claiming a live cloud deployment was tested. If your workflow also manages infrastructure as code, our guide to using GitHub Actions with Terraform covers the broader CI workflow.
How the OIDC exchange works
Without federation, a workflow often uses a long-lived cloud key stored as a repository or environment secret. Anyone who can expose that secret may be able to reuse it until it is rotated or revoked.
With OIDC, the workflow asks GitHub for a signed token that identifies the repository and the context that started the job. The cloud identity provider validates the token and its claims, then grants temporary credentials for an identity such as an AWS IAM role. No cloud access key needs to be copied into GitHub.
The token permission and cloud permissions are separate:
id-token: writelets the job request an OIDC token. It does not grant the job permission to change cloud resources.- The cloud provider's trust policy decides which GitHub identities may assume an identity.
- The cloud role's permission policy decides what an authenticated workflow may do.
Grant only the token permission to deployment jobs, and keep the cloud role narrow. A workflow that can request a token should not automatically be trusted by your cloud account.
Choose the workflow identity before configuring the cloud
Decide which repository, branch, tag, or protected environment should be allowed to deploy. GitHub places that context in the token's sub (subject) claim. The exact value matters: your cloud provider's trust rule must match the subject GitHub issues for your job.
For repositories using the older default subject format, common values look like this:
| Workflow context | Subject format |
|---|---|
Push from the main branch, with no GitHub environment | repo:OWNER/REPOSITORY:ref:refs/heads/main |
Job that references the production environment | repo:OWNER/REPOSITORY:environment:production |
Check the format for your repository before copying an example. GitHub repositories created after July 15, 2026 use an immutable default subject that includes owner and repository IDs. Older repositories keep the previous format unless they opt in; a rename or transfer after that date also changes to the immutable format. The corresponding subjects look like repo:OWNER@OWNER_ID/REPOSITORY@REPOSITORY_ID:ref:refs/heads/main or repo:OWNER@OWNER_ID/REPOSITORY@REPOSITORY_ID:environment:production. Organization or repository customizations can change the subject too. See GitHub's OIDC claims reference and configure the trust policy to match the format your workflow actually uses.
When a job references an environment, its subject identifies the environment rather than the branch. Add environment deployment rules that restrict which branches or tags can deploy; a subject ending in environment:production does not, by itself, mean that only main can reach that environment. Required approvals can add a human checkpoint where your repository plan and visibility support them.
Configure an AWS role for GitHub Actions
For AWS, create an IAM OIDC identity provider for https://token.actions.githubusercontent.com with the audience sts.amazonaws.com, then create a role whose trust policy restricts both the audience and subject. AWS recommends checking the sub condition for roles that trust GitHub's provider.
This example uses the immutable subject format and a production environment. Replace each sample value with values from your AWS account and GitHub repository. If your repository still uses the older subject format, remove the @OWNER_ID and @REPOSITORY_ID portions in the subject; if you customized the subject, use that exact value instead.
{
"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",
"token.actions.githubusercontent.com:sub": "repo:OWNER@OWNER_ID/REPOSITORY@REPOSITORY_ID:environment:production"
}
}
}
]
}
The role also needs an IAM permissions policy for the deployment itself. Give it only the actions and resources the deployment needs; a restrictive trust policy does not make an all-powerful role safe.
Add token permission to the deployment job and use the AWS credentials action to exchange the token:
name: Deploy to AWS
on:
push:
branches: [main]
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsProductionDeploy
aws-region: us-east-1
- name: Deploy
run: ./scripts/deploy.sh
The workflow's environment name must match the subject in the role trust policy. Configure that GitHub environment to allow the intended deployment branches. The example uses major action tags for readability; for production, review third-party actions and pin them to full commit SHAs so a tag change cannot silently change the code you run.
What changes for Azure and Google Cloud?
The same GitHub job permission is needed, but each provider has its own federation setup and credential action:
- Azure: Create a federated identity credential for the app registration or managed identity, matching the GitHub issuer and your repository's subject. Use the official
azure/loginaction with the identity's client, tenant, and subscription identifiers. Follow Microsoft's GitHub Actions OIDC setup; do not substitute a client secret for the federated credential. - Google Cloud: Configure Workload Identity Federation with a provider that trusts GitHub and restricts which repository and deployment context may impersonate a service account. Use Google's
google-github-actions/authaction and the official Workload Identity Federation deployment-pipeline guide.
Use each provider's current GitHub guide for its audience, claim mapping, and trust-policy syntax. The claim values and configuration formats are not interchangeable between AWS, Azure, and Google Cloud.
Keep deployment access narrow
Before using OIDC in production:
- Scope the trust to one identity. Prefer an exact repository and branch or environment subject over a wildcard that admits unrelated repositories or refs.
- Protect the deployment environment. If using an environment subject, restrict eligible branches and tags in the environment settings. Add an approval step for production when it fits your release process.
- Keep permissions at job scope. Give
id-token: writeonly to the job that needs cloud credentials. Set the other required permissions explicitly; for example,contents: readis enough for a job that only checks out code. - Separate untrusted checks from deployment. Do not let pull-request code from forks reach a trusted production role. Run untrusted validation separately from the job that assumes the cloud identity.
- Pin and review actions. A trusted workflow can still be compromised by an action that runs in the job. Review action source and permissions, and pin actions to reviewed full commit SHAs.
- Test with a limited role first. Confirm which identity is assumed and verify that a deployment can reach only its intended resources. Remove old static credentials after the federated deployment is verified.
Troubleshoot common failures
- The job cannot request an OIDC token: Check that
id-token: writeis set on the job or workflow and that the action requesting the token runs in that job. - The provider rejects the token: Compare the configured issuer, audience, and exact
subclaim with the token format for your repository. Check for a branch-versus-environment mismatch, a customized subject, or the immutable ID format. - Authentication succeeds but deployment returns access denied: The trust exchange may have worked, but the cloud role's permission policy or a resource policy may not allow the requested operation.
- A protected environment is unexpectedly available to another branch: Review the environment's deployment branch rules and approval requirements. An environment name in the OIDC subject is not a replacement for configuring those rules.
OIDC removes the need for a reusable cloud credential in GitHub; it does not remove the need to secure the workflow, its actions, the environment, or the cloud role. Start with a narrow deployment job, verify the identity and resource permissions, and then remove the static key once the replacement works.
Official sources
- GitHub's OpenID Connect overview and security guidance
- GitHub's OIDC claims reference
- GitHub's guide to configuring OIDC in AWS
- AWS IAM guide to creating an OpenID Connect identity provider
- AWS credentials action documentation
- GitHub's guide to configuring OIDC in Azure
- GitHub's guide to configuring OIDC in Google Cloud