Customize Compliance-ready Terraform modules
At a glance
- Compliance-ready Terraform modules can be customized with query parameters.
- You can enable or disable specific controls or entire frameworks.
- Uses the HTTPS URL format to apply compliance rules during module retrieval.
- If no compliance layer is applied, modules fall back to the original source. Fallback modules are not subject to compliance.tf controls.
What controls cover
Controls span five broad categories across AWS services:
- Encryption at rest — S3, RDS, EBS, DynamoDB, ElastiCache, Redshift, and others
- Access controls — public-access blocking, endpoint restrictions, IMDSv2 enforcement
- Logging and monitoring — access logging, CloudTrail integration, VPC flow logs
- Data lifecycle — versioning, backup retention, deletion protection
- Network security — TLS policies, security group restrictions
Each framework selects the subset of controls relevant to its requirements. See the full controls catalog for every available control.
Compliance-ready Terraform modules are highly customizable and can be configured to meet the specific requirements of your organization.
Use HTTPS URL format to access compliance-ready Terraform modules
Customization is supported only when accessing the compliance.tf Terraform Registry via the HTTPS URL format, because it requires query string parameters.
Here are some common use cases:
- Enable and disable specific controls. For example, a compliance framework may enable certain controls by default, but you may wish to disable some of them.
- Customize a specific module version by enabling additional important controls that are not enabled by default in the original module.
See the HTTPS URL format section for details on supported arguments and examples.
How it works
The diagram below shows how compliance controls flow from framework definitions and user-defined settings into the final Terraform module. Disabled controls are filtered out, and enabled controls are merged to produce the final configuration.
flowchart TD
A["Framework Controls<br/>(e.g., SOC2, HIPAA)"] --> C[All Active Controls]
B["Enabled Controls<br/>(from __enable__ argument)"] --> C
C --> D["Remove Disabled Controls<br/>(from __disable__ argument)"]
D --> E[Final Controls Set]
E --> F[Generate Terraform Module]Available compliance frameworks
View the list of available compliance frameworks here.
Available controls
View the list of available controls here.
Examples
S3 bucket module with one control enabled
module "s3_bucket" {
source = "https://registry.compliance.tf/terraform-aws-modules/s3-bucket/aws?enable=s3_bucket_versioning_and_lifecycle_policy_enabled"
}
Note about the registry.compliance.tf hostname
The registry.compliance.tf hostname serves the original module with zero controls enabled — a blank-slate baseline. Use it when you want to selectively enable individual controls via ?enable= rather than starting from a framework's full set.
S3 bucket module with SOC2 framework controls enabled
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws"
}
S3 bucket module with HIPAA framework controls enabled
module "s3_bucket" {
source = "https://hipaa.compliance.tf/terraform-aws-modules/s3-bucket/aws"
}
S3 bucket module with FedRAMP Moderate framework controls enabled
module "s3_bucket" {
source = "https://fedrampmoderaterev4.compliance.tf/terraform-aws-modules/s3-bucket/aws"
}
Common Scenarios
These scenarios show how to use the enable/disable parameters for real-world use cases.
Terraform source interpolation
The Terraform Registry format (source = "soc2.compliance.tf/...") does not support variables or interpolation in the source argument. To use variables for environment-based switching, use the HTTPS URL format with string interpolation.
Disable object lock for development environments
Object lock requires bucket recreation for existing buckets, making it impractical in development. A common pattern is to disable it in dev while keeping it enforced in production.
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&disable=s3_bucket_object_lock_enabled"
bucket = "my-app-data-dev"
logging = {
target_bucket = module.logging_bucket.s3_bucket_id
target_prefix = "s3-access-logs/my-app-dev/"
}
}
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0"
bucket = "my-app-data-prod"
logging = {
target_bucket = module.logging_bucket.s3_bucket_id
target_prefix = "s3-access-logs/my-app-prod/"
}
}
Object lock is enforced in production (all SOC2 controls active). In development, it is disabled to avoid the bucket recreation requirement during iterative work.
Enable KMS encryption only for production
KMS encryption (s3_bucket_default_encryption_enabled_kms) adds an auditable key management layer required by PCI DSS, HIPAA, and NIST 800-53. It also adds cost — AWS charges $0.03 per 10,000 KMS requests. In development, the default SSE-S3 encryption (AES-256) may be sufficient.
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0"
bucket = "my-app-data-dev"
logging = {
target_bucket = module.logging_bucket.s3_bucket_id
target_prefix = "s3-access-logs/my-app-dev/"
}
}
module "s3_bucket" {
source = "https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&enable=s3_bucket_default_encryption_enabled_kms"
bucket = "my-app-data-prod"
server_side_encryption_configuration = {
rule = {
apply_server_side_encryption_by_default = {
sse_algorithm = "aws:kms"
kms_master_key_id = module.kms_key.key_arn
}
}
}
logging = {
target_bucket = module.logging_bucket.s3_bucket_id
target_prefix = "s3-access-logs/my-app-prod/"
}
}
The SOC2 endpoint provides baseline encryption (SSE-S3) in both environments. The enable= parameter adds KMS encryption only in production, where the audit trail and key management controls are required.
Use different frameworks for different environments
When production requires a strict framework (SOC2 with all controls) but development needs a lighter posture, use a Terraform variable to switch the framework endpoint.
variable "environment" {
type = string
default = "dev"
}
locals {
# Production: full SOC2 controls
# Development: SOC2 controls with object lock disabled
s3_source = var.environment == "prod" ? (
"https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0"
) : (
"https://soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&disable=s3_bucket_object_lock_enabled"
)
}
module "s3_bucket" {
source = local.s3_source
bucket = "my-app-data-${var.environment}"
logging = {
target_bucket = module.logging_bucket.s3_bucket_id
target_prefix = "s3-access-logs/my-app-${var.environment}/"
}
}
This pattern works because the HTTPS URL format supports string interpolation in the source argument. The Terraform Registry format (source = "soc2.compliance.tf/...") does not support this — use the HTTPS URL format when you need dynamic source selection.