From aae506524243c142d99da0aba1ebf92c1aa7ff53 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 11:09:15 -0700 Subject: [PATCH 1/3] Fixed dev env --- .github/workflows/deploy-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 2855267..8ddfa4d 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -3,7 +3,7 @@ name: Deploy to Development on: push: branches: - - main + - dev permissions: id-token: write From 167e6b7d33c62d9671269ae36c5c7ba8f0b8d663 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 11:43:26 -0700 Subject: [PATCH 2/3] Fixed staging env --- .github/workflows/deploy-staging.yml | 27 ++++++++++++------- .../environments/staging/.terraform.lock.hcl | 25 +++++++++++++++++ terraform/environments/staging/outputs.tf | 8 ++++++ 3 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 terraform/environments/staging/.terraform.lock.hcl diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 76ea1fd..8ac2534 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -1,4 +1,4 @@ -name: Deploy Dev +name: Deploy to Staging on: push: @@ -9,6 +9,10 @@ permissions: id-token: write contents: read +concurrency: + group: staging-deployment + cancel-in-progress: true + jobs: deploy: runs-on: ubuntu-latest @@ -17,7 +21,7 @@ jobs: defaults: run: - working-directory: vite-app + working-directory: . steps: - uses: actions/checkout@v4 @@ -25,22 +29,27 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 22 + cache: npm + cache-dependency-path: package-lock.json - - run: npm install - - - run: npm run test -- --run + - name: Install dependencies and build + run: npm ci - - run: npm run build + - name: Build application + run: npm run build - - uses: aws-actions/configure-aws-credentials@v4 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-east-1 - - run: | + - name: Sync files to S3 + run: | aws s3 sync dist/ s3://${{ secrets.S3_BUCKET }} --delete - - run: | + - name: Invalidate CloudFront distribution + run: | aws cloudfront create-invalidation \ --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \ --paths "/*" diff --git a/terraform/environments/staging/.terraform.lock.hcl b/terraform/environments/staging/.terraform.lock.hcl new file mode 100644 index 0000000..92a2bcc --- /dev/null +++ b/terraform/environments/staging/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.100.0" + constraints = "~> 5.0" + hashes = [ + "h1:H3mU/7URhP0uCRGK8jeQRKxx2XFzEqLiOq/L2Bbiaxs=", + "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", + "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", + "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", + "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b", + "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862", + "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93", + "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2", + "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e", + "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421", + "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4", + "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9", + "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9", + "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70", + ] +} diff --git a/terraform/environments/staging/outputs.tf b/terraform/environments/staging/outputs.tf index 3701b68..ff2e8f3 100644 --- a/terraform/environments/staging/outputs.tf +++ b/terraform/environments/staging/outputs.tf @@ -4,4 +4,12 @@ output "distribution_domain_name" { output "bucket_name" { value = module.s3_static_site.bucket_name +} + +output "github_actions_role_arn" { + value = module.github_oidc.github_actions_role_arn +} + +output "cloudfront_distribution_id" { + value = module.cloudfront.distribution_id } \ No newline at end of file From f4b3e91e1a7ef390e26adba5afa1dc687e939ed5 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 16:41:52 -0700 Subject: [PATCH 3/3] Updated readme.md documentation file --- README.md | 283 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) diff --git a/README.md b/README.md index b24dda6..1fa3af5 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,286 @@ terraform init -reconfigure -backend-config=dev.tfbackend echo "Repository: ${{ github.repository }}" echo "Ref: ${{ github.ref }}" echo "Environment: prod" + +# ๐Ÿš€ Terraform S3 Static App Project ( Reusable and Self-bootstrapping) + +A **reusable, self-bootstrapping infrastructure template** for deploying modern **React (Vite) static applications** to AWS using: + +- ๐Ÿชฃ Amazon S3 (static hosting) +- ๐ŸŒ Amazon CloudFront (global CDN) +- ๐Ÿ” AWS IAM + GitHub OIDC (secure CI/CD authentication) +- โš™๏ธ Terraform (infrastructure as code) +- โšก GitHub Actions (multi-environment CI/CD: dev, staging, prod) + +This project is designed as a **drop-in frontend deployment foundation** for any Vite + React application that needs scalable AWS hosting with automated deployments. + +--- + +## ๐Ÿงฑ Architecture Overview + +This system provisions and connects: + +- **React + Vite App** + - Built and deployed via GitHub Actions + +- **S3 Bucket** + - Stores built static assets + - Private bucket (no public access) + +- **CloudFront Distribution** + - Serves content globally + - Handles caching and HTTPS + +- **IAM OIDC Role (GitHub Actions)** + - Secure, keyless AWS authentication + - Least privilege access for deployment + +- **Terraform Modules** + - S3 static site module + - CloudFront module + - IAM OIDC module + - Environment-based configuration + +--- + +## ๐Ÿ“ Project Structure + +``` +. +โ”œโ”€โ”€ src/ # React (Vite) application source code +โ”‚ +โ”œโ”€โ”€ public/ # Static assets served directly (faviconimages, etc.) +โ”‚ +โ”œโ”€โ”€ index.html # Vite entry HTML file +โ”œโ”€โ”€ package.json + other configs # Project dependencies and scripts +โ”‚ +โ”œโ”€โ”€ terraform/ # Infrastructure as Code (Terraform) directory +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ bootstrap/ # One-time setup (state backend, foundational resource) +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ modules/ # Reusable Terraform modules +โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”œโ”€โ”€ s3-static-site/ # S3 bucket + static hosting configuration +โ”‚ โ”‚ โ”œโ”€โ”€ cloudfront/ # CloudFront CDN distribution setup +โ”‚ โ”‚ โ”œโ”€โ”€ iam-oidc/ # GitHub Actions OIDC IAM role configuration +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ environments/ # Environment-specific configurations +โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”œโ”€โ”€ dev/ # Development environment +โ”‚ โ”‚ โ”œโ”€โ”€ staging/ # Staging environment +โ”‚ โ”‚ โ”œโ”€โ”€ prod/ # Production environment +โ”‚ +โ”œโ”€โ”€ .github/workflows/ # CI/CD pipelines (GitHub Actions) +โ”‚ โ”œโ”€โ”€ deploy-dev.yml # Dev deployment workflow +โ”‚ โ”œโ”€โ”€ deploy-staging.yml # Staging deployment workflow +โ”‚ โ”œโ”€โ”€ deploy-prod.yml # Production deployment workflow +โ”‚ +โ””โ”€โ”€ README.md # Project documentation + +``` + +--- + +## โšก Features + +This project was built with: + +- Fully automated CI/CD pipeline (GitHub Actions) +- Secure AWS authentication using OIDC (no long-lived AWS keys) +- Environment-based deployments (dev / staging / prod) +- CloudFront invalidation on every deployment +- Reusable Terraform modules for multi-project usage +- Production-ready S3 security configuration +- Clean separation of infrastructure and frontend build + +--- + +## ๐Ÿ“ฆ Prerequisites + +Before using this project, ensure you have: + +- AWS Account +- Terraform โ‰ฅ 1.10+ (Required for S3 file lock feature introduced in v.1.10. enabling file lock in S3 allows us to lock our state file without the need for DynamoDB + S3 lock feature which is being deprecated by AWS) +- Node.js โ‰ฅ 20+ +- GitHub repository +- AWS CLI configured (for local testing) + +--- + +## ๐Ÿš€ Getting Started + +1. Clone the repository + +``` +git clone https://github.com/ecoderP/s3-static-app-terraform.git +``` + +### Before you continue, Please note: + +- There are preset customisable terraform variables in .tfvarsexample. +- Terraform state backend configurations are in .tfbackendexample files. + +These are so named to bypass .gitignore. Gitgnore will ignore all .tfvars and .tfbackend files for security. You will need to rename .tfvarsexample and .tfbackendexample to .tfvars and .tfbackend extensions respectively. + +For example, for terraform/bootstrap/ directory, update configuration settings, then: + +``` +cd terraform/bootstrap + +cp terraform.tfvarsbackendexample terraform.tfvars +``` + +2. In the terraform/bootstrap folder + +- Personalise variables +- Initialise terraform + +``` +terraform init +``` + +**_Important:_** Copy the bucket name from terminal output. This is the shared backend state bucket name for all environments. Use this output as bucket name in .tfbackend for all environments. + +4. Configure environment + +Each environment (dev/staging/prod) has its own configuration. Locate .tfbackend and .tfvars configuration files, personalise and rename for each environment. + +``` +cd terraform/environments/dev + +cp dev.tfbackendexample dev.tfbackend + +terraform init -backend-config=dev.tfbackend +``` + +5. Validate code and Deploy Infrastructure for each environment + +``` +terraform validate +terraform plan +terraform apply -auto-approve +``` + +--- + +## ๐Ÿ” GitHub OIDC Authentication + +This project uses GitHub Actions โ†’ AWS OIDC federation, meaning: + +โœ” No AWS access keys stored in GitHub + +โœ” Temporary credentials issued per workflow run + +โœ” Least-privilege IAM roles scoped per environment + +### IAM Role Trust Relationship + +GitHub Actions assumes a role like: + +- Repository: Your-github-username/repo-name +- Branch-based conditions: + - dev โ†’ dev role + - staging โ†’ staging role + - main โ†’ production role + +--- + +## โš™๏ธ CI/CD Pipeline + +This project includes GitHub Actions workflows for: + +### ๐Ÿงช Dev Deployment + +- Trigger: push to dev +- Deploys to dev S3 bucket + CloudFront + +### ๐Ÿงฑ Staging Deployment + +- Trigger: push to staging +- Deploys to staging S3 bucket + CloudFront +- Used for pre-production validation + +### ๐Ÿš€ Production Deployment + +- Trigger: push to main +- Deploys stable build to production environment (S3 + CloudFront) + +### CI/CD Flow + +1. Checkout code +2. Install dependencies +3. Build Vite React app +4. Assume AWS role via OIDC +5. Sync build to S3 +6. Invalidate CloudFront cache + +### Important GitHub Actions secrets + +To get your CI/CD pipeline working, add the following environment secrets to GitHub Actions: + +- S3_BUCKET +- CLOUDFRONT_DISTRIBUTION_ID +- AWS_ROLE_ARN + +To get the values for your secrets, from each environment directory (dev, staging, prod), run: + +``` +terraform output +``` + +--- + +## โ™ป๏ธ Re-using This Project (Some Viable Options) + +This repo is designed as a starter backend infrastructure for any React + Vite frontend project. + +### Option 1: Use as a Terraform Module + +``` +module "frontend_hosting" { + source = "github.com/ecoderP/s3-static-app-terraform//modules/s3-static-site" + + bucket_name = "my-new-app" + environment = "dev" +} +``` + +### Option 2: Multi-App Scaling + +You can reuse this setup for: + +- Portfolio sites +- SaaS frontend dashboards +- Admin panels +- Marketing landing pages +- Micro-frontends + +Just change: + +- bucket name +- CloudFront config +- environment variables + +--- + +## ๐Ÿ” Security Highlights + +- S3 bucket is private by default +- CloudFront serves as the only public entry point +- IAM follows least privilege principle +- GitHub Actions uses short-lived credentials (OIDC) +- No hardcoded secrets in repo + +--- + +## ๐Ÿ“ˆ Future Improvements + +- Add custom domain + Route53 automation +- ACM SSL certificate provisioning +- Automated performance testing in CI + +--- + +## ๐Ÿ‘จโ€๐Ÿ’ป Author + +Built and Maintained by [ecoderP](https://github.com/ecoderP)