Deploying on AWS via Github Actions
I recently decided to move the deployment of this site to GitHub actions. In this post I’ll document the parts that went into it.
Since the site is hosted on AWS (a combo of S3 + Cloudfront), the Github action needed to access AWS.
This was done via the aws-actions/configure-aws-credentials action, that at a high level does the following:
- Requests a JWT ID token from the GitHub OIDC provider.
- Requests a set of short-lived tokens via the AWS STS AssumeRoleWebIdentity (using the JWT from step 1). These tokens make it possible to interact with different AWS services.
So for deploys to work we need some work on both the AWS and GitHub action side of things.
AWS
You’ll need a role on the AWS role with the permissions required to deploy your application. This role is assumed from the Github pipeline.
Using Pulumi it looks something like this:
const currentCaller = aws.getCallerIdentity()
const githubActionsRole = new aws.iam.Role("GitHubActionsRole", {
name: "GitHub_Actions_Role",
assumeRolePolicy: currentCaller.then(identity => JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Principal: {
Federated: `arn:aws:iam::${identity.accountId}:oidc-provider/token.actions.githubusercontent.com`
},
Action: "sts:AssumeRoleWithWebIdentity",
Condition: {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:GITHUB_USER/repo:*"
},
"StringEqualsIgnoreCase": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
}
}
}]
}))
});
const githubActionsDeployPolicy = new aws.iam.Policy("GitHubActionsDeployPolicy", {
name: "GitHubActionsDeployPolicy",
policy: s3Bucket.arn.apply((arn) => JSON.stringify({
Version: "2012-10-17",
Statement: [{
Sid: "AllowS3Actions",
Effect: "Allow",
Action: ["s3:PutObject", "s3:ListBucket", "s3:GetObject", "s3:DeleteObject"],
Resource: [
arn,
`${arn}/*`
]
}]
}))
});
new aws.iam.RolePolicyAttachment("GitHubActionsDeployAttachment", {
role: githubActionsRole.name,
policyArn: githubActionsDeployPolicy.arn
});
Github action
The GitHub workflow needs two permissions:
id-token: write
to create the OIDC JWT.contents: read
for the actions/checkout, i.e. checking out code.
on:
workflow_dispatch:
jobs:
get_caller_identity:
name: Deploy Site
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4.1.1
- name: Assume AWS role
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GitHub_Actions_Role
aws-region: eu-north-1
- name: Install node
uses: actions/setup-node@v4
with:
node-version: 22.14.0
- name: Install deps
run: npm ci
working-directory: web
- name: Build site
run: npm run build
working-directory: web
- name: Deploy site
run: aws s3 sync ./dist/ s3://BUCKET_NAME/ --delete
working-directory: web