aws-cloudformation / cfn-language-discussion

Language discussions for CloudFormation template language
https://aws.amazon.com/cloudformation/
Apache License 2.0
143 stars 13 forks source link

resolving parameter values via Fn::Sub inside of Fn::ForEach is throwing an error #136

Open rhbecker opened 1 year ago

rhbecker commented 1 year ago

Community Note

Tell us about the bug

I'm trying out the new Fn::ForEach capability to create a set of nested stacks (i.e. Type: AWS::CloudFormation::Stack).

I'm running into an odd issue when using Fn::Sub to define the TemplateURL property value. The Fn::Sub has no problem resolving a pseudo parameter (AWS::URLSuffix), along with a value imported from another stack via Fn::ImportValue, but it does not seem able to resolve parameters defined within the template. If I replace the internally defined parameters with hard-coded equivalents, a stack creation succeeds.

The disparity in behavior between the 2 template versions is specific to usage of Fn::ForEach. When not using Fn::ForEach, both approaches work fine.

Expected behavior

I expect both versions of the below template to work.

Observed behavior

The former works, while the latter fails with the error ...

Failed to create the changeset: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: Template format error: Unresolved resource dependencies [path, version] in the Resources block of the template

Test cases

An aws cloudformation deploy of the following template succeeds ...

---
AWSTemplateFormatVersion: 2010-09-09
Transform:
  - AWS::LanguageExtensions
Description: test new Fn::Foreach capability (composite)

Parameters:

  StackNameTemplates:
    Type: String
    Default: build-artifacts

  TopicNames:
    Type: CommaDelimitedList
    Default: TestTopic1,TestTopic2

Resources:

  Fn::ForEach::Topics:
    - TopicName
    - !Ref TopicNames
    - SnsTopic${TopicName}:
        Type: AWS::CloudFormation::Stack
        DeletionPolicy: Delete
        UpdateReplacePolicy: Delete
        Properties:
          TemplateURL: !Sub
            - https://s3.${urlsuffix}/${bucketname}/cloudformation-component-sns-topic/3b8fb142918369d46e4344a212f17b4f50ca15c9/template.yaml
            - bucketname:
                Fn::ImportValue: !Sub ${StackNameTemplates}:bucket:name
              urlsuffix: !Ref AWS::URLSuffix

In the below, I enhance the above by parameterizing 2 strings in the TemplateURL value (ComponentSnsTopicPath and ComponentSnsTopicVersion). You'll see that the Default value for each new parameter is equivalent to what was previously hard-coded into the TemplateURL value.

An aws cloudformation deploy of the following template fails ...

---
AWSTemplateFormatVersion: 2010-09-09
Transform:
  - AWS::LanguageExtensions
Description: test new Fn::Foreach capability (composite)

Parameters:

  ComponentSnsTopicPath:
    Type: String
    Default: cloudformation-component-sns-topic

  ComponentSnsTopicVersion:
    Type: String
    Default: 3b8fb142918369d46e4344a212f17b4f50ca15c9

  StackNameTemplates:
    Type: String
    Default: build-artifacts

  TopicNames:
    Type: CommaDelimitedList
    Default: TestTopic1,TestTopic2

Resources:

  Fn::ForEach::Topics:
    - TopicName
    - !Ref TopicNames
    - SnsTopic${TopicName}:
        Type: AWS::CloudFormation::Stack
        DeletionPolicy: Delete
        UpdateReplacePolicy: Delete
        Properties:
          TemplateURL: !Sub
            - https://s3.${urlsuffix}/${bucketname}/${path}/${version}/template.yaml
            - bucketname:
                Fn::ImportValue: !Sub ${StackNameTemplates}:bucket:name
              path: !Ref ComponentSnsTopicPath
              urlsuffix: !Ref AWS::URLSuffix
              version: !Ref ComponentSnsTopicVersion

Additional context

n/a