Sign in
Menu
Sign up
Docs
Operational rulesRule catalog

Set deletion protection

For platform engineers deciding whether to apply resource_set_deletion_protection: what it changes, how to enable it, and how to check it worked.

Sets the provider's deletion-protection attribute to true on RDS instances, Aurora clusters, DynamoDB tables and load balancers.

What this rule does

Sets a provider attribute on each matching resource, one attribute name per resource type:

ResourceAttribute set
aws_db_instancedeletion_protection = true
aws_rds_clusterdeletion_protection = true
aws_dynamodb_tabledeletion_protection_enabled = true
aws_lbenable_deletion_protection = true

A resource that leaves the attribute unset gains it. A resource that already sets it, to a literal or to a module variable, keeps what it has: an explicit value in the module wins, and the rule reports not_applicable for it. Unlike a lifecycle argument, this is a provider attribute, so once set the protection lives in AWS and applies to every client, not only to Terraform.

Affected resources

ResourceServiceWhy
aws_db_instanceAmazon RDSdeletion_protection, set by the rule where the module leaves it out
aws_rds_clusterAmazon Auroradeletion_protection, set by the rule where the module leaves it out
aws_dynamodb_tableAmazon DynamoDBdeletion_protection_enabled, set by the rule where the module leaves it out
aws_lbElastic Load Balancingenable_deletion_protection, set by the rule where the module leaves it out

Before and after

Before (a module without the change):

resource "aws_db_instance" "this" {
  # ... resource configuration ...

  tags = var.tags
}

After (with Set deletion protection applied):

resource "aws_db_instance" "this" {
  # ... resource configuration ...

  deletion_protection = true

  tags = var.tags
}

The attribute name differs per resource type (see "What this rule does" above); the shape of the change is the same for each.

The only change is the rule transformation. All existing arguments, outputs, and module behavior remain identical.

Why this rule exists

These services offer deletion protection as an attribute AWS itself enforces. Once set, a terraform destroy, a console click and an API call are all refused until someone turns it off first.

It is a stronger guard than prevent_destroy, which only Terraform honours. Upstream terraform-aws-modules for RDS, Aurora, DynamoDB and ALB already pass the attribute from a module input, so there the input is the place to set it. This rule is for modules that leave the attribute out.

RepositoryIssueTitle
hashicorp/terraform#22544"Variables may not be used here" for prevent_destroy
hashicorp/terraform#18367Feature request: support prevent_destroy for modules
hashicorp/terraform#24746Ability to use locals or wildcards in ignore_changes lifecycle block
hashicorp/terraform#24188Support for dynamic blocks and meta-arguments
hashicorp/terraform#27360A method to override configuration and meta arguments within a module
opentofu/opentofu#1329Support variables in lifecycle blocks

Default configuration

This rule takes no parameters.

The HCL this rule is written in, with its parameters and the transformers it runs, is published at resource_set_deletion_protection.

How to enable

On a framework host, add the rule on top of your organization's Baseline with ?add_rules= in the HTTPS module source:

module "rds" {
  source = "https://soc2.compliance.tf/terraform-aws-modules/rds/aws?version=x.y.z&add_rules=resource_set_deletion_protection"
}

On your organization's host, an org admin adds the rule to the Baseline, which reaches every module served from that host, or to a named ruleset that a module selects with ?ruleset=:

module "rds" {
  source = "https://bobthecorp.compliance.tf/terraform-aws-modules/rds/aws?version=x.y.z&ruleset=<ruleset-name>"
}

Upstream terraform-aws-rds, terraform-aws-rds-aurora, terraform-aws-dynamodb-table and terraform-aws-alb already pass the attribute from a module input (deletion_protection, deletion_protection_enabled, enable_deletion_protection), so on them the rule records not_applicable and the input is the place to set it. The rule is for modules that leave the attribute out.

Replace x.y.z with the release you use and bobthecorp with your organization's alias. Which parameters each host accepts, and which it refuses with a 400, is on Rulesets in the console.

How to check it worked

Read compliancetf-manifest.json in the module you received. Its entry for this rule carries an outcome: enforced means the change was made, not_applicable means nothing matched - no resource of a targeted type, or the module already carried the identical change. Do not infer enforcement from a successful terraform init; a rule that changes nothing also inits cleanly.

Known limits

  • The four resource types are fixed for this rule and cannot be retargeted by a parameter.
  • Only a resource that leaves the attribute unset is changed. The upstream terraform-aws-modules for RDS, Aurora, DynamoDB and ALB already expose it as a module input (deletion_protection, deletion_protection_enabled, enable_deletion_protection), and an explicit value in the module wins, so on those modules the rule reports not_applicable and the input is the place to set it.
  • Sets the attribute only. It does not add prevent_destroy; use lifecycle_prevent_destroy_data for the Terraform-side guard as well.
  • Does not protect the data inside the resource, only the resource itself.
  • Only .tf and .tofu files are read. Modules written in JSON syntax (.tf.json, .tofu.json) are left untouched.
  • Only the module's own directory and its first-level modules/* submodules are processed. Modules that this module calls by source URL are separate downloads and are handled on their own request.
  • Works with Terraform 1.x and OpenTofu 1.6 and later. The rule reads ordinary HCL, and anything it changes stays ordinary HCL.

Break-glass path

On a framework host, add ?rules=-resource_set_deletion_protection to the module source URL and run terraform init; the source changed, so Terraform installs a fresh copy without this rule. When you are done, remove the override and run terraform init again to restore it. On your organization's host the parameter is refused with a 400 rather than ignored. The rule comes from the organization's configuration: a Baseline rule applies to every module served from the bare organization form, so an admin disables it for the duration of the operation, or keeps it out of the Baseline and in a ruleset that only the modules needing it select. On a project environment the posture is the bound snapshot and its waivers. See Baseline rules and rulesets.

Failure modes

ScenarioResult
Rule targets a module with no matching resource typesNo-op. Module is delivered unchanged and the manifest records not_applicable.
The module already sets the attribute, from a literal or a variableLeft as is and recorded as not_applicable. Set the module's own input instead.
Someone runs terraform destroy on a protected resourceAWS refuses the deletion and Terraform reports the API error. Set the attribute to false, apply, then destroy.

On this page

Ask AI about this

Help improve this page