Static to Production

Chapter 1: What You’re Actually Building

2,587 words

General
Report

Static to Production

AWS Without the Noise, Book 1

Copyright © 2026 Victor Ogechukwu Ojeje. All rights reserved.

No part of this publication may be reproduced, distributed, or transmitted in any form or by any means, including photocopying, recording, or other electronic or mechanical methods, without the prior written permission of the author, except in the case of brief quotations embodied in critical reviews and certain other noncommercial uses permitted by copyright law.

This book is provided for educational purposes. The author has made every effort to ensure the technical content is accurate at the time of writing, but cloud services, pricing, and tooling change frequently. The author and publisher assume no liability for errors or omissions, or for any loss, damage, or cost, including cloud usage charges, resulting from the use of the information in this book. Review current provider documentation and pricing before you build.

Amazon Web Services, AWS, Amazon S3, and Amazon CloudFront are trademarks of Amazon.com, Inc. or its affiliates. GitHub and GitHub Actions are trademarks of GitHub, Inc. Terraform is a trademark of HashiCorp, Inc. All other trademarks are the property of their respective owners. This book is an independent work and is not affiliated with, endorsed by, or sponsored by any of these companies.

The project source code discussed in this book is available at github.com/escanut/aws-terraform-s3-cloudfront-cicd and github.com/escanut/aws-s3-static-site-cicd.

First edition, 2026

Independently published.

Contents

Introduction:
How This Book Works

What This Book Builds

This book builds a static website hosting pipeline on AWS. When you finish, you will have:

A static website stored in Amazon S3

A CloudFront CDN (content delivery network) distribution serving that site over HTTPS

A GitHub Actions pipeline that deploys every push to main automatically, in about 30 seconds

No AWS access keys stored anywhere: not in GitHub, not in environment variables, not in a config file

Managed platforms like Netlify and Vercel run this same architecture behind a single button. This book skips the button. You build each component yourself, so you can explain every decision to an interviewer or a colleague.

The last bullet is the security goal that shapes the whole project. Most tutorials tell you to create an IAM (Identity and Access Management) user, generate an access key, and paste it into GitHub as a secret. This book replaces that key with short-lived credentials issued on demand. Chapter 7 explains how.

The Two Repositories

The project spans two public GitHub repositories. You will read and modify both.

Repository

Contains

Changes

Terraform files that define all AWS infrastructure

Rarely, and with careful review

The three site files and the GitHub Actions deployment workflow

Constantly, with every content update

The split is deliberate. Infrastructure and content have different change cadences. A page edit should trigger a deployment without anyone touching the infrastructure configuration. A security policy change should get a slow, deliberate review without touching the site. Keeping them apart gives each its own history and its own review process. Production teams do the same.

The infrastructure repository has no deployment workflow. The site repository has no Terraform. Each build step in this book tells you which repository you are working in. Check before you run anything.

The site itself is three files: index.html, error.html, and style.css. The infrastructure is the subject of this book, not the content.

Cost

Everything in this book runs within the AWS Free Tier for a new account. The author’s own estimate is about $0.92 per month at low traffic. CloudFront data transfer beyond the free tier threshold is the only variable cost, and it depends on how much traffic your site receives. Running through the builds will not produce a meaningful bill.

What You Need Before Chapter 1

Operating system. Every command in this book runs in a bash terminal.

Ubuntu 20.04 or later works without modification.

macOS works identically in Terminal or iTerm2.

On Windows, use WSL2 (Windows Subsystem for Linux), which runs a Linux environment inside Windows without a separate virtual machine. A Linux VM in VMware Workstation or VirtualBox also works. Use Ubuntu or Debian, and allocate at least 2 vCPUs and 4 GB of RAM to avoid slow Terraform runs.

On Windows, run every command from inside the Linux environment. Do not use PowerShell or Command Prompt. The syntax differs and the tooling assumes Unix.

Accounts and tools.

An AWS account on the free tier

A GitHub account

Git

Terraform version 1.0 or higher

The AWS CLI, configured with credentials for your account

A knowledge base in Notion or Obsidian. Pick one. Every chapter ends with a prompt to document what you built.

Confirm the tools are installed before you continue:

git --version

terraform -version

aws --version

aws sts get-caller-identity

The last command should print your AWS account ID and the ARN of the identity you are logged in as. If any command fails, install or configure that tool now.

How Each Chapter Works

Every chapter follows the same sequence. It states the problem, explains the underlying concept in plain computing terms, maps that concept to the AWS service, and then builds it. Each chapter closes with a recap, common mistakes, and a documentation prompt for your knowledge base.

Two conventions to know in advance:

Author’s Note callouts mark places where the source code uses a pattern the author would change today. Each one explains what the pattern is, why it exists, and what the better approach looks like. Read them. The source code is real, and real code has imperfections you should recognize and not copy.

Terraform commands are held back. The early chapters have you read the configuration. Chapter 8 sets up the state backend and runs the full apply. Do not run terraform init before then. It will fail, and Chapter 3 explains why.

The Chapter Arc

Chapters 1 to 3 give you the mental model, a plan, and the Terraform foundation. Chapters 4 to 7 cover the resources in turn: S3, CloudFront, IAM, and OIDC. Chapter 8 sets up remote state and applies everything. Chapter 9 wires the deployment pipeline. Chapter 10 traces one commit through the finished system and verifies each layer.

Chapter 1:
What You’re Actually Building

Context

The introduction told you what the finished system does. This chapter explains why it is shaped the way it is.

The central question is what kind of server a website needs. Once you can answer that, the choice of S3 and CloudFront stops looking like a preference and starts looking like the obvious fit. This chapter also gets you oriented in both repositories, so the build chapters do not begin with you hunting for files.

Concept: File Servers and Application Servers

There is a distinction that most people in this field learn by osmosis over years of work. Understanding it clearly will make everything else in this book faster to grasp.

When a request comes into a web server, the server does one of two fundamentally different things. Either it finds a file and sends it, or it runs code and sends whatever that code produces.

The first kind is a file server. You request https://example.com/about, and the server looks in its configured directory, finds a file called about.html, and sends it. Nothing is computed. Nothing is generated. The same file goes to every visitor who makes that request.

The second kind is an application server. You request https://example.com/about, and the server runs a script (Python, Node.js, PHP, Ruby) that might query a database, check a session cookie, and construct an HTML response on the spot before sending it. The response can differ for every visitor.

nginx and Apache can operate in both modes, which is part of why they are confusing to learn from. In file-serving mode, you configure a root directory and nginx maps URL paths to files in that directory. In application mode, you configure a proxy that forwards requests to a Python or Node process elsewhere.

A static site is one where every page can be served as a plain file. No database queries. No session handling. No server-side code execution. Every visitor who requests the same URL gets exactly the same file.

The practical implication: you do not need a running server process to host a static site. You need a place to store files and a mechanism to deliver them to visitors. Amazon S3 is the place to store files. CloudFront is the delivery mechanism. Together, they replace a server you would otherwise have to provision, patch, monitor, and maintain.

AWS Mapping: What Each Component Does

You will build these components one at a time. Here is a one-paragraph preview of each. The purpose right now is orientation: knowing what you are looking at before you build it.

S3 (Simple Storage Service) stores the files. It is not a server. It is object storage: a bucket that holds files and can be configured to serve them over HTTP when requested by URL. Think of it as a filesystem accessible over the internet, with access control over who can read what.

CloudFront is AWS’s content delivery network. It sits in front of S3 and caches your files at edge locations around the world. These are data centers distributed across continents. When a visitor requests your site, CloudFront serves the cached file from the nearest edge location rather than fetching it fresh from the origin bucket each time. Lower latency for visitors, reduced load on your origin.

IAM (Identity and Access Management) controls which entities are permitted to perform which actions on which AWS resources. In this project, the GitHub Actions workflow needs permission to write files to S3 and to invalidate the CloudFront cache. IAM defines exactly those two permissions, scoped to the specific bucket and distribution, and nothing more.

OIDC (OpenID Connect) is how GitHub Actions proves its identity to AWS without storing credentials. GitHub acts as an identity provider. AWS is configured to trust that provider. When the workflow runs, it presents a short-lived signed token, and AWS exchanges it for temporary credentials that expire when the job finishes.

Terraform creates and manages all of the above. You write resource definitions in .tf files. Terraform compares those definitions against what currently exists in your AWS account and makes the necessary changes. The infrastructure is version-controlled, auditable, and reproducible from scratch.

GitHub Actions runs the deployment workflow. On every push to main, Actions authenticates with AWS, syncs the updated files to S3, and invalidates the CloudFront cache so the new version goes live.

That is the full system. Every chapter from here builds one piece of it.

Build: Get Oriented in Both Repositories

This chapter does not provision any infrastructure. You clone both repositories and read the source files. Working in the wrong repository is a consistent source of confusion in the early chapters, so do not skip this.

Confirm the prerequisites from the introduction are in place before you start.

Step 1: Clone the infrastructure repository

Open a terminal and run:

git clone https://github.com/escanut/aws-terraform-s3-cloudfront-cicd.git

cd aws-terraform-s3-cloudfront-cicd

ls -la

You should see: main.tf, backend.tf, variables.tf, outputs.tf, .terraform.lock.hcl.

Do not run any Terraform commands yet. The backend.tf file contains a placeholder state bucket name, YOUR-IDENTIFIER-tf-state, that does not point to a real bucket. Running terraform init right now will fail. Chapter 8 walks you through creating your own state bucket and replacing the placeholder.

Open main.tf and scroll through it. Do not try to understand every block. You are building a mental map of the file: what resource types appear, roughly in what order, and what names are used. You will return to every section in detail.

Step 2: Clone the static site repository

Open a second terminal window and keep the first one open in the infrastructure directory. Run:

git clone https://github.com/escanut/aws-s3-static-site-cicd.git

cd aws-s3-static-site-cicd

ls -la

You should see a build/ directory and a .github/ directory.

Step 3: Read the site files

cat build/index.html

Read it in full. It is short. Note what it does and does not do: plain HTML, no JavaScript framework, no server-side templating. The same content renders for every visitor. This file gets uploaded to S3 and served through CloudFront once the infrastructure exists.

cat build/error.html

S3’s static website configuration accepts an error document: the file to serve when a visitor requests a path that does not exist. This is it.

cat build/style.css

A small stylesheet. Both HTML files reference it via a relative path. When the deployment workflow runs aws s3 sync, all three files are uploaded as individual objects. The relative path works because all three land in the same bucket with the same directory structure they have locally.

Step 4: Read the deployment workflow

cat .github/workflows/deploy.yml

You do not need to understand this file yet. Note its shape: triggered on push to main, permission declarations at the top, AWS authentication steps in the middle, S3 sync and CloudFront invalidation at the end. Chapters 7 and 9 explain every part.

Step 5: Confirm which repository does which job

Run this check in each repository directory:

ls *.tf 2>/dev/null; ls .github/workflows 2>/dev/null

The infrastructure repository lists .tf files and no workflows. The site repository lists deploy.yml and no .tf files. If you ever see both in one place, you are in the wrong directory or have copied files between repositories.

What You Just Did

You cloned both repositories and read through the source files. You now have a concrete picture of the system: three files that make up a static website, a GitHub Actions workflow that deploys them, and a set of Terraform files that creates the AWS infrastructure those deployments target. You also know which repository holds which job.

You have not provisioned anything. Chapter 2 walks through the architecture diagram of the full system before any tooling is opened.

Common Mistakes and How to Catch Them

Running terraform init before replacing the placeholder in backend.tf. The file references a bucket named YOUR-IDENTIFIER-tf-state. Terraform backend blocks cannot use variables, so you replace the value by hand with your own bucket name. Running init first produces an error because the bucket does not exist. No Terraform commands until Chapter 8.

Treating the site files as the subject of the book. The three files in build/ exist so there is something real to deploy. If you want to swap in a Next.js or Hugo site before the infrastructure exists, you are redirecting effort away from the objective. Deploy what is there first. Customize later.

Forking instead of cloning for initial reading. If you fork the site repository and push to main, the GitHub Actions workflow triggers and fails, because it references an IAM role ARN that belongs to the author’s account. Clone to read. The book tells you when to create your own fork with your own configuration.

Self-Documentation

Open your knowledge base (Notion or Obsidian). If you have not set one up yet, do it now. Create a new page titled: Book 1 — Static to Production.

Under that page, record the following:

The names and URLs of both repositories, and one sentence describing what each is responsible for

Your own definition of what a static site is, written in your own words

The six components of this project (S3, CloudFront, IAM, OIDC, Terraform, GitHub Actions), each with one sentence on the role it plays

Any questions that came up while reading the source files. Even half-formed questions are worth capturing now.

By Chapter 10, this page should document the full system clearly enough that a colleague who had never seen the project could understand what was built and why.

Share this chapter

Comments

0 comments

No comments yet. Start the discussion after you finish reading.