Auto-deploy with Github OIDC and AWS
So you have built your product you have written and tested tests for your application, and you have Docker setup. The only thing that remains is deploying so you build your image from a Dockerfile config or Docker compose and upload the image to AWS Elastic Container Registry (ECR) and continue from there.
But using this way of deploying is daunting and requires you to wait for the build to complete and there's no assurance of anything, what if you miss adding a configuration or a test failed and you only realize it after testing it on the production server you have set up. One solution is to use Github Actions Workflows in combination with its OpenID Connect.
OpenID Connect allows a workflow to access the cloud provider by requesting a short-lived token that will authenticate the workflow which in this case runs all tests and deploy a built image to the provider, ECR.
So here's how we do it,
First, we add set up our GitHub workflow for the project, in my case Golang. In the parent folder add a .github folder and add a tests.yml and a deploy.yml file.
In the tests.yml file we will add some config to run all of our tests we can then add a success requirement or guard to our main branch to guard it from accepting any Pull Requests (PR) that have less than 100% tests passed.
name: Run Unit test
# Add hook on the main branch
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
name: Test
runs-on: ubuntu-latest
services:
mongodb:
image: mongo
env:
DB_NAME: mydb
DB_USER: root
DB_PASS: secret
ports:
- 8081:8081
options: --health-cmd "mongo --version" --health-interval 10s --health-timeout 5s --health-retries 3
redis:
image: redis
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 3
rabbitmq:
image: rabbitmq:3.8.9-management
ports:
- 5672:5672
- 15672:15672
env:
RQ_USERNAME: mr_rabbit
RQ_PASS: rabbitismypassword
options: --health-cmd "rabbitmqctl node_health_check" --health-interval 10s --health-timeout 5s --health-retries 3
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
- name: Install Dependencies
run: go mod download
- name: Run tests
run: make test
So the following configuration pulls all the images that my app needs to make a full tests run all on services, then downloads all dependencies then runs all tests via a command I have written in a Makefile.
Now for the cherry on top, we add our deploy
name: Deploy to production
on:
push:
branches: [ "main" ]
env:
AWS_REGION: "us-east-2"
permissions:
id-token: write # Required for requesting JWT
contents: read # Required for checking out the repository
jobs:
assumeRoleCallAndIdentity:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure aws crendentials
uses: aws-actions/configure-aws-credentials@v1.7.0
with:
role-to-assume: ${{ secrets.AWS_ARN }}
role-session-name: ${{ secrets.AWS_ROLE_SESSION_NAME }}
aws-region: ${{ env.AWS_REGION }}
- name: STS GetCallerIdentity
run: |
aws sts get-caller-identity
deploy:
name: Build image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v.1.21.3'
id: install
- name: Login to AWS ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
The config is almost the same as the test one allowing us to set the branch we want the workflow to trigger from. But we have an env, we set our config here so we can use it repetitively anywhere in the config. Since it's AWS we set the region, and permissions allow us to set rules for what the workflow can do and what it can't.
The sugar comes from the rest, the workflow allows us to assume a role as we would from the AWS Console. We use Github secrets to set our role Amazon Resource Name (ARN)
We use STS GetCallerIdentity to get details about the current role, this will tell the next action whether pushing to ECR is allowed for the role.
Finally, We build the image and deploy it to the ECR, the secrets you see there tells the GitHub Action where to push the built image. We can also set the tag to ensure we use a specific TAG for different use cases. We can have one for Staging, Development and another for Production to set up a healthy development pipeline.
To Finalise, you can head to the repository to the Settings tab and select the Actions and all the way down allow Github Actions to approve PRs
Happy building 🔥🔥.
To learn more about this head over to the following to get a better understanding.
Github OIDC. Learn how to configure Auto Deploy for any Cloud Provider
AWS Identity Providers. AWS provision for Identities