Sign in
Menu
Sign up
Docs
Adopt in an existing estate

Migration kit

For a platform lead or engineer moving an existing Terraform estate from terraform-aws-modules to compliance.tf. Follow the steps once for one module: when you finish, it plans against its compliance.tf source, and every control it broke has a fix or a recorded exception. Budget the playbook time for the module, 15-30 minutes per instance for an S3 bucket, plus the token setup on the first run.

If you are planning the whole estate, work through the Assessment checklist first. It lists your modules, flags the risks, and holds the rollout phases.

The kit also has a playbook per module, with a source diff that passes the module's controls and a fix for each plan error it raises:

PlaybookDifficultyTime
S3 bucketMinor fixes15-30 minutes per instance
RDSMinor to significant15-30 minutes per instance, or hours if the instance must be rebuilt for encryption
VPCMinor fixes10-15 minutes per instance
EC2 instanceMinor fixes15-30 minutes per instance

Before you start

  • A registry token. terraform login for your own machine, or a long-lived token for CI. Get started covers the first run; Registry tokens covers tokens for pipelines, and CI/CD integration covers HCP Terraform, Spacelift, env0 and Atlantis.
  • A supported upstream version. Compliance.tf publishes the upstream version numbers, so your version constraint stays. If you pin a version older than the supported range, upgrade the upstream module first, as its own change. See Version compatibility.
  • A module compliance.tf serves. Check the module catalog. A terraform-aws-modules module outside it still downloads, without compliance.tf controls; Compatibility with terraform-aws-modules explains the pass-through.
  • A root you can plan. The same AWS credentials and provider "aws" block you use today. Your AWS credentials and provider stay as they are.

Step 1: Pick the first module and its framework

Start with an S3 bucket in a non-production environment. Its playbook has a configuration that passes, and its plan errors cover most kinds of fix you will meet later: a missing block, a key reference, a dependency on another resource. If you do not use S3, start with a VPC, which raises the fewest plan errors of the four playbooks.

Then pick the framework. Each one has its own registry host, such as soc2.compliance.tf; Registry endpoints lists them. The examples here use soc2. On the Free plan the only framework is CIS AWS v6.0; see Plans and entitlements.

Step 2: Change the source

Point the module at your framework's registry host. Keep the version: the Registry format keeps its version argument and constraint; the HTTPS form takes one exact version in ?version=.

 module "s3_bucket" {
-  source  = "terraform-aws-modules/s3-bucket/aws"
+  source  = "soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws"
   version = "5.0.0"

   bucket = "my-app-data"
 }
 module "s3_bucket" {
-  source  = "terraform-aws-modules/s3-bucket/aws"
-  version = "5.0.0"
+  source  = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0"

   bucket = "my-app-data"
 }

The HTTPS form is the one that accepts ?disable= and ?enable=. It authenticates from .netrc instead of the Terraform CLI configuration; Registry endpoints has both.

Change many sources at once

Change one module's sources across the codebase, then check what changed:

# Files that use the upstream S3 module
rg 'source\s*=\s*"terraform-aws-modules/s3-bucket/aws"' --type tf -l

# Replace (macOS sed; on Linux drop the '' after -i)
find . -name '*.tf' -exec sed -i '' \
  's|"terraform-aws-modules/s3-bucket/aws"|"soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws"|g' {} +

# Every source that now points at compliance.tf
rg 'source\s*=.*compliance\.tf' --type tf

Change one module type per batch. Replacing every terraform-aws-modules/ source in one pass makes the plan errors hard to attribute.

Step 3: Download the new source

terraform init

terraform init installs every module whose source changed; until you run it, terraform plan stops with a Module source has changed error. You need -upgrade only to fetch the same source and version again, for example after your organization changes its rules, or to move to the newest version a constraint allows.

Initializing modules...
Downloading soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws 5.0.0 for s3_bucket...
- s3_bucket in .terraform/modules/s3_bucket

Terraform has been successfully initialized!

Step 4: Plan and fix each error

terraform plan

If the configuration breaks one of the module's controls, the plan fails. The error names the control and what it needs, and links the control's page, which lists the frameworks that map it. Abridged, without the lines where Terraform says where the variable and its validation rule are declared:

│ Error: Invalid value for variable
│
│ ...
│
│ Control 's3_bucket_logging_enabled': S3 bucket access logging is required: configure logging with a valid target bucket
│ Read more: https://compliance.tf/docs/controls/aws/s3_bucket_logging_enabled/

Most fixes add one argument or block. This one adds the logging block:

module "s3_bucket" {
  source  = "soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws"
  version = "5.0.0"

  bucket = "my-app-data"

  logging = {
    target_bucket = "my-logging-bucket"
    target_prefix = "s3-access-logs/my-app-data/"
  }
}

Re-run terraform plan until it passes. The playbook for the module has the fix for each error it raises. If a control does not apply to this resource, turn it off on the source URL and write down why; see Turn controls on and off.

Step 5: Apply

terraform apply

For existing resources, the apply makes in-place changes for the values you added, such as logging or encryption settings. The source change leaves resource addresses as they are, because Terraform tracks a resource by its address in state, not by the module source. The exceptions are in Controls that need a rebuild: check the plan for a must be replaced line before you apply.

Check the result

A migrated module plans with no validation errors. On an existing resource the plan shows No changes. Your infrastructure matches the configuration. or in-place updates only.

To confirm what was downloaded, open .terraform/modules/<module name>/compliancetf-manifest.json. It names the framework the module was built for.

If the plan passes but there is no manifest, the module probably came through the pass-through without compliance.tf controls; check it against the module catalog.

Controls that need a rebuild

Most controls set values AWS can change in place. A few set values AWS cannot change on an existing resource. If the resource already has the setting, the migration is clean. If it does not, either rebuild it or turn the control off for that resource and plan the retrofit separately.

Control areaWhat AWS requiresApplies to
RDS storage encryptionA snapshot, an encrypted copy and a restore to a new instance or clusterRDS, RDS Aurora
Encryption at rest on clusters and file systemsRecreating the resource, depending on the service and its current settingsElastiCache, EMR, FSx, Redshift
S3 Object LockNothing to recreate: AWS turns Object Lock on for an existing versioned bucket. Objects already in the bucket get no retention until you apply it with S3 Batch Operations. Once on, AWS never turns it off, and a rollback leaves it onS3

Migrate the rest of the estate

After the first module plans and applies, batch the rest by migration impact and run terraform plan after each batch. The Assessment checklist holds the rollout phases, the owner per phase and the review checklist for a migration pull request.

ModuleMigration impactUsual fixTime
ACMUsually cleanFew controls to satisfy5-10 min
ALBMinor fixesAccess logging, SSL policy15-30 min
API Gateway V2Minor fixesAccess logging, TLS configuration15-30 min
AppSyncUsually cleanFew controls to satisfy5-10 min
Auto ScalingMinor fixesLaunch template IMDSv2, detailed monitoring15-30 min
CloudFrontMinor fixesTLS policy, logging or WAF association15-30 min
CloudWatchUsually cleanFew controls to satisfy5-10 min
DMSMinor fixesEncryption, logging configuration15-30 min
DynamoDB TableMinor fixesEncryption configuration, deletion protection15-30 min
EC2 InstanceMinor fixesIMDSv2 (metadata_options), detailed monitoring15-30 min
ECRMinor fixesImage scanning, encryption, lifecycle policy15-30 min
ECSMinor fixesContainer insights, logging configuration10-15 min
EFSMinor fixesEncryption at rest, backup policy15-30 min
EKSMinor fixesControl plane logging, secrets encryption30-60 min
ElastiCacheMinor to significantEncryption at rest may need a rebuild30 min - hours
ELB (Classic)Minor fixesAccess logging, SSL policy15-30 min
EMRMinor to significantEncryption at rest may need a rebuild30 min - hours
FSxMinor to significantEncryption at rest may need a rebuild30 min - hours
KMSUsually cleanFew controls to satisfy5-10 min
LambdaUsually cleanFew controls to satisfy5-10 min
MSK Kafka ClusterMinor fixesEncryption in transit, logging configuration15-30 min
Network FirewallMinor fixesLogging, encryption configuration15-30 min
OpenSearchMinor fixesEncryption, node-to-node encryption, logging15-30 min
RDSMinor to significantEncryption at rest may need a rebuild; backup retention, deletion protection15-30 min, or hours with a rebuild
RDS AuroraMinor to significantEncryption at rest may need a rebuild; backup retention15-30 min, or hours with a rebuild
RedshiftMinor to significantEncryption may need a rebuild30 min - hours
S3 BucketMinor fixesLogging, KMS key, replication15-30 min
Secrets ManagerUsually cleanFew controls to satisfy5-10 min
SNSUsually cleanFew controls to satisfy5-10 min
SQSUsually cleanFew controls to satisfy5-10 min
SSM ParameterUsually cleanFew controls to satisfy5-10 min
Step FunctionsUsually cleanFew controls to satisfy5-10 min
VPCMinor fixesmap_public_ip_on_launch = false (SOC 2)10-15 min
VPN GatewayUsually cleanFew controls to satisfy5-10 min
WAFv2Usually cleanFew controls to satisfy5-10 min
  • Usually clean: the plan shows no changes or minor in-place updates.
  • Minor fixes: the plan fails with a few validation errors, each fixed by adding an argument or block.
  • Minor to significant: clean if encryption is already on; otherwise see Controls that need a rebuild.

A catalog module missing from the table has no rating yet. Plan one instance of it and let the result decide its batch.

Compliance.tf modules and upstream terraform-aws-modules work side by side in one configuration, so a half-migrated estate plans and applies normally. Modules you have not moved yet keep working as before.

Undo it

  1. Point the source back at terraform-aws-modules/..., keeping the version constraint.
  2. Run terraform init.
  3. Run terraform plan. Expect no resource changes. Settings AWS cannot reverse, such as Object Lock on a bucket, stay as they are.

The values you added, such as logging and encryption settings, stay on your resources: they are ordinary AWS attributes. Compliance.tf stops checking them at plan time.

Troubleshooting

ErrorCauseFix
401 Unauthorized or 403 Forbidden during initNo token for this hostname, an expired terraform login token, or a framework your plan does not includeCheck that the hostname in .terraformrc, TF_TOKEN_* or .netrc matches the source host exactly. Run terraform login again, or see Plans and entitlements
init finds no module version that matches the constraintThe pinned version is outside the supported rangeUpgrade the upstream module first; see Version compatibility
The plan passes, but no control ever firesThe module is not in the catalog and came through the pass-throughCheck the module catalog
must be replaced on a resource after the source changeA control sets a value AWS cannot change in placeSee Controls that need a rebuild

Next steps

On this page

Ask AI about this

Help improve this page