A serverless health check API built with AWS Lambda, API Gateway, and DynamoDB, deployed using Terraform and GitHub Actions.
- API Gateway: HTTP API with
/healthendpoint (GET/POST) - Lambda Function: Processes requests, logs to CloudWatch, saves to DynamoDB
- DynamoDB: Stores request data with unique IDs
- IAM: Least-privilege permissions for Lambda execution
- AWS Account with programmatic access
- GitHub Repository with the following secrets configured:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
- Terraform >= 1.0 (for local development)
- AWS CLI (optional, for testing)
- Go to your repository → Settings → Secrets and variables → Actions
- Add the following secrets:
AWS_ACCESS_KEY_ID: Your AWS access keyAWS_SECRET_ACCESS_KEY: Your AWS secret key
Create an S3 bucket and DynamoDB table for remote state:
aws s3api create-bucket \
--bucket your-terraform-state-bucket \
--region us-east-1
aws dynamodb create-table \
--table-name terraform-state-lock \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUESTUpdate terraform/backend.tf with your bucket name.
The GitHub Actions pipeline automatically:
- On Push to
stagingbranch: Deploys to staging environment - On Push to
mainbranch: Deploys to both staging and production - Manual Workflow: Deploy specific environment via workflow_dispatch
- Terraform Plan: Validates and creates execution plan
- Staging Deploy: Automatically deploys to staging
- Production Deploy: Deploys to prod (requires manual approval in GitHub)
GitHub requires environment protection rules:
- Go to Settings → Environments → Create "production" environment
- Enable "Required reviewers" and add yourself
- Production deployments will now require approval
Deploy to Staging:
git checkout -b staging
git push origin stagingDeploy to Production:
git checkout main
git push origin main
# Approve the production deployment in GitHub Actions UIManual Deployment:
- Go to Actions → Deploy Serverless Health Check → Run workflow
- Select environment (staging or prod)
- Click "Run workflow"
# Initialize Terraform
cd terraform
terraform init
# Plan deployment
terraform plan -var-file="environments/staging.tfvars"
# Apply deployment
terraform apply -var-file="environments/staging.tfvars"
# Get API endpoint
terraform output api_endpointAfter deployment, get your API endpoint:
# From Terraform output
terraform output api_endpoint
# Or from GitHub Actions logsGET Request:
curl https://your-api-id.execute-api.us-east-1.amazonaws.com/healthPOST Request:
curl -X POST https://your-api-id.execute-api.us-east-1.amazonaws.com/health \
-H "Content-Type: application/json" \
-d '{"test": "data"}'Expected Response:
{
"status": "healthy",
"message": "Request processed and saved.",
"request_id": "123e4567-e89b-12d3-a456-426614174000"
}aws dynamodb scan \
--table-name staging-requests-db \
--region us-east-1aws logs tail /aws/lambda/staging-health-check-function \
--follow \
--region us-east-1├── .github/workflows/deploy.yml # CI/CD pipeline
├── terraform/
│ ├── modules/ # Reusable Terraform modules
│ │ ├── dynamodb/
│ │ ├── lambda/
│ │ └── api-gateway/
│ ├── environments/ # Environment-specific variables
│ │ ├── staging.tfvars
│ │ └── prod.tfvars
│ ├── main.tf # Main infrastructure
│ ├── variables.tf
│ ├── outputs.tf
│ ├── providers.tf
│ └── backend.tf
├── lambda/
│ ├── health_check.py # Lambda function code
│ └── requirements.txt
└── README.md
- Why: Reusability across environments and easier maintenance
- Trade-off: Slightly more complex structure but better scalability
- Why: Simpler, cheaper, and sufficient for this use case
- Trade-off: Fewer features than REST API but not needed here
- Why: Cost-effective for unpredictable/low traffic
- Trade-off: Slightly higher per-request cost but no idle charges
- Why: Latest stable version with good AWS SDK support
- Trade-off: None, backwards compatible
- Why: Balance between debugging capability and cost
- Trade-off: Logs expire after a week
- Why: Security best practice, only grants necessary permissions
- Trade-off: None, always prefer least privilege
- Solution: Delete the lock in DynamoDB or wait for timeout
# Check logs
aws logs tail /aws/lambda/staging-health-check-function --follow
# Check function configuration
aws lambda get-function --function-name staging-health-check-function- Lambda execution role may lack permissions
- Check CloudWatch logs for Lambda errors
To destroy all resources:
cd terraform
terraform destroy -var-file="environments/staging.tfvars"
terraform destroy -var-file="environments/prod.tfvars"- Add API authentication (API keys or Cognito)
- Implement request rate limiting
- Add monitoring and alerting with CloudWatch Alarms
- Create custom domain name for API
- Add integration tests in CI/CD pipeline
- Implement blue-green deployments
MIT
This is your complete solution! The key features:
✅ Multi-environment support (staging/prod)
✅ Proper naming convention (env-resource-name)
✅ Modular Terraform structure
✅ Least-privilege IAM roles
✅ Full CI/CD with GitHub Actions
✅ Manual approval for production
✅ Lambda packaging in pipeline
✅ Clear commit history structure
✅ Comprehensive documentation
Start by creating the repository structure, then commit each component step by step with clear messages like:
1. "Initial project structure"
2. "Add Lambda function code"
3. "Add DynamoDB Terraform module"
4. "Add Lambda Terraform module"
5. "Add API Gateway Terraform module"
6. "Add main Terraform configuration"
7. "Add GitHub Actions CI/CD pipeline"
8. "Add comprehensive README