Repository: cloudcomponents/cdk-constructs (language: python)

Issue:
Updates to DummyTaskDefinition break the stack and prevent future updates
If you try to update any attribute on the DummyTaskDefinition resource (for example, the optional family prop), then it fails with this error:

```
5:14:45 AM | UPDATE_FAILED        | Custom::BlueGreenService                  | EcsServiceCustomResource5EDA8C27
CustomResource attribute error: Vendor response doesn't contain taskDefinition.taskDefinitionArn key in object arn:aws:cloudformation:us-east-1:768404655438:stack/EcsTaggingTest/91a55fa0-4b94-11ec-a731-0eacf7b0a8db|DummyTaskDefinitionE3D9D432|fcad085b-b5a2-4a25-bbad-a66a696a7803 in S3 bucke
t cloudformation-custom-resource-storage-useast1
```

The DummyTaskDefinition isn't really intended to be used / updated, so it looks like updates weren't intended to be support.  However, when this happens, it triggers a rollback causing CloudFormation to call update on the custom resource again, leaving it in this broken state.  After this happens, subsequent deployments to the stack fail, and you can't really recover without updating the custom resource.

This can happen unintentionally during a refactor where the node address changes, because the node address is the default value for the optional family property.


Discussion / hints:

Fix patch:
diff --git a/packages/cdk-blue-green-container-deployment/src/dummy-task-definition.ts b/packages/cdk-blue-green-container-deployment/src/dummy-task-definition.ts
index f5397a639..74455f89b 100644
--- a/packages/cdk-blue-green-container-deployment/src/dummy-task-definition.ts
+++ b/packages/cdk-blue-green-container-deployment/src/dummy-task-definition.ts
@@ -1,7 +1,7 @@
 import { NetworkMode } from '@aws-cdk/aws-ecs';
 import { Role, ServicePrincipal, ManagedPolicy, PolicyStatement, Effect, IRole } from '@aws-cdk/aws-iam';
 import { Construct } from '@aws-cdk/core';
-import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId, PhysicalResourceIdReference } from '@aws-cdk/custom-resources';
+import { AwsCustomResource, AwsCustomResourcePolicy, AwsSdkCall, PhysicalResourceId, PhysicalResourceIdReference } from '@aws-cdk/custom-resources';
 
 export interface IDummyTaskDefinition {
   readonly executionRole: IRole;
@@ -63,41 +63,46 @@ export class DummyTaskDefinition extends Construct implements IDummyTaskDefiniti
     this.containerName = props.containerName ?? 'sample-website';
     this.containerPort = props.containerPort ?? 80;
 
-    const taskDefinition = new AwsCustomResource(this, 'DummyTaskDefinition', {
-      resourceType: 'Custom::DummyTaskDefinition',
-      onCreate: {
-        service: 'ECS',
-        action: 'registerTaskDefinition',
-        parameters: {
-          requiresCompatibilities: ['FARGATE'],
-          family: this.family,
-          executionRoleArn: this.executionRole.roleArn,
-          networkMode: NetworkMode.AWS_VPC,
-          cpu: '256',
-          memory: '512',
-          containerDefinitions: [
-            {
-              name: this.containerName,
-              image: props.image,
-              portMappings: [
-                {
-                  hostPort: this.containerPort,
-                  protocol: 'tcp',
-                  containerPort: this.containerPort,
-                },
-              ],
-            },
-          ],
-        },
-        physicalResourceId: PhysicalResourceId.fromResponse('taskDefinition.taskDefinitionArn'),
+    const registerTaskDefinition: AwsSdkCall = {
+      service: 'ECS',
+      action: 'registerTaskDefinition',
+      parameters: {
+        requiresCompatibilities: ['FARGATE'],
+        family: this.family,
+        executionRoleArn: this.executionRole.roleArn,
+        networkMode: NetworkMode.AWS_VPC,
+        cpu: '256',
+        memory: '512',
+        containerDefinitions: [
+          {
+            name: this.containerName,
+            image: props.image,
+            portMappings: [
+              {
+                hostPort: this.containerPort,
+                protocol: 'tcp',
+                containerPort: this.containerPort,
+              },
+            ],
+          },
+        ],
       },
-      onDelete: {
-        service: 'ECS',
-        action: 'deregisterTaskDefinition',
-        parameters: {
-          taskDefinition: new PhysicalResourceIdReference(),
-        },
+      physicalResourceId: PhysicalResourceId.fromResponse('taskDefinition.taskDefinitionArn'),
+    };
+
+    const deregisterTaskDefinition: AwsSdkCall = {
+      service: 'ECS',
+      action: 'deregisterTaskDefinition',
+      parameters: {
+        taskDefinition: new PhysicalResourceIdReference(),
       },
+    };
+
+    const taskDefinition = new AwsCustomResource(this, 'DummyTaskDefinition', {
+      resourceType: 'Custom::DummyTaskDefinition',
+      onCreate: registerTaskDefinition,
+      onUpdate: registerTaskDefinition,
+      onDelete: deregisterTaskDefinition,
       policy: AwsCustomResourcePolicy.fromStatements([
         new PolicyStatement({
           effect: Effect.ALLOW,


Test patch:
diff --git a/packages/cdk-blue-green-container-deployment/src/__tests__/__snapshots__/dummy-task-definition.test.ts.snap b/packages/cdk-blue-green-container-deployment/src/__tests__/__snapshots__/dummy-task-definition.test.ts.snap
index f74c7188f..21819f400 100644
--- a/packages/cdk-blue-green-container-deployment/src/__tests__/__snapshots__/dummy-task-definition.test.ts.snap
+++ b/packages/cdk-blue-green-container-deployment/src/__tests__/__snapshots__/dummy-task-definition.test.ts.snap
@@ -116,6 +116,21 @@ Object {
             "Arn",
           ],
         },
+        "Update": Object {
+          "Fn::Join": Array [
+            "",
+            Array [
+              "{\\"service\\":\\"ECS\\",\\"action\\":\\"registerTaskDefinition\\",\\"parameters\\":{\\"requiresCompatibilities\\":[\\"FARGATE\\"],\\"family\\":\\"c86e1e5d419d3c124c7e40411b7e7805f91621e162\\",\\"executionRoleArn\\":\\"",
+              Object {
+                "Fn::GetAtt": Array [
+                  "DummyTaskDefinitionExecutionRole715DBD43",
+                  "Arn",
+                ],
+              },
+              "\\",\\"networkMode\\":\\"awsvpc\\",\\"cpu\\":\\"256\\",\\"memory\\":\\"512\\",\\"containerDefinitions\\":[{\\"name\\":\\"sample-website\\",\\"image\\":\\"image\\",\\"portMappings\\":[{\\"hostPort\\":80,\\"protocol\\":\\"tcp\\",\\"containerPort\\":80}]}]},\\"physicalResourceId\\":{\\"responsePath\\":\\"taskDefinition.taskDefinitionArn\\"}}",
+            ],
+          ],
+        },
       },
       "Type": "Custom::DummyTaskDefinition",
       "UpdateReplacePolicy": "Delete",