🔑 Key points
- You must authorize GitHub to access your AWS account.
- You can use a GitHub Actions workflow to execute AWS CLI commands.
- You will create a workflow that builds your project and uploads files to your S3 bucket.
To use Continuous Integration (CI) to deploy static frontend content to S3, you must create a trust relationship between GitHub and AWS. This is done using the OpenID Connect (OIDC) protocol to dynamically obtain an authentication token whenever you call S3 endpoints. Using OIDC to authenticate with AWS eliminates the need to store long-lived AWS credentials (like Access Keys) in your CI pipeline.
Important
Ensure you are using the us-east-1 AWS region for all your work in this course.
You establish a trust relationship between GitHub and AWS by configuring AWS to use GitHub as an OIDC identity provider, associating that identity with an AWS IAM role that allows S3 access, and configuring a GitHub Actions workflow to use that role.
First, you need to set up AWS to recognize GitHub as a trusted OIDC provider.
-
Open the AWS IAM service console.
-
Choose Identity providers from the left sidebar.
-
Click Add provider.
-
Select the provider type OpenID Connect.
-
Enter the following details:
- Provider URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com
- Provider URL:
-
Click Get thumbprint to verify the provider URL.
-
Click Add provider.
-
Click on the newly created identity provider in the list to display its properties.
-
Note the Provider ARN; you will reference this when you create the IAM role.
To follow the principle of least privilege, you must create an IAM policy that only provides the permissions necessary to update your S3 bucket and invalidate CloudFront cache files.
- In the IAM console, choose Policies.
- Click Create policy.
- Select the JSON tab to define the policy.
- Paste the following JSON, replacing the placeholders with your specific resource IDs:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListObjectsInBucket",
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::BUCKET_NAME_HERE"]
},
{
"Sid": "UpdateS3Bucket",
"Effect": "Allow",
"Action": ["s3:*Object"],
"Resource": ["arn:aws:s3:::BUCKET_NAME_HERE/*"]
},
{
"Sid": "InvalidateCloudFront",
"Effect": "Allow",
"Action": ["cloudfront:CreateInvalidation"],
"Resource": ["arn:aws:cloudfront::AWS_ACCOUNT_HERE:distribution/DISTRIBUTION_HERE"]
}
]
}- Replace
BUCKET_NAME_HEREwith your S3 bucket name (e.g.,pizza.yourhostname). - Replace
AWS_ACCOUNT_HEREwith your 12-digit AWS account ID. - Replace
DISTRIBUTION_HEREwith your CloudFront distribution ID (e.g.,F3A3ZL6IF7XCE1). - Click Next.
- Name the policy
jwt-pizza-ci-deployment. - Click Create policy.
Now, create the IAM role that GitHub Actions will assume.
-
In the IAM console, choose Roles.
-
Click Create role.
-
Select Web identity.
-
Select the identity provider you created for GitHub.
-
Select
sts.amazonaws.comfrom the Audience dropdown. -
Enter your GitHub organization (your GitHub username).
-
Enter the GitHub repository name for your fork of the
jwt-pizzarepository. -
Enter
mainfor the GitHub branch. -
Click Next.
-
Under Permissions policies, search for and select the policy you just created (
jwt-pizza-ci-deployment). -
Click Next.
-
Name the role
github-ci. -
Click Create role.
The final step is to create a GitHub Actions workflow that deploys to S3 using the OIDC credentials.
Since your repository is public, you must hide sensitive information like your AWS Account ID and resource names. Use GitHub Repository Secrets to store these values.
Create the following secrets in your GitHub repository settings (Settings > Secrets and variables > Actions):
| Secret | Description | Example |
|---|---|---|
| AWS_ACCOUNT | Your 12-digit AWS account ID | 123456789012 |
| CI_IAM_ROLE | The name of the IAM role you created | github-ci |
| APP_BUCKET | The S3 bucket name hosting your frontend | pizza.yourname.click |
| DISTRIBUTION_ID | The CloudFront Distribution ID | F3GRFXFEBQ8EEU |
-
In your local repository, create a directory named
.github/workflowsif it doesn't already exist. -
Create a new file named
testS3Deploy.ymlin that directory. -
Paste the following configuration into the file:
name: Test S3 Deploy on: push: branches: - main workflow_dispatch: permissions: id-token: write # Required for requesting the JWT contents: read # Required for actions/checkout jobs: deploy-s3: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Create OIDC token to AWS uses: aws-actions/configure-aws-credentials@v4 with: audience: sts.amazonaws.com aws-region: us-east-1 role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT }}:role/${{ secrets.CI_IAM_ROLE }} - name: Push to AWS S3 run: | mkdir dist printf "<h1>CloudFront deployment with GitHub Actions</h1>" > dist/index.html aws s3 cp dist s3://${{ secrets.APP_BUCKET }} --recursive aws cloudfront create-invalidation --distribution-id ${{ secrets.DISTRIBUTION_ID }} --paths "/*"
-
Save the file, commit it, and push it to GitHub.
The workflow uses the aws-actions/configure-aws-credentials action to request a temporary token from AWS using OIDC:
- name: Create OIDC token to AWS
uses: aws-actions/configure-aws-credentials@v4
with:
audience: sts.amazonaws.com
aws-region: us-east-1
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT }}:role/${{ secrets.CI_IAM_ROLE }}Once authorized, the workflow can execute AWS CLI commands permitted by the IAM role. It copies the dist directory contents to S3 and triggers a CloudFront invalidation. Invalidation is necessary because CloudFront caches your files; without it, updates would not appear until the cache expires (typically 24 hours).
aws s3 cp dist s3://${{ secrets.APP_BUCKET }} --recursive
aws cloudfront create-invalidation --distribution-id ${{ secrets.DISTRIBUTION_ID }} --paths "/*"After the workflow runs successfully, visit your website URL. You should see the updated index.html page.
{"id":"c91dadd0-8920-4247-b944-e37cfc91dae4","title":"AWS CloudFront","type":"url-submission","syncGrade":false,"autoGrade":false,"validateUrl":true,"gradingCriteria":"- The body of the webpage says 'CloudFront deployment with GitHub Actions'"}
Provide the URL of your publicly available CloudFront hosted website that was deployed using your CI pipeline.
_Example: pizza.mydomainname.click_
Once you have confirmed the deployment works, you can disable or delete this test workflow. You will implement a similar deployment process for the actual JWT Pizza frontend code in a later assignment.







