From 42fda23f6c44af0105f3a84676f063642bb9e8ac Mon Sep 17 00:00:00 2001 From: emmy-github-webdev Date: Sat, 13 Dec 2025 16:38:03 +0100 Subject: [PATCH 1/3] Add the architectuural diagram and updated the Readme --- README.md | 122 +++++++++++++------- svrhecheck.drawio.png | Bin 0 -> 38793 bytes terraform/lambda_package/staging-lambda.zip | Bin 745 -> 0 bytes 3 files changed, 79 insertions(+), 43 deletions(-) create mode 100644 svrhecheck.drawio.png delete mode 100644 terraform/lambda_package/staging-lambda.zip diff --git a/README.md b/README.md index 658bc54..3c957c4 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ The goal of this project is to build, configure, and automate the deployment of a simple serverless application on AWS. Created a health check endpoint that logs requests and stores them in a database, with a CI/CD pipeline to manage deployments for both staging and production environments, fully provisioned via Terraform and deployed automatically using GitHub Actions. +## Prerequisites +- An AWS account and IAM user with rights to create IAM, Lambda, API Gateway, DynamoDB, CloudWatch. +- Local toolchain: + - Terraform v1.2+ +- GitHub repo secrets (Repository > Settings > Secrets): + - `AWS_ACCESS_KEY_ID` + - `AWS_SECRET_ACCESS_KEY` + - `AWS_REGION` (e.g. `us-east-1`) + ## Architectural desig ### Core Components @@ -14,6 +23,8 @@ The goal of this project is to build, configure, and automate the deployment of Each environment (staging, prod) is isolated by naming convention and Terraform variables. +[Live Demo Link](https://www.loom.com/share/3b1a44b435724293b7be8953ed51ae27) + ### Runtime Request Flow 1. _Client_: sends a GET or POST request to: @@ -60,6 +71,7 @@ https://.execute-api..amazonaws.com/health | Terraform vars | `staging.tfvars` | `prod.tfvars` | | Lambda | `staging-health-check-function` | `prod-health-check-function` | | DynamoDB | `staging-requests-db` | `prod-requests-db` | +| S3 Bucket | `serverless-health-check-api/staging/tfstate` | `serverless-health-check-api/prod/tfstate`| | API Gateway | `staging-health-check-api` | `prod-health-check-api` | | Approval | None | Required | @@ -80,50 +92,74 @@ Each Lambda function has one dedicated IAM role with: - No credentials committed to repository - - - - - - -create hello lambda funtion using Python - +![](/svrhecheck.drawio.png) + +## Setup + +_Repository Structure_ +. +├── .github +│ └── workflows +│ └── deploy.yml + ├── destroy.yml +├── lambda +│ └── lambda_function.py +├── terraform +│ ├── provider.tf +│ ├── variables.tf +│ ├── main.tf +│ ├── iam.tf +│ ├── lambda.tf +│ ├── apigw.tf +│ ├── outputs.tf +│ └── staging.tfvars +│ └── prod.tfvars +| └── backend-prod.tfvars +| └── backend-staging.tfvars +└── README.md + +- Create the lambda function, run the python funtion locally using VS Code Run Button, import boto3, logging,json, os, uuid. +- Resource names follows _env-resource-name_ + +## How to trigger deployments + +_Manual for test_ +- terraform init -backend-config=backend-staging.tfvars for staging environment +- terraform init -backend-config=backend-prod.tfvars for prod environment +- terraform plan -backend-config=backend-staging.tfvars for staging environment +- terraform plan -backend-config=backend-prod.tfvars for prod environment +- terraform apply -var-file="staging.tfvars" +- terraform apply -var-file="prod.tfvars" +- terraform destroy -backend-config=backend-staging.tfvars for staging environment +- terraform destroy -backend-config=backend-prod.tfvars for prod environment + +_GitHub Action CICD_ +### Staging +1. Push to branch `staging` (or open a PR merging into `staging` and merge). +2. GitHub Action will automatically: + - `terraform init` + - `terraform plan -var-file="staging.tfvars"` + - `terraform apply` (auto-approved) + +### Production +1. Push to branch `main` (or merge PR into `main`). +2. Workflow will run and wait for **manual approval** in the `production` environment (configure environment reviewers in Settings). +3. Approve in GitHub UI → workflow will `apply` the `prod.tfvars` plan. + +### Trigger Destroy CICD pipeline +Manually trigger the terraform destroy workflow in GitHub UI -> actions -> destroy.yaml + +## Testing the /health endpoint + +After deployment, Terraform outputs the API endpoint. You can fetch it: +- From the Actions run logs (last step runs `terraform output -json`) + +Example curl (replace `` with the `api_endpoint` output): ``` -# Hello lambda function -def lambda_handler(event, context): - name = event.get("name", "World") - message = f"Hello, {name}!" - - return { - "statusCode": 200, - "body": message - } - -# Run the funtion locally for testing -if __name__ == "__main__": - test_event = {"name": "Emmanuel Ogah"} - result = lambda_handler(test_event, None) - print(result) -``` - -Run the python funtion locally using VS Code Run Button -- Click the “Run Python File” button in the top right corner. +# GET +curl "https://nrbefv9bcj.execute-api.us-east-1.amazonaws.com/health" -``` # Output +{"status": "healthy", "message": "Request processed and saved.", "id": "0fb4fa88-64a1-440d-84b1-5a6d4354364b"} -{'statusCode': 200, 'body': 'Hello, Emmanuel Ogah!'}" -``` - -- Create the terraform folder structure - -- Deploy with: terraform init then terraform apply -var-file="staging.tfvars" (or prod.tfvars) - - -endpoint - https://nrbefv9bcj.execute-api.us-east-1.amazonaws.com/health - - - -terraform init -backend-config=backend-staging.tfvars for staging environment - -terraform init -backend-config=backend-prod.tfvars for prod environment +``` \ No newline at end of file diff --git a/svrhecheck.drawio.png b/svrhecheck.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..c14af391ee8675dca605da9c8fc02cdf5561dbe8 GIT binary patch literal 38793 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 literal 0 HcmV?d00001 diff --git a/terraform/lambda_package/staging-lambda.zip b/terraform/lambda_package/staging-lambda.zip deleted file mode 100644 index fade2416ba888ddc40276bc2e72a4927272bebbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 745 zcmWIWW@Zs#-~d8&AOsZ1Nz6@3NsLb`%}XxH%+J#+sGJk*pMBd;V6XHa*|xue5g&`M zT(B2!f-K*`W?PR3tH2dwNj~({iuV){>`RV1a z&pgW>ICYh!@7es4&9m$0o7dZKetuLbx%vo;OKyGC_auGKwOk^c$}jeYF1@Th!>lK$ z|KL?ScgY-AIF(P+`{D5Iv$906JO5=r=Fb&9=|(R%Wja(HkrmsdT~)E`|D}S1 zrXs6iGPmB*TFw1Ms5~Q+Y3Jsa^;1$F3o;z2|Bz4|F15Qc;=+B&^xKxl*3A_uK6CL+ zfO)HA42xl>+mc;+t1s|)Jo>@*^V(u1mj3-&JS;i#>bEK~O?(t5On;MeDqdc7dvUL# zP9j&n?~#6ksFdb=Cl!wBu?3~7Zd$y0WvRZAr*A>_b8lCp#{Z5r;)nO0h)C&O@a)fp z%k^Qq=h;~9=x)1v*QD~(?=|gR%a(=udZ+2hbDY-PuUdQZWUSG)ypT8+w*Kjs0qp9( z5B&WxE7{ec+x}Q&0HeWgX8GNFM82OASt0rNsPv(QZP#pNK797ry8W>Bb9a`YkoM^1 zfhiwezRD_^7H_#%{AL0Fy<2h<8y()yeg5COQ*i}va>mpuQ&(r0r{e0kXrLxwQ%09)C-{pE=LZ18(uq!!`6m zS-o1}y*%HivgciSPq`gk5v*r>`>F8D|0=%?NfBFljiB`Uy7q5 Date: Sat, 13 Dec 2025 16:40:14 +0100 Subject: [PATCH 2/3] Add the architectuural diagram and updated the Readme --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3c957c4..98515da 100644 --- a/README.md +++ b/README.md @@ -100,8 +100,8 @@ _Repository Structure_ . ├── .github │ └── workflows -│ └── deploy.yml - ├── destroy.yml +│ ├── deploy.yml +│ └── destroy.yml ├── lambda │ └── lambda_function.py ├── terraform @@ -112,12 +112,13 @@ _Repository Structure_ │ ├── lambda.tf │ ├── apigw.tf │ ├── outputs.tf -│ └── staging.tfvars -│ └── prod.tfvars -| └── backend-prod.tfvars -| └── backend-staging.tfvars +│ ├── staging.tfvars +│ ├── prod.tfvars +│ ├── backend-prod.tfvars +│ └── backend-staging.tfvars └── README.md + - Create the lambda function, run the python funtion locally using VS Code Run Button, import boto3, logging,json, os, uuid. - Resource names follows _env-resource-name_ From 46b9f59d8b5d32d07d1e5c87671662442a577211 Mon Sep 17 00:00:00 2001 From: emmy-github-webdev Date: Sat, 13 Dec 2025 16:41:43 +0100 Subject: [PATCH 3/3] Add the architectuural diagram and updated the Readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 98515da..2445581 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ Each Lambda function has one dedicated IAM role with: ## Setup _Repository Structure_ + +```text . ├── .github │ └── workflows @@ -118,7 +120,6 @@ _Repository Structure_ │ └── backend-staging.tfvars └── README.md - - Create the lambda function, run the python funtion locally using VS Code Run Button, import boto3, logging,json, os, uuid. - Resource names follows _env-resource-name_