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.
Join the conversation! Your thoughts help the community grow.