B2B SaaS - SOC 2
For a B2B SaaS team heading toward a SOC 2 Type I or Type II report. The kit deploys the infrastructure layer the auditor asks about, from the soc2.compliance.tf registry host: encrypted data stores, private subnets, HTTPS-only endpoints, access logging and Multi-AZ databases. When you finish, the stack is applied and every SOC 2 control its modules enforce has passed terraform plan.
Repository: compliancetf/starter-kit-saas-soc2 · About 107 resources
Before you start
- The shared prerequisites on Compliance starter kits: an account whose plan includes SOC 2, Terraform at a version every module accepts, AWS credentials, a Route 53 hosted zone, the replication role and destination bucket, and account-level CloudTrail.
- The replication pair supports the SOC 2 A1.2 availability criterion; the kit's data and logs buckets replicate to it.
Deploy
git clone https://github.com/compliancetf/starter-kit-saas-soc2.git
cd starter-kit-saas-soc2
cp terraform.tfvars.example terraform.tfvars
# Set project_name, domain_name, route53_zone_id, availability_zones,
# s3_replication_role_arn and s3_replication_destination_bucket_arn
terraform login soc2.compliance.tf
terraform init
terraform plan
terraform applyIf a value in terraform.tfvars breaks a SOC 2 control, terraform plan fails with an error that names the control and the module. Fix the value and plan again.
What it deploys

A public ALB with WAF in front of a private EKS cluster, an Aurora PostgreSQL database in dedicated database subnets, and shared services: S3, DynamoDB, ElastiCache and SQS. VPC flow logs and CloudWatch log groups support CC7.1.
| Module | Version | Purpose | SOC 2 criteria |
|---|---|---|---|
vpc | ~> 6.0 | Network isolation, flow logs, database subnets | CC6.1, CC7.1 |
alb | ~> 10.0 | HTTPS load balancing, access logs | CC6.6, CC6.7 |
waf | ~> 1.0 | Web Application Firewall (OWASP Top 10, IP reputation) | CC6.6 |
acm | ~> 6.0 | TLS certificate (DNS-validated) | CC6.7 |
kms | ~> 4.0 | Customer-managed key for EKS log encryption | CC6.1 |
eks | ~> 21.0 | Kubernetes cluster, private subnets, KMS-encrypted logs | CC6.1, CC7.1 |
rds_aurora | ~> 10.0 | Aurora PostgreSQL, Multi-AZ, encrypted, IAM auth | CC6.1, A1.2 |
s3_bucket_data | ~> 5.0 | Application data, cross-region replication | CC6.1, CC7.2, A1.2 |
s3_bucket_logs | ~> 5.0 | Centralized access logs (ALB + S3), cross-region replication | CC7.1, CC8.1, A1.2 |
dynamodb | ~> 5.0 | Key-value store, point-in-time recovery | CC6.1, A1.2 |
elasticache | ~> 1.0 | Redis, encrypted at rest and in transit, auth, failover | CC6.1, A1.2 |
sqs | ~> 5.0 | Message queue, dead-letter queue | CC6.1, CC7.2 |
cloudwatch | ~> 5.0 | Application log group | CC7.1, CC7.2 |
Control coverage
| SOC 2 criterion | Description | Modules | What the kit configures |
|---|---|---|---|
| CC6.1 | Logical access security | KMS, RDS Aurora, S3, EKS, ElastiCache, DynamoDB, SQS | Encryption at rest (customer-managed key for EKS logs), no public access, IAM authentication |
| CC6.6 | Logical access security measures against threats from outside system boundaries | ALB, WAF | TLS 1.2+ (TLS 1.3 policy), HTTPS-only, WAF with OWASP Top 10 and IP reputation rules |
| CC6.7 | Restriction of data transmission to authorized users and processes | ALB, ElastiCache | HTTPS-only listeners, transit encryption enabled |
| CC7.1 | Detection of configuration changes that introduce vulnerabilities and susceptibilities to newly discovered vulnerabilities | VPC, ALB, RDS Aurora, CloudWatch, S3 (logs) | VPC flow logs, ALB access logs, PostgreSQL log exports, CloudWatch log groups |
| CC7.2 | Monitoring of system components and controls on an ongoing basis to detect anomalies indicating malicious acts or errors | S3, SQS, CloudWatch | S3 versioning, dead-letter queues, log retention policies |
| CC8.1 | Change management controls | All modules | Infrastructure changes tracked in Terraform state and version control, deletion protection (prod) |
| A1.2 | Data backup processes, recovery infrastructure, and environmental protections | RDS Aurora, ElastiCache, S3 (data + logs) | Multi-AZ deployment, automatic failover, automated backups (7-14 days), S3 cross-region replication |
Settings that a module enforces as a control fail the plan if you change them. The rest are kit defaults you can change. The SOC 2 framework page lists the controls modules enforce and the mapped-only controls that stay with your team.
What you own
The kit configures the infrastructure settings above. These stay with you:
- Application-level authentication, authorization and session management
- Identity provider configuration and MFA enforcement
- HR policies: background checks, security awareness training
- Vendor management and third-party risk assessments
- Incident response procedures and runbooks
- Business continuity planning
- Access reviews and user lifecycle management
- Application-level audit logging for business events
The auditor still tests the infrastructure controls, such as Aurora encryption and S3 public access blocking, against your evidence, and assesses the organizational controls above separately. Audit evidence lists what compliance.tf gives them.
Vanta and Drata checks
Compliance.tf has no Vanta or Drata integration. These checks, which both platforms run against your AWS account, look at settings the kit configures:
| Platform check | SOC 2 criterion | What the kit sets |
|---|---|---|
| Encryption at rest (databases) | CC6.1 | RDS Aurora, DynamoDB, ElastiCache, S3 encrypted with KMS |
| HTTPS enforced on load balancers | CC6.6, CC6.7 | ALB HTTPS-only listener, TLS 1.2+ policy |
| S3 buckets not publicly accessible | CC6.1 | S3 module blocks all public access by default |
| RDS not publicly accessible | CC6.1 | Aurora deployed in private database subnets, no public endpoint |
| VPC flow logs enabled | CC7.1 | VPC module enables flow logs by default |
| Multi-AZ for RDS | A1.2 | Aurora writer + reader across multiple availability zones |
| S3 versioning enabled | CC7.2 | S3 data bucket has versioning enabled by default |
To scope evidence collection, point your compliance platform at the AWS resource tags: every module in the kit tags resources with Project, Environment and ManagedBy = "terraform".
Customize
Swap EKS for ECS
Remove the eks module block from main.tf and add the ecs module from soc2.compliance.tf, then point the ALB target group at your ECS service instead of the EKS pods:
module "ecs" {
source = "soc2.compliance.tf/terraform-aws-modules/ecs/aws"
version = "~> 6.0"
cluster_name = local.name
# Capacity providers and services: see the module page
tags = var.tags
}The ECS module page lists its inputs.
Add a second region for disaster recovery
The data and logs buckets already replicate through s3_replication_role_arn and s3_replication_destination_bucket_arn. To extend DR to compute and the database, add a provider alias for the second region and duplicate the relevant module blocks; use Aurora Global Database for the database.
provider "aws" {
alias = "dr"
region = "us-west-2"
}Adjust instance sizes
Every instance size is a variable. Override them in terraform.tfvars:
eks_node_instance_types = ["m6i.xlarge"]
eks_node_min_size = 3
eks_node_max_size = 10
aurora_instance_class = "db.r6g.xlarge"
redis_node_type = "cache.r6g.large"Remove a module
If the application does not use DynamoDB or SQS, delete those module blocks from main.tf and their outputs. The other modules do not depend on them.
Turn off a control
Use the HTTPS source with ?disable=:
module "s3_bucket_data" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&disable=s3_bucket_object_lock_enabled"
# ...
}Only a control the module enforces can be turned off; Turn controls on and off explains the limits and how to record the reason.
Cost
| Component | Dev | Prod |
|---|---|---|
| VPC + NAT Gateway | ~$35 | ~$100 |
| ALB | ~$25 | ~$25+ |
| EKS control plane | ~$75 | ~$75 |
| EKS nodes (t3.medium) | ~$60 | ~$120+ |
| Aurora PostgreSQL (db.r6g.large) | ~$200 | ~$400 |
| S3 (data + logs) | ~$5 | ~$5+ |
| DynamoDB (on-demand) | ~$0 | ~$0+ |
| ElastiCache (cache.t4g.micro) | ~$10 | ~$20 |
| SQS | ~$0 | ~$0+ |
| CloudWatch Logs | ~$5 | ~$10+ |
| WAF | ~$5 | ~$5+ |
| KMS | ~$1 | ~$1+ |
| Total of the rows | ~$421 | ~$761+ |
The total is the sum of the rows above. Usage charges come on top: data transfer, NAT gateway data processing, log ingestion, and KMS and WAF requests. Actual costs depend on usage, data transfer and region. Infracost estimates them from your own terraform plan.