aws / aws/aws-cdk

aws-ec2: cdk ignores ReplaceAndDelete always policy in the diff output

Open
#32,472 6 comments 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-ec2 bug needs-reproduction p1
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### Describe the bug

Bear with me, as I'm not sure whether this is actually on CloudFormation or CDK side - or in the middle.

I have a `Vpc-Stack` that is defined as follows, that was previously deployed successfully:

```
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

export class VpcStack extends cdk.Stack {

readonly vpc: ec2.Vpc;
readonly dbSecurityGroup: ec2.SecurityGroup;
readonly egressSubnets: ec2.SubnetSelection;
readonly isolatedSubnets: ec2.SubnetSelection;
readonly publicSubnets: ec2.SubnetSelection;

constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);

this.vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs: 2,
subnetConfiguration: [
{ cidrMask: 24, name: 'Isolated', subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
{ cidrMask: 24, name: 'Public', subnetType: ec2.SubnetType.PUBLIC },
{ cidrMask: 24, name: 'Egress', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
],
gatewayEndpoints: {
S3: {
service: ec2.GatewayVpcEndpointAwsService.S3,
},
},
});

this.isolatedSubnets = this.vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_ISOLATED });
this.egressSubnets = this.vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS });
this.publicSubnets = this.vpc.selectSubnets({ subnetType: ec2.SubnetType.PUBLIC });

// Create a Security Group for the db subnet
this.dbSecurityGroup = new ec2.SecurityGroup(this, 'DbSecurityGroup', {
// securityGroupName: 'DbSecurityGroup',
vpc: this.vpc,
description: 'Security group for DB subnet',
});

const bastionHostSG = new ec2.SecurityGroup(this, 'BastionHostSG', {
securityGroupName: 'BastionSecurityGroup',
vpc: this.vpc,
description: 'Security group for BastionHosts',
});

new ec2.BastionHostLinux(this, 'BastionHost', {
vpc: this.vpc,
requireImdsv2: true,
securityGroup: bastionHostSG,
});

// Allow application traffic to DB subnets
this.egressSubnets.subnets!.forEach((subnet) => {
this.dbSecurityGroup.addIngressRule(ec2.Peer.ipv4(subnet.ipv4CidrBlock), ec2.Port.tcp(5432), 'Allow internal PostgreSQL traffic');
this.dbSecurityGroup.addIngressRule(ec2.Peer.ipv4(subnet.ipv4CidrBlock), ec2.Port.tcp(6379), 'Allow internal Redis traffic');
});

}
}

```

Now, today, `cdk diff` showed only CDK metadata that would need a REPLACE:
```
Stack Vpc-Stack
Conditions
[~] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"il-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]} to {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-3"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-4"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"il-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]}]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}

start: Building 2575bd4cf5e3931a406f6c71ad10f7e7989285d82ef405e8f330cdc868196e97:current_account-current_region
success: Built 2575bd4cf5e3931a406f6c71ad10f7e7989285d82ef405e8f330cdc868196e97:current_account-current_region
start: Publishing 2575bd4cf5e3931a406f6c71ad10f7e7989285d82ef405e8f330cdc868196e97:current_account-current_region
success: Published 2575bd4cf5e3931a406f6c71ad10f7e7989285d82ef405e8f330cdc868196e97:current_account-current_region
```

However, when doing a subsequent `cdk deploy`, CloudFormation suddenly started to replace an EC2 instance:

```

Vpc-Stack
Vpc-Stack: deploying... [1/5]
Vpc-Stack: creating CloudFormation changeset...
Vpc-Stack | 0/4 | 12:41:01 PM | UPDATE_IN_PROGRESS | AWS::CloudFormation::Stack | Vpc-Stack User Initiated
Vpc-Stack | 0/4 | 12:41:05 PM | UPDATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata)
Vpc-Stack | 1/4 | 12:41:06 PM | UPDATE_COMPLETE | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata)
Vpc-Stack | 1/4 | 12:41:07 PM | UPDATE_IN_PROGRESS | AWS::EC2::Instance | BastionHost/Resource (BastionHost30F9ED05) Requested update requires the creation of a new physical resource; hence creating one.
Vpc-Stack | 1/4 | 12:41:08 PM | UPDATE_IN_PROGRESS | AWS::EC2::Instance | BastionHost/Resource (BastionHost30F9ED05) Resource creation Initiated
Vpc-Stack | 2/4 | 12:41:29 PM | UPDATE_COMPLETE | AWS::EC2::Instance | BastionHost/Resource (BastionHost30F9ED05)
Vpc-Stack | 3/4 | 12:41:31 PM | UPDATE_COMPLETE_CLEA | AWS::CloudFormation::Stack | Vpc-Stack
Vpc-Stack | 3/4 | 12:41:32 PM | DELETE_IN_PROGRESS | AWS::EC2::Instance | BastionHost/Resource (BastionHost30F9ED05)
3/4 Currently in progress: Vpc-Stack, BastionHost30F9ED05
Vpc-Stack | 2/4 | 12:42:54 PM | DELETE_COMPLETE | AWS::EC2::Instance | BastionHost/Resource (BastionHost30F9ED05)
Vpc-Stack | 3/4 | 12:42:54 PM | UPDATE_COMPLETE | AWS::CloudFormation::Stack | Vpc-Stack

✅ Vpc-Stack
```

### Regression Issue

- [ ] Select this option if this issue appears to be a regression.

### Last Known Working CDK Version

_No response_

### Expected Behavior

cdk diff should show accurate resource re-creation information.

### Current Behavior

`cdk diff` did not show that an EC2 would be replaced during deployment.

### Reproduction Steps

Code above.

### Possible Solution

_No response_

### Additional Information/Context

```
npm list
cdk@0.1.0
├── @eslint/js@9.16.0
├── @stylistic/eslint-plugin@2.12.0
├── @types/babel__traverse@7.20.6
├── @types/eslint__js@8.42.3
├── @types/js-yaml@4.0.9
├── @types/node@20.17.9
├── @typescript-eslint/eslint-plugin@8.18.0
├── @typescript-eslint/parser@8.18.0
├── aws-cdk-lib@2.172.0
├── aws-cdk@2.172.0
├── cdk-nag@2.34.21
├── cloudwatch-retention-setter@0.0.15
├── constructs@10.4.2
├── eslint-import-resolver-typescript@3.7.0
├── eslint-plugin-import@2.31.0
├── eslint@9.16.0
├── js-yaml@4.1.0
├── source-map-support@0.5.21
├── typescript-eslint@8.18.0
└── typescript@5.7.2
```

### CDK CLI Version

2.172.0 (build 0f666c5)

### Framework Version

_No response_

### Node.js Version

v22.11.0

### OS

MacOS

### Language

TypeScript

### Language Version

_No response_

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the VpcStack example with CDK CLI and aws-cdk-lib 2.172.0, then compare the cdk diff output with the CloudFormation changeset and deployment events for BastionHost/Resource. Done means cdk diff accurately reports the EC2 instance replacement that deployment performs.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, typescript
Domain
cloud, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.