简体   繁体   中英

Cloudformation nested stack template ValidationError for child-to-child parameter

I've made a nested cloudformation parent template that then references 4 child templates. When I try to launch the stack via the CLI command aws cloudformation create-stack... I get the error:

An error occurred (ValidationError) when calling the CreateStack 
operation: Template error: instance of Fn::GetAtt references undefined 
resource BatchScatterGatherSubmissionActivity

This is because I have a child template called StepFunctionResourcesStack that contains the BatchScatterGatherSubmissionActivity and then another child template EC2InstanceResourcesStack that references it. I made sure to add a DependsOn clause for the latter child template, but I still get the error.

Here is the StepFunctionResourcesStack :


AWSTemplateFormatVersion: '2010-09-09'
Description: step functions resources stack.
Parameters:
  StackUID:
    Type: String 

Resources:
  StatesExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - !Sub states.${AWS::Region}.amazonaws.com
            Action: "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: StatesExecutionPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "lambda:InvokeFunction"
                Resource: "*"

  MyStateMachine:
    Type: "AWS::StepFunctions::StateMachine"
    Properties:
      #
      # The StateMachine definition is substituted in with Jinja2
      #
      DefinitionString: !Sub
        - |
          {{ machine_json | indent(10) }}
        - {% for item in machine_args -%}
          {{ item }}
          {% endfor %}

      RoleArn: !GetAtt [ StatesExecutionRole, Arn ]

  # several of these:

  BatchScatterGatherSubmissionActivity:
    Type: "AWS::StepFunctions::Activity"
    Properties:
      Name:
        Fn::Join: [ "-", [ "BatchScatterGatherSubmissionActivity", Ref: StackUID] ]

  BatchScatterGatherPollingActivity:
    Type: "AWS::StepFunctions::Activity"
    Properties:
      Name:
        Fn::Join: [ "-", [ "BatchScatterGatherPollingActivity", Ref: StackUID] ]

  BatchGatherActivity:
    Type: "AWS::StepFunctions::Activity"
    Properties:
      Name:
        Fn::Join: [ "-", [ "BatchGatherActivity", Ref: StackUID] ]

  BatchTriodenovoActivity:
    Type: "AWS::StepFunctions::Activity"
    Properties:
      Name:
        Fn::Join: [ "-", [ "BatchTriodenovoActivity", Ref: StackUID] ]

  BatchHandoffActivity:
    Type: "AWS::StepFunctions::Activity"
    Properties:
      Name:
        Fn::Join: [ "-", [ "BatchHandoffActivity", Ref: StackUID] ]    

Outputs:
  # These get used in the instance_resources child stack.
  BatchScatterGatherSubmissionActivity:
    Value: !Ref BatchScatterGatherSubmissionActivity
  BatchScatterGatherPollingActivity:
    Value: !Ref BatchScatterGatherPollingActivity
  BatchGatherActivity:
    Value: !Ref BatchGatherActivity
  BatchTriodenovoActivity:
    Value: !Ref BatchTriodenovoActivity
  BatchHandoffActivity:
    Value: !Ref BatchHandoffActivity

And here is the relevant part of the parent (nested) template where the above outputs are passed into the EC2InstanceResourcesStack:

  StepFunctionResourcesStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      Parameters:
        StackUID:
          Ref: StackUID
      TemplateURL: https://s3.amazonaws.com/CFNTemplate/step_functions_resources.stack.yaml
      Timeout: "100"  


  EC2InstanceResourcesStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      Parameters:
        BatchScatterGatherSubmissionActivity: 
          Fn::GetAtt: [ "BatchScatterGatherSubmissionActivity", "Outputs.StepFunctionResourcesStack" ]
        BatchScatterGatherPollingActivity: 
          Fn::GetAtt: [ "BatchScatterGatherPollingActivity", "Outputs.StepFunctionResourcesStack" ]
        BatchGatherActivity: 
          Fn::GetAtt: [ "BatchGatherActivity", "Outputs.StepFunctionResourcesStack" ]
        BatchTriodenovoActivity: 
          Fn::GetAtt: [ "BatchTriodenovoActivity", "Outputs.StepFunctionResourcesStack" ]
        BatchHandoffActivity: 
          Fn::GetAtt: [ "BatchHandoffActivity", "Outputs.StepFunctionResourcesStack" ]
        Subnet: !Ref Subnet
        GPCESSHKeyPair: !Ref GPCESSHKeyPair
        GPCESubnetAZ1: !Ref GPCESubnetAZ1
        ActivityAndHandoffAnsibleBucketName: !Ref ActivityAndHandoffAnsibleBucketName
        ActivityAndHandoffAnsibleKeyName: !Ref ActivityAndHandoffAnsibleKeyName
        ActivityAndHandoffDaemonBucketName: !Ref ActivityAndHandoffDaemonBucketName
        ActivityAndHandoffDaemonKeyName: !Ref ActivityAndHandoffDaemonKeyName
        ActivityAndHandoffDaemonRequirementsBucketName: !Ref ActivityAndHandoffDaemonRequirementsBucketName
        ActivityAndHandoffDaemonRequirementsKeyName: !Ref ActivityAndHandoffDaemonRequirementsKeyName
        Rkstr8PkgBucketName: !Ref Rkstr8PkgBucketName
        Rkstr8PkgKeyName: !Ref Rkstr8PkgKeyName
      TemplateURL: https://s3.amazonaws.com/CFNTemplate/instance_resources.stack.yaml
      Timeout: "100"
    DependsOn: StepFunctionResourcesStack

For my method of exporting the parameters from the child and passing them to another via the parent template, I followed the approach provided in the answer here: AWS CloudFormation: Passing Values between Nested Stacks

You cannot directly reference resources (via Ref ) from one template in another template, even when they are parent-child or sibling templates. The resources are stack specific unless they are explicitly exported via the Output section.

So you have 2 options:

Option 1: Export the value(s) you care about for BatchScatterGatherSubmissionActivity from the child template 1 via the Output section, then import those values in child template 2.

For example:

In the "source" template:

"Outputs" : {
  "MyValue" : {
    "Value" : {
      "Ref" : "BatchScatterGatherSubmissionActivity"
    },
    "Export" : {
      "Name" : "MyExportedValue"
    }
  }
}

then import the value in your "consuming" template:

{ "Fn::ImportValue" : "MyExportedValue" }

More information: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html

One drawback is that you cannot "DependsOn". Also, quite literally child template 2 depends on child template 1.

Option 2: Output the value(s) you care about for BatchScatterGatherSubmissionActivity out from the child template (ie. up to the parent), then pass those values from the parent down to the other child.

So in child template 1, you would output the value from the child stack:

"Outputs" : {
  "MyValue" : {
    "Value" : {
      "Ref" : "BatchScatterGatherSubmissionActivity"
    }
  }
}

In child template 2, you would add a parameter to the template via the "Parameters" section, and then reference that parameter.

"Parameters" : {
  "MyParam" : {
    "Type" : "String",
    "Description" : "The value from elsewhere"
  }
}

then

{ "Ref" : "MyParam" }

Finally, hook the 2 child stacks together in the parent by passing the output of child template 1 into the parameter of child template 2:

"ChildStack2": {
  "Type" : "AWS::CloudFormation::Stack",
  "Properties" : {
    "Parameters" : {
      "MyParam" : { "Fn::GetAtt" : "Outputs.MyValue" }
    }
  }
}

In this case, child stack 2 won't be created until child stack 1 is ready and outputs it's values, so there's an implied "DependsOn". But in this case, child template 2 does not depend on child template 1. Instead, it depends on anything that fulfills it's input parameter (which can be a parent stack, or something else).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM