AWS CloudFormation:使用 resolve 具有动态引用的嵌套子会导致错误并且不执行解析以从 Parameter Store 获取值

Posted

技术标签:

【中文标题】AWS CloudFormation:使用 resolve 具有动态引用的嵌套子会导致错误并且不执行解析以从 Parameter Store 获取值【英文标题】:AWS CloudFormation: Nested Sub with Dynamic References using resolve causes error and doesn't execute resolve to get value from Parameter StoreAWS CloudFormation:使用 resolve 具有动态引用的嵌套子会导致错误并且不执行解析以从 Parameter Store 获取值 【发布时间】:2019-12-28 06:05:12 【问题描述】:

我正在尝试使用 AWS CloudFormation 模板创建一个 EC2 实例,其中包含使用模板中的动态引用和跨堆栈引用生成的一些用户数据。 AWS Systems Manager Parameter Store 中有一个参数存储在Name:/MyCustomParameterValue:Test1

这个想法是将参数传递给模板堆栈(堆栈A),该堆栈引用另一个云形成堆栈(堆栈B)。堆栈 B 使用引用“StackB::ParameterStoreName”导出一个变量。堆栈 A 使用 Fn::ImportValue: 'StackB::ParameterStoreName' 获取它的值,以便它可以与动态引用方法一起使用,以使用 resolve:ssm:/MyCustomParameter:1 从 AWS SSM Parameter Store 获取它的值,并将其值传递给模板中的 UserData 字段。在此用例中尝试使用嵌套的 Fn::Sub: 函数时,我遇到了困难。

我尝试删除 | 管道并使用带有转义换行符的双引号,但这不起作用。

我还尝试使用不同类型的资源,它的属性在哪里起作用。下面是一个有效的代码示例。

Resources:
  TestBucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: 
        Fn::Sub:
          - '$SSMParameterValue-12345'
          - SSMParameterValue: 
              Fn::Sub:
                - 'resolve:ssm:$SSMParameterName:1'
                - SSMParameterName: 
                    Fn::ImportValue:
                      !Sub '$CustomStack::ParameterStoreName'

以下是我当前代码的摘录:

Parameters:
  CustomStack:
    Type: "String"
    Default: "StackB"
Resources:
  MyCustomInstance:  
    Type: 'AWS::EC2::Instance'
        Properties:
        UserData:
            Fn::Base64:
            Fn::Sub:
                - |
                #!/bin/bash -e 
                #
                # Bootstrap and join the cluster 
                /etc/eks/bootstrap.sh --b64-cluster-ca '$SSMParameterValue' --apiserver-endpoint '$Endpoint' '$ClusterName'"
                - SSMParameterValue:
                    Fn::Sub:
                    - 'resolve:ssm:/$SSMParameterName:1'
                    - SSMParameterName: 
                        Fn::ImportValue:
                            !Sub '$CustomStack::ParameterStoreName'
                Endpoint:
                    Fn::ImportValue:
                    !Sub '$CustomStack::Endpoint'
                ClusterName: 
                    Fn::ImportValue:
                    !Sub '$CustomStack::ClusterStackName'

电流输出:

#!/bin/bash -e 
# 
# Bootstrap and join the cluster 
/etc/eks/bootstrap.sh --b64-cluster-ca `resolve:ssm:MyCustomParameter:1` --apiserver-endpoint 'https://04F1597P0HJ11FQ54K0YFM9P19.gr7.us-east-1.eks.amazonaws.com' 'eks-cluster-1'

预期输出:

#!/bin/bash -e 
# 
# Bootstrap and join the cluster 
/etc/eks/bootstrap.sh --b64-cluster-ca `Test1` --apiserver-endpoint 'https://04F1597P0HJ11FQ54K0YFM9P19.gr7.us-east-1.eks.amazonaws.com' 'eks-cluster-1'

【问题讨论】:

【参考方案1】:

我认为这是因为解析是在 base64 中,也许......?当它处理该行时,它只会看到一个 base64 块,而不是 resolve... 代码。 “解析”在 !Function 之后处理,因为在代码运行之前无法解析它们。

为了解决这个问题,我添加了一个临时 SSM 参数:

eksCAtmp:
  Type: "AWS::SSM::Parameter"
  Properties:
    Type: String
    Value:
      Fn::Join:
        - ''
        - - 'resolve:ssm:'
          - Fn::ImportValue:
              !Sub "$ClusterName-EksCA"
          - ':1'

这会导入原始的 SSM 参数并摆脱“导入”并再次解析它的要求。所以现在你可以使用!GetAtt eksCAtemp.Value

例如:

  UserData: !Base64
    "Fn::Sub":
      - |
        #!/bin/bash
        set -o xtrace
        /etc/eks/bootstrap.sh $ClusterName --b64-cluster-ca $CA  --apiserver-endpoint $endpoint --kubelet-extra-args '--read-only-port=10255'
        /opt/aws/bin/cfn-signal --exit-code $? \
                 --stack  $AWS::StackName \
                 --resource NodeGroup  \
                 --region $AWS::Region
      - endpoint:
          Fn::ImportValue:
            !Sub "$ClusterName-EksEndpoint"
        CA: !GetAtt eksCAtmp.Value

(当然,如果他们允许跨堆栈导出超过 1024 个字符,我们就不需要在专用网络上启动 EKS。)

【讨论】:

【参考方案2】:

你可以这样写:

    UserData:
        Fn::Base64:
        Fn::Sub:
            - |
            #!/bin/bash -e 
            #
            # Bootstrap and join the cluster 
            export SSMParameterValue=$(aws --region $AWS::Region ssm get-parameters --names $SSMParameterName --query 'Parameters[0].Value' --output text)
            /etc/eks/bootstrap.sh --b64-cluster-ca \`$SSMParameterValue\` --apiserver-endpoint '$Endpoint' '$ClusterName'"
            - SSMParameterName: 
                Fn::ImportValue:
                    !Sub '$CustomStack::ParameterStoreName'
            Endpoint:
                Fn::ImportValue:
                !Sub '$CustomStack::Endpoint'

不要忘记您的 EC2 角色需要 ssm:GetParameters 权限。

【讨论】:

以上是关于AWS CloudFormation:使用 resolve 具有动态引用的嵌套子会导致错误并且不执行解析以从 Parameter Store 获取值的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法使用 cloudformation 修改 AWS 现有资源?

通过 cloudformation 使用 aws `cdk synth` 输出

使用 AWS CLI 将现有资源导入 CloudFormation

是否可以在 AWS::OpsWorks::Instance 资源中使用 AWS::CloudFormation::Init 和元数据?

使用 AWS CloudFormation 添加环境变量会重置 AWS Beanstalk 应用程序

如何修复与 AWS::CloudFormation::Init 一起创建 EC2 的 cloudformation 模板