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:

  1. A GitHub Actions workflow requests an OIDC identity token.

  2. AWS Security Token Service validates the token against the configured identity provider and IAM role trust policy.

  3. AWS issues temporary credentials for the IAM role.

  4. 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.com

  • Audience: 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:

JSON
{
  "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:

JSON
{
  "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.

SQL
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 --delete

This 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:

Code
aws sts get-caller-identity

It 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 sub condition.

  • 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:

Code
aws s3 sync ./dist s3://my-production-web-bucket --delete

The 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 id-token: write

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 sub claim

Files disappear after deployment

The sync command uses --delete

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: write automatically grants AWS access.

  • Using --delete without 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

  1. Use GitHub Actions OIDC instead of storing long-lived AWS access keys in repository secrets.

  2. Restrict the IAM trust policy to the intended repository and deployment context.

  3. Give the deployment role only the permissions required for its job.

  4. Protect production branches, workflow files, and deployment environments.

  5. Use separate IAM roles for development, staging, and production.

  6. Verify the AWS identity during setup and after configuration changes.

  7. Review and pin third-party actions according to your security policy.

  8. Monitor AWS activity and deployment logs for unexpected behavior.

  9. Test the deployment process in a nonproduction environment.

  10. 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.