DEV Community

Cover image for Automating React App deployments to AWS with GitHub Actions and OIDC
Francisco Silvério
Francisco Silvério

Posted on

Automating React App deployments to AWS with GitHub Actions and OIDC

After deploying my personal website to AWS, I realized the need to automate the deployment process to streamline the delivery of new features. For that, you can’t go wrong with a CI/CD pipeline powered by your Git provider — in this case, GitHub and GitHub Actions.

In this article, I’ll walk through the architecture and CI/CD pipeline I use to deploy a React application to S3 + CloudFront, authenticated via GitHub Actions OIDC (no long-lived AWS credentials).

This setup is suitable for real-world projects and follows AWS and GitHub best practices.


Architecture Overview

Before diving into CI/CD, let’s clarify the infrastructure that was already in place.


Initial AWS Setup (Pre-requisites)

The following resources were already created before the pipeline was written.

1 - S3 bucket

  • Static website hosting enabled
  • Public access configured appropriately
  • Hosts the built React assets (index.html, /assets/*, etc.)

2 - CloudFront distribution

  • Origin pointing to the S3 website endpoint
  • Default root object: index.html
  • Caching enabled
  • Custom domain configured

3 - Name.com

  • Custom domain pointing to the CloudFront distribution

Once this setup is complete, the website can already be accessed via the custom domain.
The CI/CD pipeline’s job is to automate updates safely and consistently.


AWS: Configuring OIDC Authentication

1 - Create an OIDC Identity Provider

In AWS IAM:

  • Provider URL:
https://token.actions.githubusercontent.com
Enter fullscreen mode Exit fullscreen mode
  • Audience:
sts.amazonaws.com
Enter fullscreen mode Exit fullscreen mode

This allows AWS to trust GitHub as an identity provider.


2 - Create an IAM Role for GitHub Action

This role will be assumed by the GitHub Actions runner.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<ACCOUNT_ID>: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:<ORG>/<REPO>:*"
        }
      }
    }
  ]
}

Enter fullscreen mode Exit fullscreen mode

3 - Attach Required IAM Permissions

The role needs permissions to:

  • Sync files to S3
  • Create CloudFront invalidations

Example policy (simplified):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::<BUCKET_NAME>",
        "arn:aws:s3:::<BUCKET_NAME>/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "cloudfront:CreateInvalidation",
      "Resource": "*"
    }
  ]
}

Enter fullscreen mode Exit fullscreen mode

GitHub Actions Pipeline Overview

The pipeline is split into three jobs:

  1. install – install dependencies
  2. build – build the React app and upload artifacts
  3. deploy – authenticate to AWS, deploy to S3, invalidate CloudFront

This separation improves clarity, caching, and debuggability.


Job 1: Installing Dependencies

install:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - name: Install dependencies
      uses: actions/setup-node@v4
      with:
        node-version: "22.x"
        cache: 'npm'
    - run: npm ci
Enter fullscreen mode Exit fullscreen mode

What this job does

  1. Checks out the repository
  2. Sets up Node.js 22
  3. Uses npm ci for deterministic installs
  4. Enables dependency caching for faster builds

This job ensures the dependency tree is valid before moving forward.

Job 2: Building the React Application

build:
  runs-on: ubuntu-latest
  needs: install
  steps:
    - uses: actions/checkout@v4
    - name: Use Node.js 22
      uses: actions/setup-node@v4
      with:
        node-version: "22.x"
        cache: 'npm'
    - run: npm ci
    - run: npm run build
    - name: Upload dist
      uses: actions/upload-artifact@v4
      with:
        name: dist
        path: ./dist
Enter fullscreen mode Exit fullscreen mode

What this job does

  • Rebuilds the application in a clean environment
  • Produces a static dist/ folder
  • Uploads the build output as a pipeline artifact

Artifacts allow the deploy job to be fully decoupled from the build process.

Job 3: Deploying to AWS (S3 + CloudFront)

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: aws
    permissions:
      id-token: write
      contents: read

    steps:
      - name: Download dist artifact
        uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ARN }}
          aws-region: ${{ vars.AWS_REGION }}
          role-session-name: ${{ vars.ROLE_SESSION_NAME}}

      - name: Deploy to S3
        run: |
          aws s3 sync dist s3://${{ vars.S3_BUCKET_NAME }} \
            --delete \
            --exact-timestamps

      - name: Invalidate CloudFront
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }} \
            --paths "/*"

Enter fullscreen mode Exit fullscreen mode

What this job does

  • Downloads the build artifact
  • Authenticates to AWS
  • Uploads the static files to the bucket
  • Invalidate CloudFront's cache

Invalidating /* ensures CloudFront fetches the new version immediately.


Final Result

After every push to main:

  • The app is built
  • Assets are uploaded to S3
  • CloudFront cache is invalidated
  • The custom domain serves the latest version reliably

All without storing a single AWS secret in GitHub.


References

- GitHub Actions OIDC

- AWS IAM OIDC

- CloudFront invalidations

- aws-actions/configure-aws-credentials

Top comments (0)