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.36k stars 3.77k forks source link

ssm: unable to use serialized YAML string values as content for CfnDocument #28341

Open chrisrtaylor opened 6 months ago

chrisrtaylor commented 6 months ago

Describe the bug

According to the documentation for the content property of CfnDocument, it should be able to take an object representing JSON formatted content or a String if creating a YAML based document. When trying to use a string value for content, the document fails to create

Expected Behavior

I should be able to define a document using a YAML formatted string as content. The following code should synthesize and create the appropriate resource:

    const ssmDoc = new ssm.CfnDocument(this, "TestSsmDoc", {
      content: `---
schemaVersion: "2.2"
description: "Test SSM doc for CDK."
mainSteps:
  - action: "aws:runShellScript"
    name: "test"
    inputs:
      runCommand: |
        echo "Hello, World!"
`,
      name: "TestSsmDocument",
      documentType: "Command",
      documentFormat: "YAML",
    });

Current Behavior

The above code fails to synthesize with the following error:

CfnSynthesisError: Resolution error: Supplied properties not correct for "CfnDocumentProps"
  content: "---\nschemaVersion: \"2.2\"\ndescription: \"Test SSM doc for CDK.\"\nmainSteps:\n  - action: \"aws:runShellScript\"\n    name: \"test\"\n    inputs:\n      runCommand: |\n        echo \"Hello, World!\"\n" should be an 'object'.
    at ValidationResult.assertSuccess (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/core/lib/runtime.js:1:2707)
    at convertCfnDocumentPropsToCloudFormation (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ssm/lib/ssm.generated.js:1:24518)
    at CfnDocument.renderProperties (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/aws-ssm/lib/ssm.generated.js:1:18952)
    at PostResolveToken.Resources (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/core/lib/cfn-resource.js:1:7779)
    at PostResolveToken.postProcess (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/core/lib/util.js:1:1653)
    at Object.postProcess (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:1205)
    at DefaultTokenResolver.resolveToken (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/core/lib/resolvable.js:1:1483)
    at resolve (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2711)
    at Object.resolve (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:1079)
    at resolve (<PROJECT_ROOT>/node_modules/.pnpm/aws-cdk-lib@2.114.1_constructs@10.3.0/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2990) {
  type: 'CfnSynthesisError'
}

If I wrap the string literal in new String(...), the stack synthesizes, but does so incorrectly. It renders the context as an indexed dictionary of the string array which fails to create the resource with the error Resource handler returned message: "Invalid request provided: Missing "schemaVersion" in the document.:

Resources:
  TestSsmDoc:
    Type: AWS::SSM::Document
    Properties:
      Content:
        "0": "-"
        "1": "-"
        "2": "-"
        "3": "\n"
        "4": s
        "5": c
        "6": h
        "7": e
        "8": m
        "9": a
        "10": V
        "11": e
        "12": r
        "13": s
        "14": i
        "15": o
        "16": "n"
        "17": ":"
        "18": " "
        "19": '"'
        "20": "2"
        "21": "."
        "22": "2"
<snip>

Reproduction Steps

Create a stack with the following stack and attempt to synthesize the stack:

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as ssm from "aws-cdk-lib/aws-ssm";

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

    const ssmDoc = new ssm.CfnDocument(this, "TestSsmDoc", {
      content: `---
schemaVersion: "2.2"
description: "Test SSM doc for CDK."
mainSteps:
  - action: "aws:runShellScript"
    name: "test"
    inputs:
      runCommand: |
        echo "Hello, World!"
`,
      name: "TestSsmDocument",
      documentType: "Command",
      documentFormat: "YAML",
    });
  }
}

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.114.1 (build 02bbb1d)

Framework Version

No response

Node.js Version

v18.16.1

OS

Mac OS X

Language

TypeScript

Language Version

Typescript (5.2.2)

Other information

No response

pahud commented 6 months ago

This is a little bit tricky but I was able to synthesize with this:

    const content = yaml.parse(fs.readFileSync(path.join(__dirname, './demo.yaml')).toString());
    new ssm.CfnDocument(this, "TestSsmDoc", {
      content,
      name: "TestSsmDocument",
      documentType: "Command",
      documentFormat: "YAML",
    });

And I got this

  TestSsmDoc:
    Type: AWS::SSM::Document
    Properties:
      Content:
        schemaVersion: "2.2"
        description: Test SSM doc for CDK.
        mainSteps:
          - action: aws:runShellScript
            name: test
            inputs:
              runCommand: |
                echo "Hello, World!"
      DocumentFormat: YAML
      DocumentType: Command
      Name: TestSsmDocument

Let me know if it works for you.

chrisrtaylor commented 6 months ago

Yes, wrapping the string content with the yaml library does work and is an okay work around. However, the documentation linked above indicates that the content property does accept the String type which is still a problem.