GitHub Actions can automate application builds and deployments to AWS. The usual approach is to configure a workflow that builds the application and then uploads the output to an AWS service such as Amazon S3.
The challenge is authentication. GitHub Actions needs permission to access AWS, but storing long-lived access keys in repository secrets creates an additional security risk. Those credentials must be protected, rotated, and revoked when necessary.
AWS supports OpenID Connect (OIDC), which allows GitHub Actions to request temporary AWS credentials without storing long-lived AWS access keys in GitHub secrets.
In this approach, GitHub provides an identity token, AWS validates it against an IAM role's trust policy, and the workflow receives temporary credentials. The permissions attached to that role determine which AWS resources the workflow can access.
This article explains how to configure OIDC authentication, create an IAM role, and deploy a website to Amazon S3 using GitHub Actions.
How GitHub Actions Connects to AWS Using OIDC
The process involves four steps:
A GitHub Actions workflow requests an OIDC identity token.
AWS Security Token Service validates the token against the configured identity provider and IAM role trust policy.
AWS issues temporary credentials for the IAM role.
The workflow uses those credentials to deploy the application.
Two IAM configurations control this process.
The trust policy determines which GitHub identity can assume the role. The permissions policy determines what that role can do after authentication.
Both policies must be configured correctly. Restricting AWS permissions is not enough if an unintended repository can assume the role.
Step 1: Configure the GitHub OIDC Identity Provider
Open the AWS Management Console and navigate to IAM. Under Identity providers, create an OpenID Connect provider with the following settings:
Provider URL:
https://token.actions.githubusercontent.comAudience:
sts.amazonaws.com
If the provider already exists in your AWS account, you can reuse it.
This configuration allows AWS to validate OIDC tokens issued by GitHub Actions. Creating the provider does not automatically give any repository access to AWS. Access is controlled separately through the IAM role's trust policy.
Step 2: Create an IAM Role for GitHub Actions
Create an IAM role that the deployment workflow can assume.
For this example, assume the GitHub repository is my-org/my-web-app, and deployments should run only from its main branch.
Configure the following trust policy for the role:
{
"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:my-org/my-web-app:ref:refs/heads/main"
}
}
}
]
}Replace 123456789012 with your AWS account ID and my-org/my-web-app with your actual repository name.
The Federated field identifies the GitHub OIDC provider. The Action allows AWS to issue a role session through web identity federation.
The conditions restrict access to tokens with the expected audience and subject. In this example, the subject identifies the specified repository and branch.
If your workflow uses a GitHub Environment, the subject claim may use an environment-based value instead of the branch-based value shown above. Configure the trust policy to match the actual workflow context, and protect the environment with appropriate deployment rules.
Avoid broad trust policies that allow unrelated repositories or branches to assume a production deployment role.
Step 3: Assign Permissions to the IAM Role
The role also needs permission to deploy files to the target S3 bucket.
For this example, assume the bucket is named my-production-web-bucket.
Attach a policy such as the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListDeploymentBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-production-web-bucket"
},
{
"Sid": "ManageDeploymentObjects",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-production-web-bucket/*"
}
]
}Replace the example bucket name with your deployment bucket.
The policy separates bucket-level and object-level permissions because S3 uses different resource ARNs for these operations.
s3:ListBucket allows the workflow to list objects in the bucket. s3:PutObject permits uploads, while s3:DeleteObject allows objects to be removed.
The delete permission is necessary for the --delete option used later in this article. If your deployment does not need to remove obsolete files, omit that permission and remove the option from the deployment command.
Additional permissions may be required if the bucket uses a customer-managed AWS KMS key or if the workflow also invalidates a CloudFront distribution. Grant only the permissions required by your actual deployment.
Step 4: Create the GitHub Actions Workflow
Create a file named .github/workflows/deploy.yml in your repository.
The following example builds a Node.js website and uploads its output to Amazon S3.
name: Deploy Website to AWS
on:
push:
branches:
- main
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Check out source code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- name: Install dependencies
run: npm ci
- name: Build website
run: npm run build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
aws-region: ap-south-1
- name: Verify AWS identity
run: aws sts get-caller-identity
- name: Deploy to S3
run: |
aws s3 sync ./dist s3://my-production-web-bucket --deleteThis example assumes the project uses Node.js, has an npm ci command, and generates its production files in the dist directory. Change these commands if your application uses a different build process.
You must also replace the example AWS account ID, IAM role name, region, and bucket name with your actual configuration.
The contents: read permission allows the workflow to check out repository files. The id-token: write permission allows it to request an OIDC token from GitHub.
The id-token: write setting does not grant AWS access by itself. AWS must still accept the token under the role's trust policy, and the role must have the required permissions.
The configure-aws-credentials action requests temporary credentials for the specified role. Subsequent AWS CLI commands use those credentials.
The action versions shown are examples. For production workflows, review the actions you use and consider pinning third-party actions to verified full commit SHAs according to your organization's security policy.
Step 5: Verify the AWS Identity
The workflow includes this command:
aws sts get-caller-identityIt displays information about the AWS identity associated with the current credentials, including the AWS account and assumed-role identity.
During initial setup, verify that the workflow is using the intended account and deployment role before allowing it to modify production resources.
If role assumption fails, check the following:
The OIDC provider exists in the correct AWS account.
The provider ARN in the trust policy is correct.
The audience matches
sts.amazonaws.com.The repository and branch match the
subcondition.The workflow has
id-token: write.The IAM role ARN and AWS region are correct.
Do not resolve authentication failures by removing trust-policy restrictions. Identify the mismatched claim or configuration and correct it.
Step 6: Understand the Deployment Command
The workflow deploys the website using:
aws s3 sync ./dist s3://my-production-web-bucket --deleteThe sync command compares the local build directory with the target bucket and uploads files that need to be copied.
The --delete option also removes destination objects that are not present in the source directory.
This is useful for static website deployments, but it can remove valid files if the wrong directory or bucket is specified. Test the deployment against a nonproduction bucket before enabling deletion in production.
If the bucket uses a customer-managed KMS key, verify that the role has the necessary KMS permissions. Also confirm that bucket policies, encryption settings, and other resource-level restrictions permit the deployment.
Step 7: Protect the Production Workflow
Using temporary credentials reduces the risk associated with long-lived keys, but it does not make an insecure workflow safe.
Protect the deployment process in several ways.
Restrict access to trusted code
Protect the main branch with appropriate review requirements and status checks. Limit who can modify the workflow file, since changes to that file can affect deployment behavior.
Use GitHub Environments
For production deployments, consider creating a GitHub Environment named production. Configure required reviewers and branch restrictions where appropriate.
If the environment changes the OIDC subject claim, update the IAM trust policy accordingly. Verify the actual claim instead of assuming the branch-based example remains valid.
Separate roles by environment
Use different IAM roles for development, staging, and production. Each role should have permissions limited to its own resources.
This reduces the impact of an error or compromise in a nonproduction workflow.
Review third-party actions
A compromised action running in a job with AWS credentials may be able to use those credentials. Review the actions used in deployment workflows, pin them to verified versions or commit SHAs where appropriate, and avoid running untrusted code in privileged jobs.
Troubleshooting Common OIDC Deployment Errors
Error or symptom | Likely cause | What to check |
|---|---|---|
Not authorized to perform sts:AssumeRoleWithWebIdentity | Trust policy does not match the token | Provider ARN, audience, repository, branch, or environment |
No GitHub OIDC token available | Workflow permissions are insufficient | Confirm |
S3 access denied | The role lacks required permissions | Check bucket and object ARNs, bucket policies, and required actions |
Deployment targets the wrong location | Incorrect bucket or region | Verify workflow configuration |
Workflow works on one branch but fails on another | Trust policy permits only a specific branch | Check the OIDC |
Files disappear after deployment | The sync command uses | Verify the source directory and destination bucket |
When troubleshooting, distinguish authentication errors from authorization errors.
Authentication determines whether GitHub Actions can assume the IAM role. Authorization determines which AWS operations the resulting role session can perform.
If aws sts get-caller-identity succeeds but the S3 deployment fails with an access-denied error, investigate the role's permissions and the bucket's resource policy first.
Common Mistakes to Avoid
Keeping old access keys active after confirming that OIDC works.
Allowing unrelated repositories or branches to assume the production role.
Granting administrator permissions when the workflow only needs to deploy files.
Assuming
id-token: writeautomatically grants AWS access.Using
--deletewithout verifying the source directory and destination bucket.Allowing untrusted pull request workflows to access production credentials.
Ignoring bucket policies or encryption permissions when troubleshooting S3 access.
Treating temporary credentials as protection against a compromised workflow.
Best Practices for Secure AWS Deployments
Use GitHub Actions OIDC instead of storing long-lived AWS access keys in repository secrets.
Restrict the IAM trust policy to the intended repository and deployment context.
Give the deployment role only the permissions required for its job.
Protect production branches, workflow files, and deployment environments.
Use separate IAM roles for development, staging, and production.
Verify the AWS identity during setup and after configuration changes.
Review and pin third-party actions according to your security policy.
Monitor AWS activity and deployment logs for unexpected behavior.
Test the deployment process in a nonproduction environment.
Deactivate obsolete access keys after confirming that no other workload depends on them.
Summary
GitHub Actions can deploy applications to AWS without storing long-lived access keys by using OpenID Connect and IAM roles. GitHub provides an identity token, AWS validates it against the role's trust policy, and the workflow receives temporary credentials.
The important part is configuring both the trust policy and the permissions policy correctly. The trust policy should identify the repository and workflow context that may assume the role, while the permissions policy should limit access to the resources required for deployment.
With restricted IAM permissions, protected GitHub workflows, and proper deployment testing, OIDC provides a practical way to automate AWS deployments while reducing the risks associated with long-lived credentials.

Join the conversation! Your thoughts help the community grow.