Agent Guidelines for SES Receiving Terraform
This document provides coding agents with essential information about this Terraform-based AWS SES inbound email pipeline project.
Project Overview
A Terraform configuration that provisions an AWS SES inbound email pipeline with S3 storage, SNS/SQS event processing, and Lambda-based email organization.
Tech Stack:
- Terraform (>= 1.6.0)
- AWS Provider (>= 5.50)
- Python 3.12 (Lambda runtime)
- AWS Services: SES, S3, SNS, SQS, Lambda, IAM
Build, Test, and Deployment Commands
Terraform Commands
# Initialize Terraform (download providers, set up backend)
terraform init
# Validate configuration syntax
terraform validate
# Format all .tf files to canonical style
terraform fmt
# Check formatting without making changes
terraform fmt -check
# Show execution plan
terraform plan
# Apply changes
terraform apply
# Apply with auto-approval (use with caution)
terraform apply -auto-approve
# Destroy all resources
terraform destroy
# Show current outputs
terraform output
# Show specific output
terraform output mx_record
# Run tflint to check for errors and best practices
tflint
# Initialize tflint (download plugins)
tflint --init
TFLint
TFLint is a Terraform linter that checks for errors, best practices, and potential issues.
Important: Always run tflint after modifying any .tf file before committing or applying changes.
# Initialize tflint (required first time and after config changes)
tflint --init
# Run tflint on current directory
tflint
# Run with specific format
tflint --format compact
# Run recursively on all modules
tflint --recursive
Lambda Testing
# Package Lambda function manually
cd lambda && zip -r ../lambda.zip . && cd ..
# Test Lambda locally (requires AWS SAM CLI or custom test harness)
# Note: This project doesn't include unit tests - consider adding them
# Invoke deployed Lambda function with test event
aws lambda invoke \
--function-name ses-receive-app-markcallen-dev-move \
--payload '{"Records":[{"ses":{"mail":{"messageId":"test123"},"receipt":{"recipients":["test@example.com"]}}}]}' \
response.json
AWS CLI Helpers
# Check SES receipt rule set status
aws ses describe-receipt-rule-set --rule-set-name ses-receive-app-markcallen-dev-rule-set
# List S3 bucket contents
aws s3 ls s3://ses-inbound-app-markcallen-dev/ --recursive
# View Lambda logs
aws logs tail /aws/lambda/ses-receive-app-markcallen-dev-move --follow
# Read SQS queue
aws sqs receive-message --queue-url <queue-url>
Code Style Guidelines
Terraform
File Organization:
main.tf- Primary resource definitionsvariables.tf- Input variable declarationsoutputs.tf- Output value definitions- Keep related resources grouped logically in main.tf
Formatting:
- Use
terraform fmtbefore committing (2-space indentation) - Run
tflintafter modifying any.tffile to check for errors and best practices - One resource per block
- Blank line between resources
- Use snake_case for resource names and variables
Resource Naming:
- Pattern:
${var.project_tag}-<descriptor> - Examples:
ses-receive-app-markcallen-dev-lambda-role,ses-receive-app-markcallen-dev-queue - Keep names descriptive and consistent
Variables:
- Always include
descriptionandtype - Provide sensible defaults where appropriate
- Use descriptive variable names (e.g.,
subdomain_fqdn, notdomain)
Best Practices:
- Use
depends_onexplicitly when resource dependencies aren't implicit - Tag all resources with
{ Project = var.project_tag } - Use
jsonencode()for IAM policies and complex JSON structures - Use data sources (e.g.,
data.aws_caller_identity) instead of hardcoding account info - Enable encryption, versioning, and public access blocks on S3 buckets
Python (Lambda)
Style:
- Follow PEP 8 conventions
- 4-space indentation
- Use double quotes for strings
- Keep handler functions simple and focused
Imports:
- Standard library first (
import os) - Third-party libraries second (
import boto3) - Blank line between groups
Naming:
- snake_case for variables and functions
- UPPER_CASE for constants/environment variables
- Example:
BUCKET,INCOMING_PREFIX,message_id
Error Handling:
- Lambda should handle exceptions gracefully
- Consider adding try/except blocks for S3 operations
- Return structured responses:
{"ok": True, ...}
Environment Variables:
- Use
os.environfor required vars - Use
os.environ.get()with defaults for optional vars - Example:
BUCKET = os.environ["BUCKET"]
AWS SDK Usage:
- Initialize boto3 clients at module level (outside handler)
- Use explicit parameter names in boto3 calls
- Example:
s3.copy_object(Bucket=BUCKET, Key=dest_key, CopySource=src)
Common Workflows
Adding a New AWS Resource
- Add resource block to
main.tfin appropriate section - Add any required IAM permissions
- Tag with
{ Project = var.project_tag } - Run
terraform fmtandterraform validate - Run
tflintto check for errors and best practices - Run
terraform planto review changes - Add output to
outputs.tfif resource info needs to be exposed
Modifying Lambda Function
- Edit
lambda/handler.py - Test locally if possible or use safe test data
- Run
terraform apply(this will automatically re-zip and deploy) - Monitor CloudWatch logs for errors:
aws logs tail /aws/lambda/<function-name> --follow
Updating Variables
- Modify
terraform.tfvars(not tracked in git) or pass via CLI - Never hardcode account-specific values in .tf files
- Use variables for anything that might change between environments
Important Notes
- Not a Git Repository: This directory is not version-controlled. Consider initializing git.
- No Automated Tests: Lambda function lacks unit tests. Consider adding pytest-based tests.
- State Management: Terraform state is local. Consider using remote backend (S3 + DynamoDB) for team environments.
- SES Sandbox: Newly created AWS accounts have SES in sandbox mode - you can only send/receive from verified addresses.
- S3 Cleanup: The bucket is not configured with
force_destroy = true, so destroy will fail if bucket contains objects.
Security Best Practices
- Never commit
terraform.tfvarsor.tfstatefiles - Use least-privilege IAM policies
- Keep AWS credentials out of code (use AWS CLI profiles or environment variables)
- Enable S3 bucket encryption (already configured with AES256)
- Use TLS for data in transit (SES rule uses
tls_policy = "Optional"- consider "Require") - Review IAM policies before applying
Troubleshooting
Terraform apply fails:
- Check AWS credentials:
aws sts get-caller-identity - Ensure region supports SES receiving (us-east-1, us-east-2, us-west-2, eu-west-1)
- Verify unique bucket name globally
Emails not arriving:
- Verify MX record in DNS
- Check SES receipt rule is active:
aws ses describe-active-receipt-rule-set - Activate rule set:
aws ses set-active-receipt-rule-set --rule-set-name <name> - Verify domain in SES console
Lambda not moving emails:
- Check CloudWatch logs
- Verify Lambda has S3 permissions
- Ensure SES receipt rule has Lambda action enabled