aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.5k stars 3.85k forks source link

aws-backup: Parameter names with incorrect casing for AWS::Backup::Framework L1 construct in Python #29117

Open pwed opened 7 months ago

pwed commented 7 months ago

Describe the bug

Can not deploy AWS Backup Audit Manager Framework using the L1 construct

The issue is that the property name for the sub type FrameworkControl.ControlScope.ComplianceResourceTypes is synthesized as complianceResourceTypes (lowercase c) and CFN will fail validation.

Expected Behavior

L1 construct for AWS::Backup::Framework.FrameworkControl.ControlScope.ComplianceResourceTypes would synth with the correct casing

        "DefaultFramework": {
            "Type": "AWS::Backup::Framework",
            "Properties": {
                "FrameworkControls": [
                    {
                        "ControlInputParameters": [],
                        "ControlName": "BACKUP_RESOURCES_PROTECTED_BY_BACKUP_PLAN",
                        "ControlScope": {
                            "ComplianceResourceTypes": [ // <-- Expect Upper Case "C"
                                "RDS"
                            ]
                        }
                    }
              ]
         }
    }

Current Behavior

L1 construct for AWS::Backup::Framework.FrameworkControl.ControlScope.ComplianceResourceTypes synth produces the following with a lowercase "c"

        "DefaultFramework": {
            "Type": "AWS::Backup::Framework",
            "Properties": {
                "FrameworkControls": [
                    {
                        "ControlInputParameters": [],
                        "ControlName": "BACKUP_RESOURCES_PROTECTED_BY_BACKUP_PLAN",
                        "ControlScope": {
                            "complianceResourceTypes": [ // <-- Produces lower case "c"
                                "RDS"
                            ]
                        }
                    }
              ]
         }
    }
# cdk deploy

...

The stack named backup-audit-sandpit failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE: Properties validation failed for resource Framework with message:
#/FrameworkControls/1/ControlScope: extraneous key [complianceResourceTypes] is not permitted

Reproduction Steps

from aws_cdk import (
    aws_backup as backup,
    Stack,
    App,
)

class BackupAudit(Stack):
    def __init__(self, app: App, id: str, **kwargs) -> None:
        super().__init__(app, id, **kwargs)

        default_framework = backup.CfnFramework(
            self,
            "DefaultFramework",
            framework_controls=[
                backup.CfnFramework.FrameworkControlProperty(
                    control_name="BACKUP_RESOURCES_PROTECTED_BY_BACKUP_PLAN",
                    control_input_parameters=[],
                    control_scope=backup.CfnFramework.ControlScopeProperty(
                        compliance_resource_types=[
                            "RDS",
                            "Aurora",
                            "EFS",
                            "EC2",
                            "EBS",
                            "DynamoDB",
                            "FSx",
                        ],
                    ),
                ),
            ],
        )

app = App()

BackupAudit(app, "backup-audit-sandpit")

app.synth()

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.127.0 (build 6c90efc)

Framework Version

aws-cdk-lib 2.128.0

Node.js Version

v20.11.0

OS

Ubuntu 22.04 (WSL)

Language

Python

Language Version

Python 3.10.12

Other information

Passing a raw dictionary into control_scope with the correct key values will produce the correct CFN output

The issue only seems to occur when you use the backup.CfnFramework.ControlScopeProperty() class as an input

pwed commented 7 months ago

I have tested with a typescript project and this doesn't seem to be an issue as you can just pass in a Dict with the correct key names

// aws-backup-test/lib/aws-backup-test-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as backup from 'aws-cdk-lib/aws-backup';

export class AwsBackupTestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const backupFramework = new backup.CfnFramework(this, "Framework", {
      frameworkControls: [
        {
          controlName: "BACKUP_RESOURCES_PROTECTED_BY_BACKUP_PLAN",
          controlScope: {
            "ComplianceResourceTypes": [
              "RDS"
            ]
          }
        }
      ]
    })
  }
}
pwed commented 7 months ago

A workaround I have found for python is to not use the backup.CfnFramework.ControlScopeProperty() class and just pass in a raw Dict as the value for control_scope.

from aws_cdk import (
    aws_backup as backup,
    Stack,
    App,
)

class BackupAudit(Stack):
    def __init__(self, app: App, id: str, **kwargs) -> None:
        super().__init__(app, id, **kwargs)

        default_framework = backup.CfnFramework(
            self,
            "DefaultFramework",
            framework_controls=[
                backup.CfnFramework.FrameworkControlProperty(
                    control_name="BACKUP_RESOURCES_PROTECTED_BY_BACKUP_PLAN",
                    control_input_parameters=[],
                    control_scope={
                        "ComplianceResourceTypes": [
                            "RDS",
                            "Aurora",
                            "EFS",
                            "EC2",
                            "EBS",
                            "DynamoDB",
                            "FSx",
                        ],
                    },
                ),
            ],
        )

app = App()

BackupAudit(app, "backup-audit-sandpit")

app.synth()
pahud commented 7 months ago

The controlScope is type any which means it's not typed and you'll need to pass a JSON dict to it as you mentioned above. https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_backup.CfnFramework.FrameworkControlProperty.html#controlscope

pwed commented 7 months ago

Hi @pahud,

Thanks for your response

My issue was specifically with the python class backup.CfnFramework.ControlScopeProperty which can be used to construct the JSON Dict. When using this class, and using the key work argument compliance_resource_types, I would expect it to render correctly.

I peeked into the source and found that the L1 constructs are generated by a build step so could not see a simple way to override the output of this type of class