Infrastructure Overview
The Lambda infrastructure lives in Dash360.Infrastructure/ and is managed with Terraform.
Dash360.Infrastructure/
├── terraform/
│ ├── environments/
│ │ ├── dev/ # Development (us-west-2)
│ │ ├── demo/ # Demo/UAT (us-west-2)
│ │ ├── prod-us/ # Production US (us-west-2, 17+ tenants)
│ │ ├── prod-uk/ # Production UK (eu-west-2, 2 tenants)
│ │ └── localstack/ # Local testing with LocalStack
│ └── modules/
│ ├── dynamodb/ # DynamoDB tables
│ ├── s3-buckets/ # Multi-tenant S3 storage
│ ├── sqs-queues/ # Import/export/backup queues
│ ├── iam/ # Lambda execution roles
│ ├── lambda-export-functions/ # Data-driven Lambda deployment
│ ├── eventbridge/ # Progress notifications
│ ├── parameter-store/ # Tenant configuration
│ └── step-functions/ # Transaction orchestration
CI/CD Pipeline
Lambda deployments are automated via GitHub Actions (.github/workflows/lambda-deploy.yml).
Deployment Flow
Feature branch PR → tests only (no deployment)
↓
develop → auto-deploy to dev
↓
main → auto-deploy to demo (UAT)
↓
manual trigger → prod-us (approval required)
↓
manual trigger → prod-uk (approval required)
Trigger Reference
Trigger Environment Tests Deploy Approval PR opened/updated — Yes No No Push to develop dev Yes Auto No Push to main demo Yes Auto No Manual: dev dev Yes Yes No Manual: demo demo Yes Yes No Manual: prod-us prod-us Yes Yes Manual Manual: prod-uk prod-uk Yes Yes Manual
Path Filtering
The pipeline only triggers when these paths change:
Dash360.Lambda/**
Dash360.Lambda.SharedLayer/**
Dash360.Lambda.Tests/**
Dash360.Shared/**
Dash360.Infrastructure/terraform/**
.github/workflows/lambda-deploy.yml
Changes to .md files and docs/** are ignored.
Manual Deployment
Go to Actions → “Lambda CI/CD Pipeline”
Click Run workflow
Select target environment
For production: wait for approval from required reviewers
Production deployments (prod-us, prod-uk) require manual workflow dispatch — they never auto-deploy. Someone must intentionally trigger via the Actions UI.
State is stored in encrypted S3 buckets:
Environment State Bucket dev dash360-terraform-state-devdemo dash360-terraform-state-demoprod-us dash360-terraform-state-prod-usprod-uk dash360-terraform-state-prod-uk
The state bucket must be created manually before first deployment. Terraform can’t create the bucket it stores its own state in.
Local Deployment Scripts
PowerShell scripts are available for local deployments and debugging:
LocalStack
Development
Demo
Production US
cd Dash360.Infrastructure / terraform / environments / localstack
. \deploy-localstack.ps1
Health Check Commands
# List Lambda functions for an environment
aws lambda list-functions \
--query "Functions[?contains(FunctionName, 'dash360-dev')].FunctionName"
# Test Lambda invocation (dry run — no side effects)
aws lambda invoke \
--function-name dash360-dev-ExportRouter \
--invocation-type DryRun \
response.json
# View recent CloudWatch logs
aws logs tail /aws/lambda/dash360-dev-ExportRouter --since 1h
Rollback
To rollback to a previous version:
Re-run a previous workflow : Find the last successful workflow run in Actions and click “Re-run all jobs”
Restore Terraform state : S3 versioning is enabled — restore a previous state file if needed
Adding New Lambda Functions
Add to export_functions map
Add an entry in each environment’s main.tf: "Holidays" = {
data_type = "Holidays"
lambda_handler = "Dash360.Lambda::Dash360.Lambda.Functions.Holidays.HolidaysExportFunction::HandleSqsAsync"
timeout = 300
memory_size = 512
purpose = "Holidays export processing"
config_version = "v1.0"
additional_env_vars = {}
}
Implement the Lambda handler
Add the function in Dash360.Lambda/Functions/. See Architecture for the full pattern.
Add tests
Add tests in Dash360.Lambda.Tests/Functions/. See the TESTING.md in that project for patterns.
Deploy
Push to develop to auto-deploy to dev. After testing, merge to main to deploy to demo, then trigger manually for production.
LocalStack Testing Guide
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Your Windows Machine │
│ │
│ ┌──────────────┐ ┌──────────────────────────────────────┐ │
│ │ Dash360 │ │ Docker Desktop │ │
│ │ Web App │ │ ┌──────────────────────────────┐ │ │
│ │ (Visual │────▶│ │ LocalStack Pro │ │ │
│ │ Studio) │ │ │ ┌─────┐ ┌─────┐ ┌────────┐ │ │ │
│ └──────────────┘ │ │ │ SQS │ │ S3 │ │ Lambda │ │ │ │
│ │ │ │ └─────┘ └─────┘ └────────┘ │ │ │
│ │ │ │ ┌─────────┐ ┌───────────┐ │ │ │
│ ▼ │ │ │DynamoDB │ │EventBridge│ │ │ │
│ ┌──────────────┐ │ │ └─────────┘ └───────────┘ │ │ │
│ │ SQL Server │◀────│ └──────────────────────────────┘ │ │
│ │ Developer │ └──────────────────────────────────────┘ │
│ └──────────────┘ Lambda connects to SQL │
│ Server via your IP address │
└─────────────────────────────────────────────────────────────────┘
Running Tests
# Run all Lambda tests (unit + integration)
dotnet test Dash360.Lambda.Tests/Dash360.Lambda.Tests.csproj
# Unit tests only (fast, no LocalStack required)
dotnet test --filter "Category!=Integration"
# Run with coverage report
dotnet test --collect: "XPlat Code Coverage"
# Run specific export function tests
dotnet test --filter "ExportFunctionTests"
# Run integration tests (requires LocalStack running)
dotnet test --filter "Category=Integration"
Current Test Coverage
Project Coverage Dash360.Lambda 88% Dash360.Lambda.SharedLayer 88% Dash360.Shared 71%
Monitoring Lambda Logs
# View LocalStack logs
docker logs dash360-localstack --tail 50
# List Lambda log groups
aws --endpoint-url=http://localhost:4566 logs describe-log-groups --region us-west-2
# Get log events for a specific function
aws --endpoint-url=http://localhost:4566 logs get-log-events \
--log-group-name /aws/lambda/dash360-localstack-RiskCategories \
--log-stream-name STREAM_NAME \
--region us-west-2
Or use the LocalStack Web UI at http://localhost:8080 to browse logs visually.
Stopping LocalStack
# Stop containers
cd Dash360.LocalStack
docker compose down
# Stop and remove all data (clean slate)
docker compose down -v
Troubleshooting Lambda Issues
Issue Solution ”The specified queue does not exist” Run .\deploy-localstack.ps1 — infrastructure hasn’t been deployed Lambda can’t connect to SQL Server Verify IP in .env, check port 1433 firewall rule, test with netstat -an | findstr 1433 LocalStack containers not starting Check Docker Desktop is running, verify .env has valid LOCALSTACK_AUTH_TOKEN Terraform errors on init Run terraform init from the localstack environment directory Lambda execution errors Check CloudWatch logs or the LocalStack Web UI at http://localhost:8080 Build failure: Lambda function Run dotnet clean then dotnet build on the Lambda project
Verify Parameter Store Values
# Check that the webhook endpoint was stored correctly
aws --endpoint-url=http://localhost:4566 ssm get-parameter \
--name "/dash360/localstack/testcustomer/webhookEndpoint" \
--region us-west-2
If the value is stale after updating .env, delete terraform.tfstate* and re-apply.
Check S3 Export Files
# List exported files in S3
aws --endpoint-url=http://localhost:4566 s3 ls \
s3://dash360-exports-localstack/ --recursive
# Get Step Functions state machines (for Backup Project ARN)
aws --endpoint-url=http://localhost:4566 stepfunctions list-state-machines \
--region us-west-2