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:
| Playbook | Difficulty | Time |
|---|---|---|
| S3 bucket | Minor fixes | 15-30 minutes per instance |
| RDS | Minor to significant | 15-30 minutes per instance, or hours if the instance must be rebuilt for encryption |
| VPC | Minor fixes | 10-15 minutes per instance |
| EC2 instance | Minor fixes | 15-30 minutes per instance |
Before you start
- A registry token.
terraform loginfor 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 tfChange 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 initterraform 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 planIf 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 applyFor 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 area | What AWS requires | Applies to |
|---|---|---|
| RDS storage encryption | A snapshot, an encrypted copy and a restore to a new instance or cluster | RDS, RDS Aurora |
| Encryption at rest on clusters and file systems | Recreating the resource, depending on the service and its current settings | ElastiCache, EMR, FSx, Redshift |
| S3 Object Lock | Nothing 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 on | S3 |
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.
| Module | Migration impact | Usual fix | Time |
|---|---|---|---|
| ACM | Usually clean | Few controls to satisfy | 5-10 min |
| ALB | Minor fixes | Access logging, SSL policy | 15-30 min |
| API Gateway V2 | Minor fixes | Access logging, TLS configuration | 15-30 min |
| AppSync | Usually clean | Few controls to satisfy | 5-10 min |
| Auto Scaling | Minor fixes | Launch template IMDSv2, detailed monitoring | 15-30 min |
| CloudFront | Minor fixes | TLS policy, logging or WAF association | 15-30 min |
| CloudWatch | Usually clean | Few controls to satisfy | 5-10 min |
| DMS | Minor fixes | Encryption, logging configuration | 15-30 min |
| DynamoDB Table | Minor fixes | Encryption configuration, deletion protection | 15-30 min |
| EC2 Instance | Minor fixes | IMDSv2 (metadata_options), detailed monitoring | 15-30 min |
| ECR | Minor fixes | Image scanning, encryption, lifecycle policy | 15-30 min |
| ECS | Minor fixes | Container insights, logging configuration | 10-15 min |
| EFS | Minor fixes | Encryption at rest, backup policy | 15-30 min |
| EKS | Minor fixes | Control plane logging, secrets encryption | 30-60 min |
| ElastiCache | Minor to significant | Encryption at rest may need a rebuild | 30 min - hours |
| ELB (Classic) | Minor fixes | Access logging, SSL policy | 15-30 min |
| EMR | Minor to significant | Encryption at rest may need a rebuild | 30 min - hours |
| FSx | Minor to significant | Encryption at rest may need a rebuild | 30 min - hours |
| KMS | Usually clean | Few controls to satisfy | 5-10 min |
| Lambda | Usually clean | Few controls to satisfy | 5-10 min |
| MSK Kafka Cluster | Minor fixes | Encryption in transit, logging configuration | 15-30 min |
| Network Firewall | Minor fixes | Logging, encryption configuration | 15-30 min |
| OpenSearch | Minor fixes | Encryption, node-to-node encryption, logging | 15-30 min |
| RDS | Minor to significant | Encryption at rest may need a rebuild; backup retention, deletion protection | 15-30 min, or hours with a rebuild |
| RDS Aurora | Minor to significant | Encryption at rest may need a rebuild; backup retention | 15-30 min, or hours with a rebuild |
| Redshift | Minor to significant | Encryption may need a rebuild | 30 min - hours |
| S3 Bucket | Minor fixes | Logging, KMS key, replication | 15-30 min |
| Secrets Manager | Usually clean | Few controls to satisfy | 5-10 min |
| SNS | Usually clean | Few controls to satisfy | 5-10 min |
| SQS | Usually clean | Few controls to satisfy | 5-10 min |
| SSM Parameter | Usually clean | Few controls to satisfy | 5-10 min |
| Step Functions | Usually clean | Few controls to satisfy | 5-10 min |
| VPC | Minor fixes | map_public_ip_on_launch = false (SOC 2) | 10-15 min |
| VPN Gateway | Usually clean | Few controls to satisfy | 5-10 min |
| WAFv2 | Usually clean | Few controls to satisfy | 5-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
- Point the source back at
terraform-aws-modules/..., keeping the version constraint. - Run
terraform init. - 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
| Error | Cause | Fix |
|---|---|---|
401 Unauthorized or 403 Forbidden during init | No token for this hostname, an expired terraform login token, or a framework your plan does not include | Check 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 constraint | The pinned version is outside the supported range | Upgrade the upstream module first; see Version compatibility |
| The plan passes, but no control ever fires | The module is not in the catalog and came through the pass-through | Check the module catalog |
must be replaced on a resource after the source change | A control sets a value AWS cannot change in place | See Controls that need a rebuild |