aws/aws-cdk

rds: RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE not propagated to child resources in RDS constructs

Open

#37780 opened on May 6, 2026

View on GitHub
 (1 comment) (0 reactions) (0 assignees)TypeScript (10,710 stars) (3,530 forks)batch import
@aws-cdk/aws-rdsbugeffort/mediumgood first issuep2

Description

Describe the bug

When RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE is set on DatabaseCluster, DatabaseInstance, or ServerlessCluster, the helper resources they create (subnet groups and instances) do not inherit the policy. They are silently left with CloudFormation's default Delete behavior, meaning they will attempt to be destroyed on stack deletion but usually fail because they are in use.


Affected Constructs

  • aws-cdk-lib/aws-rds.DatabaseClusterNew
  • aws-cdk-lib/aws-rds.DatabaseInstanceNew
  • aws-cdk-lib/aws-rds.ServerlessClusterNew

##Root Cause

Found three references in RDS that compare removal policies against RemovalPolicy.RETAIN only, missing RETAIN_ON_UPDATE_OR_DELETE which was added later.

  1. helperRemovalPolicy in aws-rds/lib/private/util.ts

This function determines the policy applied to helper resources (subnet groups, cluster instances). It predates RETAIN_ON_UPDATE_OR_DELETE and only handles RETAIN: https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-rds/lib/private/util.ts#L151

  export function helperRemovalPolicy(basePolicy?: RemovalPolicy): RemovalPolicy {
    return basePolicy === RemovalPolicy.RETAIN
      ? RemovalPolicy.RETAIN
      : RemovalPolicy.DESTROY;
  }

RETAIN_ON_UPDATE_OR_DELETE falls through to DESTROY, which is then suppressed by renderUnless, resulting in undefined being passed to SubnetGroup — so no policy is applied at all.

  1. defaultDeletionProtection in aws-rds/lib/private/util.ts

Deletion protection is not auto-enabled when removalPolicy is RETAIN_ON_UPDATE_OR_DELETE, even though it is for RETAIN: https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-rds/lib/private/util.ts#L83

  return deletionProtection ?? (removalPolicy === RemovalPolicy.RETAIN ? true : undefined);
  1. ServerlessClusterNew subnet group in aws-rds/lib/serverless-cluster.ts

An inline check independent of helperRemovalPolicy has the same problem: https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-rds/lib/serverless-cluster.ts#L475

  removalPolicy: props.removalPolicy === RemovalPolicy.RETAIN ? props.removalPolicy : undefined,

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Library Version

RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE was introduced in aws/aws-cdk#26602.

Expected Behavior

"Postgres2V17ClusterSubnets001BC406": {
   "Type": "AWS::RDS::DBSubnetGroup",
   "Properties": {
    "DBSubnetGroupDescription": "Subnets for Cluster database",
    "SubnetIds": [
     "subnet-0123",
     "subnet-01234",
     "subnet-012345"
    ],
   },
   "UpdateReplacePolicy": "Retain",
   "DeletionPolicy": "RetainExceptOnCreate",
   "Metadata": {
    "aws:cdk:path": "Postgres2V17/Cluster/Subnets/Default"
   }
  },

Current Behavior

"Postgres2V17ClusterSubnets001BC406": {
   "Type": "AWS::RDS::DBSubnetGroup",
   "Properties": {
    "DBSubnetGroupDescription": "Subnets for Cluster database",
    "SubnetIds": [
     "subnet-0123",
     "subnet-01234",
     "subnet-012345"
    ],
   },
   "Metadata": {
    "aws:cdk:path": "Postgres2V17/Cluster/Subnets/Default"
   }
  },

Reproduction Steps

const cluster = new rds.DatabaseCluster(stack, 'Cluster', {
  engine: rds.DatabaseClusterEngine.auroraPostgres({
    version: rds.AuroraPostgresEngineVersion.VER_17_2,
  }),
  vpc,
  removalPolicy: cdk.RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE,
});

Possible Solution

helperRemovalPolicy — pass RETAIN_ON_UPDATE_OR_DELETE through rather than collapsing it to RETAIN. Helper resources are created before the primary resource, so if their creation fails and the stack rolls back, RetainExceptOnCreate correctly allows them to be cleaned up rather than orphaned:

  export function helperRemovalPolicy(basePolicy?: RemovalPolicy): RemovalPolicy {
    return basePolicy === RemovalPolicy.RETAIN || basePolicy ===
  RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE
      ? basePolicy
      : RemovalPolicy.DESTROY;
  }

defaultDeletionProtection — extend the condition:

  return deletionProtection ?? (
    removalPolicy === RemovalPolicy.RETAIN || removalPolicy === RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE
      ? true
      : undefined
  );

serverless-cluster.ts — replace the inline check with renderUnless and helperRemovalPolicyto stay consistent with DatabaseCluster and DatabaseInstance:

  removalPolicy: renderUnless(helperRemovalPolicy(props.removalPolicy), RemovalPolicy.DESTROY),

Additional Information/Context

No response

AWS CDK Library version (aws-cdk-lib)

2.240.0

AWS CDK CLI version

2.1108.0 (build eace286)

Node.js Version

v22.19.0

OS

macOS Tahoe 26.4.1 (25E253)

Language

TypeScript

Language Version

No response

Other information

No response

Contributor guide