From b9d86974c0e1d411bbf36f7f63be3bd5c681f0ef Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 11:59:17 -0700 Subject: [PATCH 1/7] upgrading deploy-dev.yaml file to github Actions latest major version - Node.js 24 --- .github/workflows/deploy-dev.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 8ddfa4d..71f4fd2 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -9,6 +9,9 @@ permissions: id-token: write contents: read +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + concurrency: group: development-deployment cancel-in-progress: true @@ -24,9 +27,11 @@ jobs: working-directory: . steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - name: Setup Node.js + uses: actions/setup-node@v5 with: node-version: 22 cache: npm From 99185f3c8d9d8fd9ca3ee7a161f589ae809c2194 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 12:05:59 -0700 Subject: [PATCH 2/7] upgraded deploy-dev.yaml file to github Actions latest major version - Node.js 24 --- .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 71f4fd2..cffd5fa 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -50,7 +50,7 @@ jobs: echo "Environment: dev" - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v5 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-east-1 From 326f6f71b1b1502e13bac4c2e6806951de411be9 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 15:54:32 -0700 Subject: [PATCH 3/7] Completed readme.md documentation file --- .gitignore | 2 +- README.md | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 279 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index dc93deb..5c836a9 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,7 @@ crash.*.log # Exclude all .tfvars files, which are likely to contain sensitive data *.tfvars *.tfvars.json -.tfvars* +.tfvars # Ignore override files override.tf diff --git a/README.md b/README.md index b24dda6..a57e5f3 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,280 @@ -terraform init -migrate-state -backend-config=dev.tfbackend -terraform init -reconfigure -backend-config=dev.tfbackend +# ๐Ÿš€ Terraform S3 Static App Project ( Reusable and Self-bootstrapping) -# Just commenting random stuff for debugging +A **reusable, self-bootstrapping infrastructure template** for deploying modern **React (Vite) static applications** to AWS using: -- name: Debug GitHub Context - run: | - echo "Repository: ${{ github.repository }}" - echo "Ref: ${{ github.ref }}" - echo "Environment: prod" +- ๐Ÿชฃ 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 (Typical) + +``` +. +โ”œโ”€โ”€ 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) +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ bootstrap/ # One-time setup (state backend, foundational resources) +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ 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 + +- 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+ +- 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: + +- I have preset customisable terraform variables in .tfvarsexample. +- Terraform state backend configurations are in .tfbackendexample files. + +I named these this way to bypass .gitignore, because git will ignore all .tfvars and .tfbackend files. You will need to rename .tfvarsexample and .tfbackendexample to .tfvars and .tfbackend extensions respectively. + +For example, for bootstrap/ directory: + +``` +cd terraform/bootstrap +``` + +After updating your AWS region, project name and your unique bucket name: + +``` +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 develop +- Deploys to dev S3 bucket + CloudFront + +### ๐Ÿงฑ Staging Deployment + +- Trigger: push to staging +- Used for pre-production validation + +### ๐Ÿš€ Production Deployment + +- Trigger: push to main +- Deploys stable build to production environment + +### 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 project, from each environment directory (dev, staging, prod), run: + +``` +terraform output +``` + +--- + +## โ™ป๏ธ How to Re-use This Project (Important) + +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) From 40d243312949a926ed32bccc0019e2d334e0cc94 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 16:42:33 -0700 Subject: [PATCH 4/7] Updated readme.md documentation file --- README.md | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a57e5f3..2cdd361 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ This system provisions and connects: --- -## ๐Ÿ“ Project Structure (Typical) +## ๐Ÿ“ Project Structure ``` . @@ -50,9 +50,9 @@ This system provisions and connects: โ”œโ”€โ”€ index.html # Vite entry HTML file โ”œโ”€โ”€ package.json + other configs # Project dependencies and scripts โ”‚ -โ”œโ”€โ”€ terraform/ # Infrastructure as Code (Terraform) +โ”œโ”€โ”€ terraform/ # Infrastructure as Code (Terraform) directory โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€ bootstrap/ # One-time setup (state backend, foundational resources) +โ”‚ โ”œโ”€โ”€ bootstrap/ # One-time setup (state backend, foundational resource) โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ modules/ # Reusable Terraform modules โ”‚ โ”‚ โ”‚ @@ -79,6 +79,8 @@ This system provisions and connects: ## โšก 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) @@ -94,7 +96,7 @@ This system provisions and connects: Before using this project, ensure you have: - AWS Account -- Terraform โ‰ฅ 1.10+ +- 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) @@ -111,20 +113,16 @@ git clone https://github.com/ecoderP/s3-static-app-terraform.git ### Before you continue, Please note: -- I have preset customisable terraform variables in .tfvarsexample. +- There are preset customisable terraform variables in .tfvarsexample. - Terraform state backend configurations are in .tfbackendexample files. -I named these this way to bypass .gitignore, because git will ignore all .tfvars and .tfbackend files. You will need to rename .tfvarsexample and .tfbackendexample to .tfvars and .tfbackend extensions respectively. +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 bootstrap/ directory: +For example, for terraform/bootstrap/ directory, update configuration settings, then: ``` cd terraform/bootstrap -``` -After updating your AWS region, project name and your unique bucket name: - -``` cp terraform.tfvarsbackendexample terraform.tfvars ``` @@ -140,7 +138,8 @@ 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. + +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 @@ -165,7 +164,9 @@ terraform apply -auto-approve 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 @@ -186,18 +187,19 @@ This project includes GitHub Actions workflows for: ### ๐Ÿงช Dev Deployment -- Trigger: push to develop +- 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 +- Deploys stable build to production environment (S3 + CloudFront) ### CI/CD Flow @@ -210,13 +212,13 @@ This project includes GitHub Actions workflows for: ### Important GitHub Actions secrets -To get your ci/cd pipeline working, add the following environment secrets to GitHub Actions: +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 project, from each environment directory (dev, staging, prod), run: +To get the values for your secrets, from each environment directory (dev, staging, prod), run: ``` terraform output @@ -224,7 +226,7 @@ terraform output --- -## โ™ป๏ธ How to Re-use This Project (Important) +## โ™ป๏ธ Re-using This Project (Some Viable Options) This repo is designed as a starter backend infrastructure for any React + Vite frontend project. From 27ad145f46ca6d7984e2660afc9f4c82da289361 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 22:37:56 -0700 Subject: [PATCH 5/7] Added .tfvarsexample and .tfbackendexample files --- terraform/bootstrap/terraform.tfvarsexample | 4 ++++ terraform/environments/dev/dev.tfbackendexample | 5 +++++ terraform/environments/dev/terraform.tfvarsexample | 6 ++++++ terraform/environments/prod/prod.tfbackendexample | 5 +++++ terraform/environments/prod/terraform.tfvarsexample | 6 ++++++ terraform/environments/staging/staging.tfbackendexample | 5 +++++ terraform/environments/staging/terraform.tfvarsexample | 6 ++++++ 7 files changed, 37 insertions(+) create mode 100644 terraform/bootstrap/terraform.tfvarsexample create mode 100644 terraform/environments/dev/dev.tfbackendexample create mode 100644 terraform/environments/dev/terraform.tfvarsexample create mode 100644 terraform/environments/prod/prod.tfbackendexample create mode 100644 terraform/environments/prod/terraform.tfvarsexample create mode 100644 terraform/environments/staging/staging.tfbackendexample create mode 100644 terraform/environments/staging/terraform.tfvarsexample diff --git a/terraform/bootstrap/terraform.tfvarsexample b/terraform/bootstrap/terraform.tfvarsexample new file mode 100644 index 0000000..0bb43c2 --- /dev/null +++ b/terraform/bootstrap/terraform.tfvarsexample @@ -0,0 +1,4 @@ +aws_region = "Your AWS region" +project_name = "Your project name" +backend_bucket_name = "Your unique bucket name" +s3_encryption_algorithm = "AES256" # Default to SSE-S3 diff --git a/terraform/environments/dev/dev.tfbackendexample b/terraform/environments/dev/dev.tfbackendexample new file mode 100644 index 0000000..3d94a31 --- /dev/null +++ b/terraform/environments/dev/dev.tfbackendexample @@ -0,0 +1,5 @@ +bucket = "Your bootstrap backend state bucket name" +region = "Your AWS region" +key = "dev/terraform.tfstate" +encrypt = true +use_lockfile = true # important for S3 backend state lock \ No newline at end of file diff --git a/terraform/environments/dev/terraform.tfvarsexample b/terraform/environments/dev/terraform.tfvarsexample new file mode 100644 index 0000000..836681f --- /dev/null +++ b/terraform/environments/dev/terraform.tfvarsexample @@ -0,0 +1,6 @@ +aws_region = "Your AWS region" +project_name = "Project name" +website_bucket_name = "Your unique bucket name" # unique for each environment +github_repo = "Your-github-username/repo-name" # Not web link +github_branch = "dev" +environment = "dev" diff --git a/terraform/environments/prod/prod.tfbackendexample b/terraform/environments/prod/prod.tfbackendexample new file mode 100644 index 0000000..93dbd39 --- /dev/null +++ b/terraform/environments/prod/prod.tfbackendexample @@ -0,0 +1,5 @@ +bucket = "Your bootstrap backend state bucket name" +region = "Your AWS region" +key = "prod/terraform.tfstate" +encrypt = true +use_lockfile = true # important for S3 backend state lock \ No newline at end of file diff --git a/terraform/environments/prod/terraform.tfvarsexample b/terraform/environments/prod/terraform.tfvarsexample new file mode 100644 index 0000000..027ad41 --- /dev/null +++ b/terraform/environments/prod/terraform.tfvarsexample @@ -0,0 +1,6 @@ +aws_region = "Your AWS region" +project_name = "Project name" +website_bucket_name = "Your unique bucket name" +github_repo = "Your github username/repo name" # Not web link +github_branch = "prod" +environment = "prod" diff --git a/terraform/environments/staging/staging.tfbackendexample b/terraform/environments/staging/staging.tfbackendexample new file mode 100644 index 0000000..fb4c21e --- /dev/null +++ b/terraform/environments/staging/staging.tfbackendexample @@ -0,0 +1,5 @@ +bucket = "Your bootstrap backend state bucket name" +region = "Your AWS region" +key = "staging/terraform.tfstate" +encrypt = true +use_lockfile = true # important for S3 backend state lock \ No newline at end of file diff --git a/terraform/environments/staging/terraform.tfvarsexample b/terraform/environments/staging/terraform.tfvarsexample new file mode 100644 index 0000000..361647c --- /dev/null +++ b/terraform/environments/staging/terraform.tfvarsexample @@ -0,0 +1,6 @@ +aws_region = "Your AWS region" +project_name = "Project name" +website_bucket_name = "Your unique bucket name" # unique for each environment +github_repo = "Your github username/repo name" # Not web link +github_branch = "staging" +environment = "staging" From 7ae802328ec080483b756049c82b5e0e08ffc071 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 22:54:46 -0700 Subject: [PATCH 6/7] Updated readme.md --- README.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2cdd361..cd9a00a 100644 --- a/README.md +++ b/README.md @@ -264,8 +264,32 @@ Just change: - 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 + +--- + +## ๐Ÿงฑ Infrastructure Teardown + +1. From each environment directory (dev, staging and prod), run the command: + +``` +terraform destroy -auto-approve +``` + +Do this for all environments. + +2. From the terraform **_bootstrap_** directory, run the command: + +``` +terraform destroy -auto-approve + +``` + +3. Optional, but can do: List all AWS buckets in your account and confirmbuckets are not listed. Run the command: + +``` +aws s3 ls + +``` --- From e1c2f287e1a9a093101547f91bfade052a7168a4 Mon Sep 17 00:00:00 2001 From: ecoderP Date: Sat, 23 May 2026 23:30:25 -0700 Subject: [PATCH 7/7] Updated readme.md --- README.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/README.md b/README.md index cd9a00a..1010aed 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,115 @@ aws s3 ls --- +## ๐Ÿ“š Lessons Learned + +### 1. Infrastructure Modularity Matters Early + +Breaking infrastructure into reusable Terraform modules made the project significantly easier to maintain and scale across environments. + +Separating: + +- S3 configuration +- CloudFront setup +- IAM/OIDC authentication + +allowed infrastructure changes to be isolated without affecting the entire stack. + +### 2. OIDC Authentication Is More Secure Than Long-Lived AWS Keys + +Using GitHub OIDC federation eliminated the need to store AWS access keys in GitHub Secrets. + +This project provided hands-on experience with: + +- IAM trust policies +- federated authentication +- least-privilege access design + +and highlighted modern cloud security best practices. + +### 3. Environment Isolation Prevents Deployment Drift + +Separating dev, staging, and production infrastructure reduced accidental cross-environment changes and improved deployment confidence. + +This also made testing infrastructure changes safer before promoting them to production. + +### 4. Terraform State Management Requires Planning + +Managing Terraform state becomes increasingly important as infrastructure grows. + +This project reinforced: + +- the importance of remote state backends +- state locking +- consistent environment structure +- predictable resource naming conventions + +### 5. CI/CD Pipelines Are Infrastructure Too + +A deployment pipeline should be treated as part of the infrastructure rather than an afterthought. + +Automating: + +- builds +- deployments +- authentication +- CloudFront invalidations + +improved reliability and reduced manual deployment errors. + +### 6. Small AWS Misconfigurations Can Cause Large Failures + +Minor IAM or bucket policy mistakes can completely break deployments. + +Troubleshooting issues such as: + +- AccessDenied errors +- incorrect OIDC trust relationships +- CloudFront origin permissions +- S3 bucket policy conflicts + +helped build deeper AWS troubleshooting skills. + +### 7. Reusability Requires Intentional Design + +Making a project reusable is not automatic. + +It required: + +- parameterized Terraform variables +- environment abstraction +- clean module boundaries +- predictable naming conventions + +This project reinforced the importance of designing for reuse from the beginning rather than trying to fix it later. + +### 8. Production Infrastructure Requires Both Security and Automation + +A working deployment is not necessarily production-ready. + +This project highlighted the balance between: + +- security +- scalability +- automation +- maintainability +- developer experience + +when building real-world cloud infrastructure. + +### 9. Documentation Is Part of Engineering + +Clear documentation became essential as the project grew in complexity. + +Writing reusable setup instructions and architecture explanations can improve: + +- onboarding +- maintainability +- troubleshooting +- long-term project usability + +--- + ## ๐Ÿ“ˆ Future Improvements - Add custom domain + Route53 automation