Skip to content

(cloudformation-include): Cannot use computed outputs from nested stack #27233

@ozeebee

Description

@ozeebee

Describe the bug

Hi,
I must integrate some pre-built components (provided as CFN templates) in my app as nested stacks.
When I try to use computed outputs (whose value use an intrinsic function for instance) exposed by the nested stack in my CDK stack, deployment fails with message:

❌ Deployment failed: Error [ValidationError]: Template error: every Fn::Join object requires two parameters, (1) a string delimiter and (2) a list of strings to be joined or a function that returns a list of strings (such as Fn::GetAZs) to be joined.

It looks like the generated CFN is incorrect.
This is confirmed by running cloudformation validate-template with the CLI.

Expected Behavior

To be able to use nested stack's outputs in my CDK constructs.

Current Behavior

Invalid CFN template generates an error during deployment:

❌ Deployment failed: Error [ValidationError]: Template error: every Fn::Join object requires two parameters, (1) a string delimiter and (2) a list of strings to be joined or a function that returns a list of strings (such as Fn::GetAZs) to be joined.

Reproduction Steps

For instance, I have a CFN template creating some VPC Endpoint, exposing outputs like this:

  "Outputs": {
    "SimpleOutput": {
      "Description": "Simple Output",
      "Value": {
        "Ref": "VpcEndpoint"
      }
    },
    "ComputedOutput": {
      "Description": "Computed Output",
      "Value": {
        "Fn::Join": [
          ",",
          {
            "Fn::GetAtt": "VpcEndpoint.DnsEntries"
          }
        ]
      }
    }
  }

Now consider this stack:

interface MyNestedStackProps extends NestedStackProps {
  VpcId: string,
  SubnetIDs: string[]
}

class MyNestedStack extends NestedStack {
  readonly cfnInc: cfninc.CfnInclude
  constructor(scope: Construct, id: string, props: MyNestedStackProps) {
    super(scope, id)
    // Include CFN template
    this.cfnInc = new cfninc.CfnInclude(this, 'CfnTemplate', {
      templateFile: 'cfn-template.json',
      parameters: {
        'VpcId': props.VpcId,
        'SubnetIDs': props.SubnetIDs
      }
    })
  }
}

export interface CdkbugreproducerStackProps extends StackProps {
  vpcName: string
}

export class CdkbugreproducerStack extends Stack {
  constructor(scope: Construct, id: string, props: CdkbugreproducerStackProps) {
    super(scope, id, props)

    const vpc = Vpc.fromLookup(this, 'MyVPC', { vpcName: 'My-VPC' })
    const privateSubnetIds = vpc.privateSubnets.map(_ => _.subnetId)

    // Include CFN template as a NESTED STACK in the stack
    const nested = new MyNestedStack(this, 'MyNestedStack', {
      VpcId: vpc.vpcId,
      SubnetIDs: privateSubnetIds
    })
    const simpleOutput = nested.cfnInc.getOutput('SimpleOutput').value
    const computedOutput = nested.cfnInc.getOutput('ComputedOutput').value

    new ssm.StringParameter(this, 'MyParamSimpleOutput', {
      parameterName: 'MyParamFromStackSimpleOutput',
      stringValue: simpleOutput
    })
    new ssm.StringParameter(this, 'MyParamComputedOutput', {
      parameterName: 'MyParamFromStackComputedOutput',
      stringValue: computedOutput
    })
  }
}

So, in this simple example, I just want to get the DNS entries of the VPC Endpoint (created in the nested stack) and create an SSM parameter with its value, which fails.
In practice, we have similar error with other stacks exposing outputs with Fn::Sub, like for instance in:

"DomainEndpoint": {
    "Description": "ElasticSearch Service domain endpoint",
    "Value": {
        "Fn::Sub": "https://${ElasticSearch.DomainEndpoint}"
    }
},

Possible Solution

No idea. I don't even have work-around for this.

Additional Information/Context

No response

CDK CLI Version

2.96.2 (build 3edd240)

Framework Version

2.96.2

Node.js Version

v18.12.0

OS

macos

Language

Typescript

Language Version

3.9.10

Other information

I have a full reproducer project available in case it can help (can push it to GitHub or GitLab if needed).

Some observations after testing various things:

  • This error does not happen when including the CFN template directly into the CDK stack, but I must add them as nested stacks (for compliance reasons)
  • In the code above, If I comment the SSM parameter for "ComputedOutput", it works.

Here's the output of the synthesis for this "ComputedOutput" SSM parameter:

...
  "MyParamComputedOutput8C461DF5": {
   "Type": "AWS::SSM::Parameter",
   "Properties": {
    "Name": "MyParamFromStackComputedOutput",
    "Type": "String",
    "Value": {
     "Fn::Join": [
      ",",
      {
       "Fn::GetAtt": [
        "MyNestedStackNestedStackMyNestedStackNestedStackResource9C617903",
        "Outputs.cdkbugreproducercfMyNestedStackCfnTemplateVpcEndpointC42D0C11DnsEntries"
       ]
      }
     ]
    }
   },
...

And the generated CF for the outputs in the included nested stack looks like this:

 "Outputs": {
  "SimpleOutput": {
   "Description": "Simple Output",
   "Value": {
    "Ref": "VpcEndpoint"
   }
  },
  "ComputedOutput": {
   "Description": "Computed Output",
   "Value": {
    "Fn::Join": [
     ",",
     {
      "Fn::GetAtt": "VpcEndpoint.DnsEntries"
     }
    ]
   }
  },
  "cdkbugreproducercfMyNestedStackCfnTemplateVpcEndpointC42D0C11Ref": {
   "Value": {
    "Ref": "VpcEndpoint"
   }
  },
  "cdkbugreproducercfMyNestedStackCfnTemplateVpcEndpointC42D0C11DnsEntries": {
   "Value": {
    "Fn::GetAtt": "VpcEndpoint.DnsEntries"
   }
  }
 }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions