AWS infrastructure for brignano.io - personal website hosting, email forwarding, and DNS management.
This repository contains Infrastructure as Code (IaC) for deploying and managing the brignano.io domain and related AWS services. It uses Terraform for infrastructure provisioning and CloudFormation for AWS authentication setup.
- 📖 Infrastructure Documentation - Detailed Terraform configuration guide
- 🔧 CloudFormation Setup - OIDC authentication configuration
- 📋 Design Document - Architecture and design decisions
- 🔄 Terraform Import Guide - How to import existing AWS resources
Internet → Route 53 → Vercel (Website)
└→ SES → S3 → Lambda → SES → Gmail (Email Forwarding)
What's Provisioned:
- Route 53 DNS - Manages brignano.io and anthonybrignano.com domains
- Amazon SES - Receives emails at hi@brignano.io and forwards them
- AWS Lambda - Python function that processes and forwards emails
- S3 Storage - Archives all incoming emails
- IAM Roles - Secure permissions for Lambda execution
- CloudWatch - Logs and monitoring
.
├── iac/ # Terraform Infrastructure as Code
│ ├── main.tf # Main resource definitions
│ ├── provider.tf # AWS provider and Terraform Cloud config
│ ├── locals.tf # Local variables
│ ├── data.tf # Data sources
│ ├── outputs.tf # Output values
│ ├── lambda/ # Email forwarding Lambda function
│ │ ├── forward_email.py # Python Lambda handler
│ │ └── requirements.txt # Python dependencies
│ └── README.md # Detailed IaC documentation
├── cloudformation/ # CloudFormation templates
│ ├── template.yml # OIDC provider setup for Terraform Cloud
│ └── README.md # CloudFormation setup guide
├── docs/ # Additional documentation
│ ├── design.md # Architecture design decisions
│ └── terraform-import.md # Import existing resources guide
├── .github/workflows/ # CI/CD pipelines
│ ├── plan.yml # Terraform plan on PRs
│ ├── apply.yml # Terraform apply on main branch
│ └── aws-setup.yml # CloudFormation deployment
└── readme.md # This file
- Primary Domain: brignano.io → Points to Vercel hosting
- Backup Domain: anthonybrignano.com → Also points to Vercel
- DNS Provider: AWS Route 53 for reliable DNS resolution
- WWW Redirect: Both domains support www. subdomain
- Receive Email: hi@brignano.io
- Forward To: Personal Gmail account (configured in
locals.tf) - Storage: All emails archived in S3 bucket
- No-Reply Handling: Emails to noreply@brignano.io are automatically bounced
- Terraform manages all AWS resources
- Version Control for infrastructure changes
- CI/CD via GitHub Actions and Terraform Cloud
- Automated Deployment on merge to main branch
- OIDC Authentication: Keyless AWS access from Terraform Cloud
- IAM Least Privilege: Minimal permissions for each component
- No Stored Credentials: Uses temporary credentials via OIDC
- Audit Logs: CloudWatch logs for all operations
Before deploying this infrastructure, ensure you have:
- AWS Account with administrative access
- Terraform Cloud Account
- Organization:
brignano - Workspace:
aws-config
- Organization:
- Domain Names
- Registered and ready to configure nameservers
- GitHub Repository Access
- Able to configure secrets and run workflows
- Vercel Project (or alternative hosting)
- For website hosting
- Email Account
- Gmail or other for receiving forwarded emails
-
Deploy CloudFormation Stack (OIDC Provider for Terraform Cloud):
# This is automated via GitHub Actions # See cloudformation/README.md for details
-
Configure Terraform Cloud Workspace:
- Set
TFC_AWS_PROVIDER_AUTH=true - Set
TFC_AWS_RUN_ROLE_ARN= (ARN from CloudFormation output)
- Set
-
Update Configuration:
- Edit
iac/locals.tfwith your domains, email, and Vercel settings
- Edit
-
Verify SES Email:
- Check your forwarding destination email for AWS SES verification
- Click the verification link
-
Deploy Infrastructure:
- Push changes to
mainbranch - GitHub Actions triggers Terraform Cloud apply
- Push changes to
For making infrastructure changes:
-
Create a Branch:
git checkout -b feature/my-change
-
Make Changes:
- Edit files in
iac/directory
- Edit files in
-
Open Pull Request:
- GitHub Actions runs
terraform plan - Review the plan in the PR comment
- GitHub Actions runs
-
Merge to Main:
- GitHub Actions runs
terraform apply - Infrastructure is updated automatically
- GitHub Actions runs
To adapt this repository for your own use:
-
Update
iac/locals.tf:locals { domain_name = { default = "yourdomain.com" backup = "yourbackup.com" } email_address = "your-email@gmail.com" vercel_ip_address = "your-vercel-ip" vercel_cname_record = "your-vercel-cname" }
-
Update CloudFormation
template.yml:- Change hosted zone IDs to match your domains
- Update organization/workspace names
-
Update GitHub Secrets:
TF_API_TOKEN- Terraform Cloud API tokenAWS_ASSUME_ROLE_ARN- IAM role ARN for GitHub Actions
Estimated monthly costs for running this infrastructure:
| Service | Usage | Monthly Cost |
|---|---|---|
| Route 53 Hosted Zones | 2 zones | $1.00 |
| Route 53 Queries | ~1M queries | $0.40 |
| SES Receiving | First 1,000 emails | Free |
| SES Sending | ~100 emails | $0.01 |
| Lambda | ~100 invocations | Free |
| S3 Storage | ~1 GB | $0.02 |
| CloudWatch Logs | 1 log group, 30-day retention | $0.50 |
| Total | ~$1.93/month |
Actual costs may vary. Free tier covers Lambda and most SES usage for low-volume personal use.
Issue: Error: creating Route 53 Record: InvalidChangeBatch: resource already exists
- Solution: Import the existing resource into Terraform state
- Guide: See docs/terraform-import.md
Issue: Email not being forwarded
- Check: SES email verification status
- Check: Lambda CloudWatch logs:
/aws/lambda/email-forwarder - Check: S3 bucket for incoming email objects
- Verify: SES sending limits not exceeded
Issue: Lambda timeout errors
- Cause: Large email attachments
- Solution: Increase timeout in
iac/main.tf(currently 30s)
Issue: Terraform Cloud authentication fails
- Check: CloudFormation stack deployed successfully
- Check:
TFC_AWS_RUN_ROLE_ARNvariable set correctly - Verify: OIDC provider thumbprint is current
Lambda Execution Logs:
aws logs tail /aws/lambda/email-forwarder --followTerraform Cloud Runs:
CloudFormation Stack:
- AWS Console → CloudFormation → TerraformAssumeRoleSetup
-
plan.yml- Runs on Pull Requests- Uploads configuration to Terraform Cloud
- Runs
terraform plan - Comments plan output on PR
-
apply.yml- Runs on Push to Main- Uploads configuration to Terraform Cloud
- Runs
terraform apply - Automatically confirms and applies changes
-
aws-setup.yml- Deploys CloudFormation- Runs when
cloudformation/template.ymlchanges - Uses OIDC to authenticate with AWS
- Deploys/updates the TerraformAssumeRoleSetup stack
- Runs when
Developer → Git Push → GitHub → Terraform Cloud → AWS
│
├─ PR: terraform plan (review)
└─ Main: terraform apply (deploy)
- ✅ IAM Least Privilege - Each component has minimal permissions
- ✅ OIDC Authentication - No long-term AWS credentials stored
- ✅ Private S3 Bucket - Email storage not publicly accessible
- ✅ SES Verification - Prevents unauthorized email forwarding
- ✅ CloudWatch Logging - Audit trail for all operations
- ✅ Terraform Cloud - State files encrypted and secured
- 🔒 Enable MFA on AWS root account
- 🔒 Regularly review IAM permissions
- 🔒 Monitor CloudWatch logs for suspicious activity
- 🔒 Keep Terraform providers up to date
- 🔒 Review SES bounce and complaint rates
- 🔒 Consider enabling S3 bucket versioning
- 🔒 Consider enabling S3 server-side encryption
- Email Format: Only plain text emails are forwarded (HTML stripped)
- Large Attachments: May timeout (30-second Lambda limit)
- Reply-To Header: Not currently preserved in forwarded emails
- CC/BCC: Not forwarded to destination
- SES Sandbox: New AWS accounts require production access request
- Support HTML email forwarding
- Preserve Reply-To, CC, BCC headers
- Handle email attachments properly
- Add email filtering/spam detection
- CloudWatch alarms for Lambda failures
- S3 bucket encryption at rest
- SPF/DKIM/DMARC documentation
- Automated testing for Lambda function
- Cost monitoring and alerts
This is a personal infrastructure repository. If you're using it as a template:
- Fork the repository
- Update configuration for your domains and email
- Deploy to your own AWS account
- Customize as needed
See individual file headers for license information.
Note: This section is maintained for backward compatibility. See iac/README.md for current documentation.
Terraform configuration for:
- Route 53: DNS zones for
brignano.ioandanthonybrignano.comwith A records pointing to Vercel - SES (Simple Email Service): Email identity verification and receipt rules for
hi@brignano.io - Lambda: Email forwarding function that processes incoming emails and forwards them to your primary email
- S3: Email storage bucket for archiving incoming messages
- IAM: Roles and policies for Lambda to access S3 and send emails via SES
- CloudWatch: Logging for Lambda execution
Note: This section is maintained for backward compatibility. See cloudformation/README.md for current documentation.
CloudFormation template for setting up GitHub Actions OIDC authentication with AWS:
- OIDC Provider: Configures OpenID Connect provider for
https://app.terraform.io - IAM Role:
TerraformCloudAssumeRolefor Terraform Cloud to assume with OIDC - IAM Policy:
TerraformCloudAssumePolicygrants permissions to manage Route 53 (DNS), SES, Lambda, S3, CloudWatch, and IAM resources - GitHub Actions Integration: Enables secure, keyless authentication from GitHub Actions workflows via OIDC federation
This setup allows Terraform Cloud to deploy infrastructure changes without storing AWS credentials.
- Create an
.env.localfile (gitignored):
AWS_ACCESS_KEY=""
AWS_SECRET_ACCESS_KEY=""
TF_TOKEN_app_terraform_io=""- Configure AWS CLI (optional, for manual operations):
aws configure- Install Terraform locally (for testing):
brew install terraform # macOS
# or download from https://www.terraform.io/downloads-
AWS Documentation:
-
Terraform:
-
Examples:
Maintained by: Anthony Brignano
Last Updated: 2026-01-08