compliance.tf

Compliance Starter Kits

Three pre-composed Terraform configurations for common compliance scenarios. Each kit bundles the modules you actually need, wired together with the right controls for your framework, so you can start with a clean architecture instead of retrofitting compliance onto existing infrastructure.

All modules are sourced from the compliance.tf registry. The registry enforces controls at terraform plan time; non-compliant configurations fail before they're applied.


Kits

KitTargetFrameworksModulesGitHub
B2B SaaS: SOC 2Seed-to-Series B SaaS selling to enterpriseSOC 213compliancetf/starter-kit-saas-soc2
Fintech: PCI DSS v4.0Payment platforms, neobanks, BNPLPCI DSS v4.019compliancetf/starter-kit-fintech-pcidss
HealthTech: HIPAATelehealth, EHR, health data platformsHIPAA19compliancetf/starter-kit-healthtech-hipaa

Which kit is right for you?

If your first enterprise deal is stalled on a security questionnaire or SOC 2 requirement, use the B2B SaaS kit.

If you're handling cardholder data now or will be, use the Fintech kit. The cardholder data environment (CDE) scoping and network segmentation decisions you make in the first six months are hard to undo.

If you're about to handle PHI for the first time, use the HealthTech kit. RDS encryption must be enabled at database creation; it can't be added later without rebuilding.

If you need SOC 2 alongside PCI DSS or HIPAA, start with the kit for the stricter framework. Neither the Fintech nor the HealthTech kit ships a SOC 2 control mapping; the B2B SaaS kit has the criterion-by-criterion table, and most of the modules it uses appear in the other two kits as well.


How it works

Each kit is a standalone Terraform configuration you clone, fill in, and apply:

  1. Clone the repo and copy terraform.tfvars.example to terraform.tfvars
  2. Fill in your AWS region, domain name, and Route 53 zone
  3. Authenticate: terraform login soc2.compliance.tf (or pcidss / hipaa)
  4. Run terraform init && terraform plan && terraform apply

terraform plan fails if any control for your framework is violated. The error names the specific control and the module that triggered it. Fix the issue and re-run.

The kits use existing compliance.tf modules. No wrapper modules, no extra abstractions. You own the Terraform code after you clone it.

Prefer composing yourself? Browse every module for your framework directly on the registry — soc2.compliance.tf, pcidss.compliance.tf, or hipaa.compliance.tf — with versions, inputs, and outputs for each terraform-aws-modules module.


Create the replication prerequisites

All three kits replicate their S3 buckets to a second region, so each one asks for two values you must create before the first terraform apply: s3_replication_role_arn and s3_replication_destination_bucket_arn. Neither is created by the kit, because both usually live in a separate account or a separate state file.

Versioning must be enabled on both sides of a replication pair, and KMS-encrypted objects need extra configuration (see the note below). The kits' source buckets already have it (enforced by the compliance controls); the destination bucket you create here needs it too — S3 refuses the replication configuration otherwise. The snippet below is the same-account, second-region case. A cross-account destination additionally needs a provider for the destination account, a destination bucket policy granting the replication role, and (for KMS) a destination key policy — see AWS's cross-account replication walkthrough. Apply this in your DR region, in a configuration outside the kit:

provider "aws" {
  alias  = "dr"
  region = "us-west-2"
}

resource "aws_s3_bucket" "replica" {
  provider = aws.dr
  bucket   = "myapp-dr-us-west-2"
}

resource "aws_s3_bucket_versioning" "replica" {
  provider = aws.dr
  bucket   = aws_s3_bucket.replica.id

  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_iam_role" "replication" {
  name = "s3-replication"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Service = "s3.amazonaws.com" }
      Action    = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_role_policy" "replication" {
  role = aws_iam_role.replication.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["s3:GetReplicationConfiguration", "s3:ListBucket"]
        Resource = "arn:aws:s3:::myapp-*"
      },
      {
        Effect   = "Allow"
        Action   = ["s3:GetObjectVersionForReplication", "s3:GetObjectVersionAcl", "s3:GetObjectVersionTagging"]
        Resource = "arn:aws:s3:::myapp-*/*"
      },
      {
        Effect   = "Allow"
        Action   = ["s3:ReplicateObject", "s3:ReplicateDelete", "s3:ReplicateTags"]
        Resource = "${aws_s3_bucket.replica.arn}/*"
      }
    ]
  })
}

The source-side resources use an arn:aws:s3:::myapp-* wildcard because the kit's buckets do not exist yet when the role is created. Replace myapp with the project_name you set in terraform.tfvars, and narrow the wildcard to the exact bucket names once the kit has been applied.

Then pass the two ARNs into the kit:

s3_replication_role_arn               = "arn:aws:iam::123456789012:role/s3-replication"
s3_replication_destination_bucket_arn = "arn:aws:s3:::myapp-dr-us-west-2"

SSE-KMS objects are not replicated by default

What replicates is driven by how the SOURCE objects are encrypted. S3 replication skips SSE-KMS-encrypted source objects unless the replication rule opts in (source_selection_criteria.sse_kms_encrypted_objects) and names a replica_kms_key_id, with the role granted kms:Decrypt on the source key and kms:Encrypt on the replica key. The fintech and healthtech buckets are KMS-encrypted by default, and the kits' shipped replication rules do not configure this — so as shipped, their encrypted objects will not replicate until you extend the rule. (If the destination bucket applies default SSE-KMS to incoming plaintext replicas, the role additionally needs kms:GenerateDataKey on that key.) After your first apply, verify replication (S3 replication metrics, or aws s3api head-object on a fresh test object in the destination). The healthtech PHI bucket adds one more requirement: it uses S3 Object Lock, so its replication destination must also have Object Lock enabled, and the role needs s3:GetObjectRetention/s3:GetObjectLegalHold on the source. See replicating KMS-encrypted objects.

Migrating existing infrastructure?

If you already have Terraform using terraform-aws-modules, the kits are not the right starting point. See the Brownfield Migration Kit instead; it covers switching existing modules to compliance.tf without state surgery or resource recreation.

For teams that are starting fresh, the starter kits get you to a working, compliant baseline in one terraform apply.

On this page

Ask AI about this

Help improve this page