Zac Fukuda
066

GitHub Actions: Test, Build, Deploy to AWS S3

You version control your code with Git. GitHub might be your first choice for your remote origin. The alternative is GitLab. GitLab used to have own ci/cd tool for long. That could be a main reason why one team prefers GitLab to GitHub. When you decide to go with GitHub, to implement ci/cd process, you have to register other service like CircleCI or TravisCI. But now GitHub has its own ci/cd called GitHub Actions. This is great in that your development process could be more centralized.

Meanwhile, Amazon Cloud Service(aws) may be your first choice of cloud service. Aws has its own repository service CodeCommit. But because of popularity of GitHub, you would like GitHub as a repo.

So, in this article I am going to show you how to use GitHub Actions to test, build, and deploy your static TypeScript/JavaScript to aws Simple Storage Service(s3).

There are many blog posts on the Internet on this. Almost all blog posts write on the iam user credential to authenticate GitHub workflow with aws. They show you to store access key and key secret in repo settings. But Aws recommend using GitHub's OpenID Connect(oidc) provider.

See oidc for more information about it.

So, I am going to show you how to use GitHub Actions and aws with oidc. That’s different from the others.

Prerequisites

  • You must have a GitHub account
  • You must have an aws iam user that can login the console
  • Your iam user must have permission to:
    • Add an identity provider
    • Create a role
    • Attach a policy to the role

Steps

  1. Create a GitHub repository
  2. Create an s3 bucket
  3. Add an identity provider
  4. Update the GitHub Actions workflow

Create a GitHub repository

Go to Create a new repository page on GitHub and create an empty private repository. You can give any name that you like to the repo. I use github-actions-s3-example in this article.

Create a new repository

I have a sample repository you can clone. Please run the following command in terminal.

git clone https://github.com/zacfukuda/github-actions-s3-example.git
cd github-actions-s3-example

This repository contains the very simple TypeScript source code that must be verified with ESLint and Jest before build process.

You can test to see if your npm scrips can perform without error on your local computer.

yarn
yarn lint
yarn test
yarn build

You can use npm but yarn is used in GitHub Actions.

After clone the repo, let’s push the files to your new repo:

git add .
git commit -m 'cloned the example repo'
git remote set-url origin YOUR_GITHUB_REPOSITORY_URL
git push -u origin main

Go to Actions tab in your GitHub repo. You see Deploy to S3 workflow in the left pane.

Create an S3 bucket

Login Aws Management Console. Make sure your region is selected as intended. I use us-east-1(North Virginia) as a demo. Go to S3 console, click Create bucket button. From the Create bucket wizard, create a new bucket named something like github-actions-s3-example-yourname for test purpose.

Create bucket

You can leave the settings default besides bucket name.

Add an identity provider

Go to iam console. In the left navigation pane, choose Identity providers, then click Add provider button.

Please enter the following information in the wizard.

  • Provider type: OpenID Connect
  • Provider url: https://token.actions.githubusercontent.com (click Get thumbprint afer enter the url.)
  • Audience: sts.amazonaws.com
Add an Identity Provider

Verify that you enter the correct information. Then click Add provider button at the bottom.

Once you go back to Identity Providers pane, you see the new provider you just created in the table.

From the table click token.actions.githubusercontent.com, then click Assign role button. Choose Create a new role and go Next.

Create a new role

In the wizard Select trusted entity, enter following information.

  • Trusted entity type: Web identity
  • Web identity
    • Identity provider: token.actions.githubusercontent.com
    • Audience: sts.amazonaws.com
    • GitHub organization: your_github_account
    • GitHub repository: your_github_repository (github-actions-s3-example)
    • GitHub branch: main
Trusted entity
Web identity

Go to Next.

In Add permissions step, do not add any permission yet. Just go to Next. We will add an inline permission later.

In Name, review, and create page, Enter a role name like GitHubActionsS3ExampleRole. You can leave the rest of fields unchanged. The example of Trust policy json is shown below:

{
	"Version": "2012-10-17",
	"Statement": [
			{
					"Effect": "Allow",
					"Action": "sts:AssumeRoleWithWebIdentity",
					"Principal": {
							"Federated": "arn:aws:iam::YOUR_AWS_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
					},
					"Condition": {
							"StringEquals": {
									"token.actions.githubusercontent.com:aud": [
											"sts.amazonaws.com"
									],
									"token.actions.githubusercontent.com:sub": [
											"repo:YOUR_GITHUB_ACCOUNT/YOUR_GITHUB_REPOSITORY:ref:refs/heads/main"
									]
							}
					}
			}
	]
}

Click Create role.

To confirm that your new role is assigned to the IdP, Go to Roles > your_iam_role_name(GitHubActionsS3ExampleRole), open Trust relations tab. You see the trust policy that was shown during the wizard.

Go to Roles > your_iam_role_name(GitHubActionsS3ExampleRole). In Permissions panel, Click Add permissions > Create inline policy.

In Policy editor, switch to json mode, and the paste the following text:

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "VisualEditor0",
			"Effect": "Allow",
			"Action": [
				"s3:PutObject",
				"s3:GetObject",
				"s3:ListBucket",
				"s3:DeleteObject"
			],
			"Resource": [
				"arn:aws:s3:::YOUR_S3_BUCKET_NAME",
				"arn:aws:s3:::YOUR_S3_BUCKET_NAME/"
			]
		}
	]
}

Don’t forget to replace YOUR_S3_BUCKET_NAME with your actual bucket name. This is a minimum permission for GitHub Actions to copy the artifact of build folder to S3.

Go to Next.

In step Review and create, enter policy name like crud-permission-on-s3-bucket. Click Create policy.

Update the GitHub Actions workflow

Now you must all variables necessary for GitHub Actions to work. The content of .github/workflows/deploy-to-s3.yml is as follows:

name: Deploy to S3
run-name: ${{ github.actor }} is deploying to S3
on:
	push:
		branches:
			- main
		paths:
			- 'src/**'
env:
	AWS_REGION: YOUR_AWS_REGION
	IAM_ROLE_ARN: arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/YOUR_IAM_ROLE_NAME
	S3_BUCKET_NAME: YOUR_S3_BUCKET_NAME
permissions:
	id-token: write
	contents: read
jobs:
	deploy:
		name: Deploy
		runs-on: ubuntu-latest
		timeout-minutes: 10
		steps:
			- uses: actions/checkout@v4
			- uses: actions/setup-node@v4
				with:
					node-version: 20
					cache: yarn
			- name: Install dependencies
				run: yarn install
			- name: Linting
				run: yarn lint
			- name: Unit testing
				run: yarn test
			- name: Build
				run: yarn build
			- name: Configure AWS credentials
				uses: aws-actions/configure-aws-credentials@v4
				with:
					role-to-assume: ${{ env.IAM_ROLE_ARN }}
					role-session-name: samplerolesession
					aws-region: ${{ env.AWS_REGION }}
			- name: Deploy to S3
				run: aws s3 sync ./build/ s3://${{ env.S3_BUCKET_NAME }}/build --delete

Please update the env configurations to yours. If you have followed the tutorial exactly, each value besides your aws account id to be:

  • Aws region: us-east-1
  • Iam role: GitHubActionsS3ExampleRole
  • S3 bucket name: github-actions-s3-example-yourname

This workflow invokes only when src is changed. Please add any modification to files under src directory.

After making a change, please run the following commands:

git add .
git commit -m 'testing workflow'
git push -u origin main

Go to Actions panel of your GitHub repository. You must see the workflow Deploy to S3 is in progress, after a couple minutes or so in success.

GitHub Actions workflow sucess

Now go to your S3 console, open your new bucket. You must have build folder in the bucket, which contains index.js and hello.js.

To test whether the workflow will fail when there is an error in the source code, update src/hello.ts like this:

export default function hello(name: string): string {
  return 'Hello, ' + name;
}

Commit your change and push it. This time your workflow would exit at the unit testing causing an error, which prevents the following steps not executed.

Clean up

Before you go away, let’s not forget to clean up the resources we created in this tutorial.

  1. Delete your iam role(GitHubActionsS3ExampleRole)
  2. (Optional) Remove the identity provider(token.actions.githubusercontent.com)
  3. Delete your S3 bucket (github-actions-s3-example-yourname)
  4. Delete your GitHub repository(github-actions-s3-example)

If you have a plan using token.actions.githubusercontent.com IdP with the audience sts.amazonaws.com, for example for your project, you can keep it.

References