Skip to content

Commit 424d187

Browse files
committed
Add Field Notes section and first CI/CD article
1 parent 963d826 commit 424d187

7 files changed

Lines changed: 1057 additions & 0 deletions

File tree

406 KB
Loading
21.3 KB
Loading
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
8+
<title>Building My First CI/CD Pipeline | Cliffable</title>
9+
<meta name="description"
10+
content="Field note on building a GitHub Actions CI/CD pipeline to deploy a static AWS website to S3 and invalidate CloudFront.">
11+
<meta name="robots" content="index, follow">
12+
<link rel="canonical" href="https://cliffable.com/field-notes/building-my-first-cicd-pipeline.html">
13+
14+
<link rel="stylesheet" href="../styles.css">
15+
16+
<link rel="icon" type="image/x-icon" href="../favicon/favicon.ico">
17+
<link rel="icon" type="image/png" sizes="32x32" href="../favicon/favicon-32x32.png">
18+
<link rel="icon" type="image/png" sizes="16x16" href="../favicon/favicon-16x16.png">
19+
<link rel="apple-touch-icon" sizes="180x180" href="../favicon/apple-touch-icon.png">
20+
21+
<!-- Google tag (gtag.js) -->
22+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-L41X6NGBMK"></script>
23+
24+
<script>
25+
window.dataLayer = window.dataLayer || [];
26+
function gtag() {
27+
dataLayer.push(arguments);
28+
}
29+
gtag('js', new Date());
30+
31+
gtag('config', 'G-L41X6NGBMK');
32+
</script>
33+
</head>
34+
35+
<body>
36+
<header>
37+
<div class="container header-inner">
38+
39+
<div class="logo">
40+
<a href="/">
41+
<img src="/assets/logos/cliffable-logo-white-v2.svg" alt="Cliffable logo" height="60">
42+
</a>
43+
</div>
44+
45+
<nav class="main-nav">
46+
<a href="/projects/">Projects</a>
47+
<a href="/field-notes/">Field Notes</a>
48+
<a href="/about.html">About</a>
49+
<a href="/contact.html">Contact</a>
50+
</nav>
51+
52+
</div>
53+
</header>
54+
55+
<main>
56+
57+
<section class="hero field-note-article-hero">
58+
<div class="container field-note-container">
59+
60+
<p class="field-note-article-meta">
61+
AWS • CI/CD • GitHub Actions
62+
</p>
63+
64+
<p class="field-note-date">
65+
Published 23 May 2026
66+
</p>
67+
68+
<h1>Building my first CI/CD pipeline</h1>
69+
70+
<p class="field-note-intro">
71+
Moving from manual S3 uploads to a GitHub Actions deployment pipeline using OIDC, IAM roles and
72+
CloudFront invalidation.
73+
</p>
74+
75+
</div>
76+
</section>
77+
78+
<section class="field-note-featured-image">
79+
<div class="container field-note-container">
80+
81+
<a href="../assets/images/field-notes/building-my-first-cicd-pipeline/automated-deployment-pipeline.png"
82+
class="lightbox-trigger">
83+
84+
<img src="../assets/images/field-notes/building-my-first-cicd-pipeline/automated-deployment-pipeline.png"
85+
alt="GitHub Actions automated deployment pipeline architecture diagram">
86+
87+
</a>
88+
89+
<p class="field-note-image-caption">
90+
From local code changes to automated live deployment.
91+
</p>
92+
93+
</div>
94+
</section>
95+
96+
<section class="field-note-content-section">
97+
<div class="container field-note-container">
98+
99+
<h2>The original workflow</h2>
100+
<p>
101+
This project started as a way to remove manual deployment steps from the Cliffable static website
102+
workflow.
103+
Previously, changes were uploaded directly to S3 by hand after local testing.
104+
</p>
105+
106+
<p>
107+
While this worked for a small personal site, it quickly became obvious that the process was:
108+
</p>
109+
110+
<ul>
111+
<li>slow</li>
112+
<li>error-prone</li>
113+
<li>difficult to scale</li>
114+
<li>lacking rollback visibility</li>
115+
</ul>
116+
117+
<p>
118+
The goal was to build a lightweight CI/CD pipeline that could automatically deploy changes whenever
119+
code was pushed to the main branch.
120+
</p>
121+
122+
<h2>How the pipeline works</h2>
123+
<p>
124+
The automated deployment pipeline connects GitHub, GitHub Actions and AWS into a single workflow
125+
that deploys website updates automatically whenever new code is pushed to the repository.
126+
</p>
127+
<p>
128+
When code is pushed to the main branch, GitHub Actions automatically triggers a workflow defined
129+
inside the repository. The workflow authenticates to AWS using OpenID Connect (OIDC), temporarily
130+
assumes a tightly scoped IAM role and synchronises the website files to the S3 bucket hosting the
131+
site.
132+
</p>
133+
134+
<p>
135+
After deployment, the workflow creates a CloudFront invalidation so cached files are refreshed
136+
across the CDN. This ensures visitors immediately receive the latest version of the website.
137+
</p>
138+
139+
<h2>Security improvements</h2>
140+
<p>
141+
One of the most important improvements was removing long-lived AWS access keys from the deployment
142+
process. Instead of storing permanent credentials inside GitHub, the workflow uses OpenID Connect
143+
(OIDC) to request temporary credentials directly from AWS.
144+
</p>
145+
146+
<div class="field-note-inline-image">
147+
148+
<a href="../assets/images/field-notes/building-my-first-cicd-pipeline/github-actions-oidc-permissions.png"
149+
class="lightbox-trigger">
150+
151+
<img src="../assets/images/field-notes/building-my-first-cicd-pipeline/github-actions-oidc-permissions.png"
152+
alt="GitHub Actions deploy.yml permissions block using OIDC authentication">
153+
154+
</a>
155+
156+
<p class="field-note-image-caption">
157+
GitHub Actions workflow permissions for OIDC authentication.
158+
</p>
159+
160+
</div>
161+
162+
<p>
163+
GitHub Actions is allowed to assume a tightly scoped IAM role that only grants permission to deploy
164+
the website and create CloudFront invalidations. Access is further restricted to the Cliffable
165+
repository and the main branch.
166+
</p>
167+
168+
<h2>What I learned</h2>
169+
<p>
170+
This project was my first experience building a real CI/CD deployment workflow using GitHub Actions
171+
and AWS. It reinforced how much operational complexity can be removed with relatively simple
172+
automation.
173+
</p>
174+
175+
<p>
176+
It also highlighted the importance of security boundaries in cloud systems. Learning how OIDC trust
177+
relationships, IAM policies and repository restrictions work together gave me a much better
178+
understanding of secure deployment design.
179+
</p>
180+
181+
<p>
182+
Beyond the technical implementation itself, the project changed the way I think about infrastructure
183+
management. Deployments became faster, more repeatable and significantly less stressful than manual
184+
uploads.
185+
</p>
186+
187+
<h2>Conclusion</h2>
188+
<p>
189+
Automating deployments removed a large amount of manual operational overhead from the Cliffable
190+
website workflow while also improving security and consistency. It was a relatively small project
191+
technically, but a significant step forward in understanding how modern cloud deployment pipelines
192+
are designed and operated.
193+
</p>
194+
195+
<h2>Related project</h2>
196+
197+
<p class="field-note-callout">
198+
This deployment pipeline was implemented as part of the
199+
<a href="../projects/csgl-lightsail-wordpress.html">
200+
WordPress on AWS Lightsail
201+
</a>
202+
project.
203+
</p>
204+
</div>
205+
206+
</section>
207+
208+
</main>
209+
210+
<footer>
211+
<div class="container footer-content">
212+
213+
<div class="footer-contact">
214+
<p class="footer-heading">Contact</p>
215+
216+
<div class="footer-links">
217+
<a href="mailto:mailcliffsmith@gmail.com">Email</a>
218+
<a href="https://www.linkedin.com/in/cliff-smith-london/" target="_blank"
219+
rel="noopener noreferrer">LinkedIn</a>
220+
<a href="https://github.com/cliffable" target="_blank" rel="noopener noreferrer">GitHub</a>
221+
</div>
222+
</div>
223+
224+
<div class="footer-meta">
225+
<p>© 2026 Cliff Smith</p>
226+
</div>
227+
228+
</div>
229+
</footer>
230+
231+
<div id="lightbox" class="lightbox">
232+
<img id="lightbox-img" src="" alt="">
233+
</div>
234+
235+
<script>
236+
const lightboxTriggers = document.querySelectorAll('.lightbox-trigger');
237+
const lightbox = document.getElementById('lightbox');
238+
const lightboxImg = document.getElementById('lightbox-img');
239+
240+
lightboxTriggers.forEach(trigger => {
241+
trigger.addEventListener('click', event => {
242+
event.preventDefault();
243+
lightbox.style.display = 'flex';
244+
lightboxImg.src = trigger.href;
245+
});
246+
});
247+
248+
lightbox.addEventListener('click', () => {
249+
lightbox.style.display = 'none';
250+
lightboxImg.src = '';
251+
});
252+
</script>
253+
254+
</body>
255+
256+
</html>

field-notes/index.html

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
8+
<title>Field Notes | Cliffable</title>
9+
<meta name="description"
10+
content="Technical field notes from building, operating and troubleshooting real cloud systems on AWS.">
11+
<meta name="robots" content="index, follow">
12+
<link rel="canonical" href="https://cliffable.com/field-notes/">
13+
14+
<link rel="stylesheet" href="../styles.css">
15+
<link rel="icon" type="image/x-icon" href="../favicon/favicon.ico">
16+
<link rel="icon" type="image/png" sizes="32x32" href="../favicon/favicon-32x32.png">
17+
<link rel="icon" type="image/png" sizes="16x16" href="../favicon/favicon-16x16.png">
18+
<link rel="apple-touch-icon" sizes="180x180" href="../favicon/apple-touch-icon.png">
19+
20+
<!-- Google tag (gtag.js) -->
21+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-L41X6NGBMK"></script>
22+
23+
<script>
24+
window.dataLayer = window.dataLayer || [];
25+
function gtag() { dataLayer.push(arguments); }
26+
gtag('js', new Date());
27+
28+
gtag('config', 'G-L41X6NGBMK');
29+
</script>
30+
</head>
31+
32+
<body>
33+
34+
<header>
35+
<div class="container header-inner">
36+
37+
<div class="logo">
38+
<a href="/">
39+
<img src="/assets/logos/cliffable-logo-white-v2.svg" alt="Cliffable logo" height="60">
40+
</a>
41+
</div>
42+
43+
<nav class="main-nav">
44+
<a href="/projects/">Projects</a>
45+
<a href="/field-notes/">Field Notes</a>
46+
<a href="/about.html">About</a>
47+
<a href="/contact.html">Contact</a>
48+
</nav>
49+
50+
</div>
51+
</header>
52+
53+
<main>
54+
55+
<section class="hero field-notes-hero">
56+
<div class="container">
57+
58+
<h1>Field Notes</h1>
59+
60+
<p>
61+
Technical observations from building, operating and troubleshooting real cloud systems.
62+
</p>
63+
64+
</div>
65+
</section>
66+
67+
<section class="field-notes-list">
68+
<div class="container">
69+
70+
<article class="field-note-card">
71+
72+
<div class="field-note-thumbnail">
73+
74+
<a href="/field-notes/building-my-first-cicd-pipeline.html">
75+
76+
<img src="../assets/images/field-notes/building-my-first-cicd-pipeline/automated-deployment-pipeline.png"
77+
alt="Automated deployment pipeline architecture diagram">
78+
79+
</a>
80+
81+
</div>
82+
83+
<div class="field-note-content">
84+
85+
<p class="field-note-meta">
86+
AWS • CI/CD • GitHub Actions
87+
</p>
88+
89+
<p class="field-note-date">
90+
Published 23 May 2026
91+
</p>
92+
93+
<h2>
94+
<a href="/field-notes/building-my-first-cicd-pipeline.html">
95+
Building my first CI/CD pipeline
96+
</a>
97+
</h2>
98+
99+
<p class="field-note-summary">
100+
Moving from manual S3 uploads to a GitHub Actions deployment pipeline using OIDC, IAM roles and CloudFront
101+
invalidation.
102+
</p>
103+
104+
<a href="/field-notes/building-my-first-cicd-pipeline.html" class="field-note-link">
105+
Read note →
106+
</a>
107+
108+
</div>
109+
110+
</article>
111+
112+
</div>
113+
</section>
114+
115+
</main>
116+
117+
<footer>
118+
<div class="container footer-content">
119+
120+
<div class="footer-contact">
121+
<p class="footer-heading">Contact</p>
122+
123+
<div class="footer-links">
124+
<a href="mailto:mailcliffsmith@gmail.com">Email</a>
125+
<a href="https://www.linkedin.com/in/cliff-smith-london/" target="_blank"
126+
rel="noopener noreferrer">LinkedIn</a>
127+
<a href="https://github.com/cliffable" target="_blank" rel="noopener noreferrer">GitHub</a>
128+
</div>
129+
</div>
130+
131+
<div class="footer-meta">
132+
<p>© 2026 Cliff Smith</p>
133+
</div>
134+
135+
</div>
136+
</footer>
137+
138+
</body>
139+
140+
</html>

0 commit comments

Comments
 (0)