Skip to main content

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

TriggerEnvironmentTestsDeployApproval
PR opened/updatedYesNoNo
Push to developdevYesAutoNo
Push to maindemoYesAutoNo
Manual: devdevYesYesNo
Manual: demodemoYesYesNo
Manual: prod-usprod-usYesYesManual
Manual: prod-ukprod-ukYesYesManual

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

  1. Go to Actions → “Lambda CI/CD Pipeline”
  2. Click Run workflow
  3. Select target environment
  4. 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.

Terraform State

State is stored in encrypted S3 buckets:
EnvironmentState Bucket
devdash360-terraform-state-dev
demodash360-terraform-state-demo
prod-usdash360-terraform-state-prod-us
prod-ukdash360-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:
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:
  1. Re-run a previous workflow: Find the last successful workflow run in Actions and click “Re-run all jobs”
  2. Restore Terraform state: S3 versioning is enabled — restore a previous state file if needed

Adding New Lambda Functions

1

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 = {}
}
2

Implement the Lambda handler

Add the function in Dash360.Lambda/Functions/. See Architecture for the full pattern.
3

Add tests

Add tests in Dash360.Lambda.Tests/Functions/. See the TESTING.md in that project for patterns.
4

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

ProjectCoverage
Dash360.Lambda88%
Dash360.Lambda.SharedLayer88%
Dash360.Shared71%

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

IssueSolution
”The specified queue does not exist”Run .\deploy-localstack.ps1 — infrastructure hasn’t been deployed
Lambda can’t connect to SQL ServerVerify IP in .env, check port 1433 firewall rule, test with netstat -an | findstr 1433
LocalStack containers not startingCheck Docker Desktop is running, verify .env has valid LOCALSTACK_AUTH_TOKEN
Terraform errors on initRun terraform init from the localstack environment directory
Lambda execution errorsCheck CloudWatch logs or the LocalStack Web UI at http://localhost:8080
Build failure: Lambda functionRun 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