Skip to content

Commit 8c28df8

Browse files
authored
Merge pull request #281 from sagarsys/blog/github-actions-aws
Add blogpost about Automation with Github actions and AWS
2 parents a092aa1 + 985f1c3 commit 8c28df8

File tree

5 files changed

+274
-0
lines changed

5 files changed

+274
-0
lines changed

data/authors/sagar-sawuck.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
name: Sagar Sawuck
3+
avatar: /authors/sagar-sawuck.jpeg
4+
occupation: Senior Frontend Consultant
5+
twitter: https://twitter.com/sagarsys
6+
linkedin: https://www.linkedin.com/in/sagarsys/
7+
github: https://github.com/sagarsys
8+
website: https://sagarsys.net
9+
---
10+
11+
Senior JavaScript Developer with 10+ years of experience specializing in front-end development, with a strong focus on React, TypeScript, and NextJs. Sagar focuses on developing high-performance, scalable web applications and leading teams in complex projects. Experienced in mobile-first development, api development, and full-stack development. Passionate about clean code, test-driven development, continuous learning, and driving innovation.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
---
2+
title: 'Frontend Automation with Github Actions and AWS'
3+
date: '2025-01-31'
4+
tags: ['frontend', 'aws', 'github-actions', 'devops']
5+
images: ['/articles/frontend-automation-with-github-actions-and-aws/gha-in-action.png']
6+
summary: "No matter which tech stack you use, automating your deployment process can save you time and headaches. Let's see how to use Github Actions to automate your frontend deployments to AWS S3."
7+
authors: ['sagar-sawuck']
8+
theme: 'blue'
9+
---
10+
11+
## Table of Contents
12+
13+
<TOCInline toc={props.toc} exclude={["Table of Contents"]} toHeading={2, 3} />
14+
15+
## TL;DR The problem: Automating AWS deployments
16+
17+
I recently updated [my developer website](https://sagarsys.net) after almost 3 years! Following an epic battle with the usual node module dependencies update (the struggle is real :D), I ran into another issue: **deployment**.
18+
19+
For some context, the website is a static website hosted on AWS S3, with CloudFront caching. This is a popular choice for frontend hosting. However, it is quite a hassle to deploy manually:
20+
21+
1. Build locally the project
22+
2. Login to AWS
23+
3. Go to S3, delete the contents of the bucket
24+
4. Upload the new build files in the bucket
25+
5. Go to CloudFront, invalidate the cache
26+
6. Test live ⇒ bug ? Fix and go back to 1 : done!
27+
28+
After using Github Actions (GHAs) in my previous job to automate testing on pull requests, I wanted to automate the deployment of my website when new changes are pushed to the `master` branch.
29+
30+
## The solution: Using GHA
31+
32+
### **Introducing GHA**
33+
34+
TL;DR: If you are new to GHAs, it basically allows you to run specific workflows (a set of jobs or actions) on predefined events like pushing to a branch or opening pull requests directly in a Github repository.
35+
36+
> GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository, or deploy merged pull requests to production.
37+
38+
The main components of GHAs:
39+
40+
- **Workflows** - set of automated jobs you want to execute on specific events
41+
- **Events** - triggers that run workflows, e.g opening a pull request
42+
- **Actions** - set of predefined and reusable workflows, e.g setup node to install deps
43+
- **Runners** - on-demand servers that execute workflows when events are triggered
44+
45+
**Example**: When a PR is opened, we want to run unit tests.
46+
47+
```yaml
48+
on: # <-- event
49+
pull_request:
50+
types: [opened]
51+
52+
jobs: # <--- workflow
53+
build-and-test:
54+
runs-on: ubuntu-latest # <-- runner
55+
56+
steps: # <-- workflow breakdown
57+
- name: Checkout code
58+
uses: actions/checkout@v3 # <-- action
59+
60+
- name: Set up Node.js
61+
uses: actions/setup-node@v3 # <-- action
62+
63+
- name: Install dependencies
64+
run: npm install # <-- job
65+
66+
- name: Build project
67+
run: npm run build # <-- job
68+
69+
- name: Run tests
70+
run: npm run test # <-- job
71+
```
72+
73+
This workflow runs whenever a pull request is made. It runs on `ubuntu-latest`. It uses a predefined action `actions/checkout@v3` to checkout the code, then another action to setup the specified node version, followed by the defined jobs: installing dependencies `npm install`, building the project `npm run build` and running the tests `npm run test`. The NPM commands are as defined in the project’s `package.json`.
74+
75+
You can dive deeper into GHAs on the official Github documentation [here](https://docs.github.com/en/actions/about-github-actions/understanding-github-actions).
76+
77+
### **The Original Problem definition**
78+
79+
Getting back to the original problem, we want to build the app and then deploy to AWS S3 when a push is made to the `master` branch.
80+
81+
By now, the first part of the task should be quite simple. Similar to the example above, we want to checkout the code, setup a specific version of NodeJs compatible with the project, install dependencies, and finally build the project - but it uses a different event.
82+
83+
```yaml
84+
on:
85+
push:
86+
branches:
87+
- master
88+
89+
jobs:
90+
build-and-deploy:
91+
runs-on: ubuntu-latest
92+
93+
steps:
94+
- name: Checkout code
95+
uses: actions/checkout@v3
96+
97+
- name: Set up Node.js
98+
uses: actions/setup-node@v3
99+
with:
100+
node-version: '20' # Specify the Node.js version
101+
102+
- name: Install dependencies
103+
run: npm ci
104+
105+
- name: Build project
106+
run: npm run build
107+
```
108+
109+
The next step is about how to deploy to AWS.
110+
111+
### **How to use the AWS CLI and Github secrets**
112+
113+
AWS provides a [CLI tool](https://aws.amazon.com/cli/) that can be used to programmatically manage AWS resources like S3 or CloudFront. Although not necessary, I recommend setting it up locally to test the commands before trying it out on an actual hosted website.
114+
115+
You can follow the AWS documentation for how to [install](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) and [configure](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) it.
116+
117+
This is precisely what we need to do in our Github workflow. Luckily, there is an official action available from AWS to do just that: `aws-actions/configure-aws-credentials@v2`. For this action to work though, an IAM (Identity and Access Management) user needs to created on AWS with the required permissions to authorise the action to communicate with our AWS resources.
118+
119+
Let’s briefly walk-through the steps needed (assuming AWS root user or sufficient admin privileges):
120+
121+
1. Sign in to the AWS Management Console:
122+
- Go to https://console.aws.amazon.com/
123+
- Sign in with your AWS account
124+
2. Navigate to IAM:
125+
- In the AWS services search bar, type "IAM" and select it from the dropdown
126+
3. Create a new IAM user:
127+
- In the left sidebar, click on "Users"
128+
- Click the "Add users" button
129+
- Choose a username (e.g., "github-actions-deployer")
130+
- For "Select AWS credential type", choose "Access key - Programmatic access"
131+
- Click "Next: Permissions"
132+
4. Set permissions:
133+
- Click "Attach existing policies directly"
134+
- Search for and select the policies needed for your deployment. Common ones include:
135+
- `AmazonS3FullAccess` (for S3 deployments)
136+
- `CloudFrontFullAccess` (if using CloudFront)
137+
- Alternatively, you can create a custom policy with more restricted permissions
138+
- Click "Next: Tags"
139+
5. Add tags (optional):
140+
- You can add key-value pair tags for better organisation
141+
- Click "Next: Review"
142+
6. Review and create:
143+
- Review the user details and permissions
144+
- Click "Create user"
145+
7. Secure the credentials:
146+
- On the success page, you'll see the Access Key ID and Secret Access Key
147+
- Download the .csv file or copy these credentials immediately
148+
- Important: This is the only time you'll see the Secret Access Key, so make sure to save it securely
149+
150+
Next up, we need to configure the [Github secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) with the AWS access key.
151+
152+
> Secrets are variables that you create in an organization, repository, or repository environment. The secrets that you create are available to use in GitHub Actions workflows. GitHub Actions can only read a secret if you explicitly include the secret in a workflow.
153+
154+
1. Set up GitHub Secrets:
155+
- Go to your GitHub repository
156+
- Click on `Settings > Secrets > New repository secret`
157+
- Add two secrets:
158+
- Name: `AWS_ACCESS_KEY_ID`, Value: `[Your Access Key ID]`
159+
- Name: `AWS_SECRET_ACCESS_KEY`, Value: `[Your Secret Access Key]`
160+
2. Use in GitHub Actions:
161+
162+
- In the Github workflow, the AWS action can now be use these secrets like this:
163+
164+
```yaml
165+
- name: Configure AWS credentials
166+
uses: aws-actions/configure-aws-credentials@v2
167+
with:
168+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
169+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
170+
aws-region: us-east-1 # Replace with your AWS region
171+
```
172+
173+
Important Security Considerations:
174+
175+
- Never share your AWS credentials or commit them to your repository.
176+
- Use the principle of least privilege: only grant the permissions necessary for deployment.
177+
- Regularly rotate your access keys.
178+
- For production environments, consider implementing additional security measures like IP restrictions or multi-factor authentication.
179+
180+
By following these steps, you'll create AWS credentials that can be securely used in your GitHub Actions workflow for deployment. **Remember to keep your credentials secure and never expose them in your code or public repositories.**
181+
182+
### **The Final Piece of The Puzzle**
183+
184+
Now all that’s left is to synchronise the `build` files to our S3 bucket and optionally invalidate the CloudFront cache. These [commands](https://awscli.amazonaws.com/v2/documentation/api/latest/index.html) are available from the AWS CLI tool:
185+
186+
```yaml
187+
- name: Deploy to S3
188+
run: |
189+
aws s3 sync build/ s3://your-bucket-name --delete
190+
# Optional: Invalidate CloudFront distribution
191+
aws cloudfront create-invalidation --distribution-id YOUR_DISTRIBUTION_ID --paths "/*"
192+
```
193+
194+
Important notes:
195+
196+
1. **`aws s3 sync`**: This is the base command that tells AWS CLI to synchronise files with an S3 bucket.
197+
2. The **`build/`** directory is assumed to be where your Node.js build output goes. Adjust this if your project uses a different output directory.
198+
3. **`s3://your-bucket-name`**: This is the destination - the S3 bucket where you want to sync your files. Replace **`your-bucket-name`** with the actual name of your S3 bucket.
199+
4. **`--delete`**: This option tells the sync command to delete files from the destination (S3 bucket) that no longer exist in the source (local build directory). This ensures that your S3 bucket exactly matches your local build output.
200+
5. If you're using CloudFront, replace **`YOUR_DISTRIBUTION_ID`** with your actual CloudFront distribution ID.
201+
6. Make sure your S3 bucket is configured for static website hosting if you're deploying a website.
202+
7. Ensure the AWS IAM user associated with the credentials has the necessary permissions to write to the S3 bucket and create CloudFront invalidations (if used).
203+
204+
Et voilà !
205+
206+
### **Show me the code!**
207+
208+
Putting everything together, as convention would have it, a YAML file should be created in your repository as follows: `.github/workflows/{{name-of-your-workflow}}.yml`
209+
210+
```yaml
211+
name: Build and Deploy to S3
212+
213+
on:
214+
push:
215+
branches:
216+
- master
217+
218+
jobs:
219+
build-and-deploy:
220+
runs-on: ubuntu-latest
221+
222+
steps:
223+
- name: Checkout code
224+
uses: actions/checkout@v3
225+
226+
- name: Set up Node.js
227+
uses: actions/setup-node@v3
228+
with:
229+
node-version: '16'
230+
231+
- name: Install dependencies
232+
run: npm ci
233+
234+
- name: Build project
235+
run: npm run build
236+
237+
- name: Configure AWS credentials
238+
uses: aws-actions/configure-aws-credentials@v2
239+
with:
240+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
241+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
242+
aws-region: us-east-1
243+
244+
- name: Deploy to S3
245+
run: |
246+
aws s3 sync build/ s3://${{ secrets.AWS_BUCKET_NAME }} --delete
247+
# Invalidate CloudFront distribution
248+
aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_ID }} --paths "/*"
249+
```
250+
251+
You can find an example on my Github [here](https://github.com/sagarsys/sagarsys.net/blob/master/.github/workflows/build-and-deploy-to-s3.yml).
252+
253+
![Github Actions Job Dashboard](/articles/frontend-automation-with-github-actions-and-aws/github-actions-dashboard.png)
254+
255+
1. You can now monitor your deployments directly in your Github repo!
256+
2. The name in the YAML file is used to identify a specific action, in our case “build and deploy to S3”.
257+
3. You can click on the commit message for more details about a run.
258+
259+
## Final Thoughts
260+
261+
Deploying my website now takes about a minute after pushing changes to the `master` branch! Although there is a slight learning curve, automating workflows with GHAs saves a lot of time once you get used to it and is definitely worth exploring!
262+
263+
As the lines between software development and DevOps become increasingly blurred, GHAs become crucial. They automate repetitive tasks like testing, building, and deploying, allowing more focus on the application's core instead of infrastructure.
Loading

public/authors/sagar-sawuck.jpeg

42.6 KB
Loading

0 commit comments

Comments
 (0)