Repository: awslabs/aws-ddk (language: python)

Issue:
Configurator 1.0.0b0 Does not support non-property config 
### Describe the bug

Encountered unsupported property removal_policy

Configuration that is not a CloudFormation property is being added to CFN templates.

### How to Reproduce

```
*P.S. Please do not attach files as it's considered a security risk. Add code snippets directly in the message body as much as possible.*
```


### Expected behavior

_No response_

### Your project

_No response_

### Screenshots

_No response_

### Environment

```
Provide your `pip list` output, particularly the version of the AWS DDK library you used. Providing this information may significantly improve resolution times.
```


### OS

Mac

### Python version

typescript

### AWS DDK version

1.0.0b0

### Additional context

_No response_


Discussion / hints:

Fix patch:
diff --git a/src/config/configurator.ts b/src/config/configurator.ts
index 29509602..2a73265f 100644
--- a/src/config/configurator.ts
+++ b/src/config/configurator.ts
@@ -34,6 +34,17 @@ function readConfigFile(path: string): object {
     throw TypeError("Config file must be in YAML or JSON format");
   }
 }
+function setRemovalPolicy(value: string, node: cdk.CfnResource): void {
+  if (value.toLowerCase() == "destroy") {
+    node.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY);
+  } else if (value.toLowerCase() == "retain") {
+    node.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN);
+  } else if (value.toLowerCase() == "snapshot") {
+    node.applyRemovalPolicy(cdk.RemovalPolicy.SNAPSHOT);
+  } else {
+    throw new Error(`${value} is not a valid removal policy type. Must be one of ['DESTROY', 'RETAIN', & 'SNAPSHOT']`);
+  }
+}
 interface getConfigProps {
   readonly config?: string | object;
 }
@@ -113,13 +124,21 @@ class ConfiguratorAspect implements cdk.IAspect {
   }
   public visit(node: constructs.IConstruct): void {
     if (this.resourceType && cdk.CfnResource.isCfnResource(node) && node.cfnResourceType == this.resourceType) {
-      node.addPropertyOverride(this.propertyName, this.propertyValue);
+      if (this.propertyName == "RemovalPolicy") {
+        setRemovalPolicy(this.propertyValue, node);
+      } else {
+        node.addPropertyOverride(this.propertyName, this.propertyValue);
+      }
     }
 
     const nodePathItemRegex = new RegExp(`^(.*\/)?(${this.resourceId}\/Resource)(\/.*)?$`);
 
     if (this.resourceId && cdk.CfnResource.isCfnResource(node) && nodePathItemRegex.test(node.node.path)) {
-      node.addPropertyOverride(this.propertyName, this.propertyValue);
+      if (this.propertyName == "RemovalPolicy") {
+        setRemovalPolicy(this.propertyValue, node);
+      } else {
+        node.addPropertyOverride(this.propertyName, this.propertyValue);
+      }
     }
   }
 }


Test patch:
diff --git a/test/config.test.ts b/test/config.test.ts
index e1b32778..663a9a6b 100644
--- a/test/config.test.ts
+++ b/test/config.test.ts
@@ -476,3 +476,64 @@ test("Get Tags", () => {
   assert(prodTags.CostCenter === "2015");
   assert(globalTags["global:foo"] === "bar");
 });
+
+test("Config Removal Policy", () => {
+  const sampleConfig = {
+    environments: {
+      dev: {
+        resources: {
+          MyBucket: {
+            RemovalPolicy: cdk.RemovalPolicy.DESTROY,
+          },
+          MyOtherBucket: {
+            RemovalPolicy: cdk.RemovalPolicy.RETAIN,
+          },
+        },
+      },
+    },
+  };
+  const stack = new cdk.Stack();
+  new s3.Bucket(stack, "MyBucket");
+  new s3.Bucket(stack, "MyOtherBucket");
+  new Configurator(stack, sampleConfig, "dev");
+  const template = Template.fromStack(stack);
+  template.hasResource("AWS::S3::Bucket", {
+    DeletionPolicy: "Delete",
+    UpdateReplacePolicy: "Delete",
+  });
+  template.hasResource("AWS::S3::Bucket", {
+    DeletionPolicy: "Retain",
+    UpdateReplacePolicy: "Retain",
+  });
+});
+
+test("Config Removal Policy Invalid", () => {
+  const sampleConfig = {
+    environments: {
+      dev: {
+        resources: {
+          MyBucket: {
+            RemovalPolicy: "foobar",
+          },
+        },
+      },
+    },
+  };
+  const stack = new cdk.Stack();
+  new s3.Bucket(stack, "MyBucket");
+  new Configurator(stack, sampleConfig, "dev");
+  expect(() => {
+    Template.fromStack(stack);
+  }).toThrowError("foobar is not a valid removal policy type. Must be one of ['DESTROY', 'RETAIN', & 'SNAPSHOT']");
+});
+
+test("Config Removal Policy JSON Config", () => {
+  const stack = new cdk.Stack();
+  new s3.Bucket(stack, "MyBucket");
+  new Configurator(stack, "./test/test-config.json", "dev");
+  const template = Template.fromStack(stack);
+  template.hasResource("AWS::S3::Bucket", {
+    DeletionPolicy: "Delete",
+    UpdateReplacePolicy: "Delete",
+  });
+});
diff --git a/test/test-config.json b/test/test-config.json
index 68d50167..6c4a89ff 100644
--- a/test/test-config.json
+++ b/test/test-config.json
@@ -10,6 +10,9 @@
         "AWS::Lambda::Function": {
           "MemorySize": 128,
           "Runtime": "python3.8"
+        },
+        "AWS::S3::Bucket": {
+          "RemovalPolicy": "DESTROY"
         }
       },
       "tags": {"CostCenter": "2014"}