如何使用AWS CDK获取伪参数用户数据?

时间:2019-09-02 07:37:21

标签: python python-3.x aws-cdk

我想将此工作CloudFormation代码转换为正确的Python AWS CDK版本。 EC2必须在VPC中启动。用户数据用于安装应用程序。完成后,我需要回叫Cloudformation。

UserData:
        Fn::Base64: !Sub |
          <script>
          cfn-signal.exe --exit-code 0 --stack ${AWS::StackId} --resource EC2Instance --region ${AWS::Region}
          </script>

我尝试对aws_cdk.core.Fn.base64使用直接方式,该方式对于EC2用户数据中的伪参数声明不起作用。

这是我当前的状态:

EC2InstanceUserData = aws_ec2.UserData.for_windows()
EC2InstanceUserData.add_commands(
   "cfn-signal.exe --exit-code 0 ",
   "--stack ",
   VpcStack.stack_id(XXX, e.g. self?), # not working
   " --resource ",
   VpcStack.get_logical_id(XXX, e.g. self?), # not working
   " --region ",
   VpcStack.region(XXX, e.g. self?) # not working
)

3 个答案:

答案 0 :(得分:1)

方法1:

优点:

  • 可以接受任意数量的变量,例如上下文中定义的变量,而不仅仅是core.Aws对象中的变量,例如Region或Account ID。

缺点:

  • 您需要在对user_data.sh脚本中所有常规变量的引用前加上$!{,而不仅仅是${

步骤

使用映射字典并将其解析到Fn.sub函数中。我个人比较喜欢在user_data.sh脚本的顶部声明的内容,而不是在整个脚本中都替换的内容,因此请使用双下划线作为前缀和后缀。请注意,您仍然需要将映射视为变量而不是字符串。

$cat user_data.sh
ACCOUNT_ID="${__ACCOUNT_ID__}"
REGION="${__REGION__}"

## Updates
yum update -y

## Fix time
ln -sf /usr/share/zoneinfo/Australia/Melbourne /etc/localtime

## ECR Repo
ECR_REPO="${!ACCOUNT_ID}.dkr.ecr.${!REGION}.amazonaws.com/"
...

然后在我的堆栈声明中放置以下字典:

mappings = {"__ACCOUNT_ID__": self.account,
            "__REGION__": self.region}

然后将user_data.sh读入子函数,将dict映射作为第二个参数

with open("user_data/user_data.sh", 'r') as user_data_h:
    # Use a substitution
    user_data_sub = core.Fn.sub(user_data_h.read(), mappings)

然后使用UserData模块中的custom attribute

# Import substitution object into user_data set
user_data = ec2.UserData.custom(user_data_sub)

方法2

优点:

  • 无需更改bash语法

缺点:

  • 令牌变量很难读取,并且仅限于core.Aws对象的属性。例如AccountID和Region。

步骤

您可以在cdk工作流程中运行打印语句,以帮助您确定对变量core.Aws.ACCOUNT_IDcore.Aws.REGION进行评估,并在user_data脚本中使用它们。 (我正在用python编写部署,并已将ec2的部署基于aws official examples repo上的现有VPC。

即:

host = ec2.Instance(...)
print(core.aws.ACCOUNT_ID)
print(core.Aws.REGION)

然后我运行cdk synth,其结果是:

${Token[AWS::AccountId.0]}
${Token[AWS::Region.4]}
Resources:...

在这里,我可以在我的user_data脚本中使用它们:即

#!/bin/bash
ACCOUNT_ID="${Token[AWS::AccountId.0]}"
REGION="${Token[AWS::Region.4]}"

## Updates
yum update -y

## Fix time
ln -sf /usr/share/zoneinfo/Australia/Melbourne /etc/localtime

请注意,当重新运行cdk synth时,yaml构造函数将其识别为特殊字符(yaml双倍间距是一个已知的cdk错误):

...
UserData:
        Fn::Base64:
          Fn::Join:
            - ""
            - - >-
                #!/bin/bash


                # AWS vars:

                ACCOUNT_ID="
              - Ref: AWS::AccountId
              - >-
                "

                REGION="
              - Ref: AWS::Region
              - >-
                "

                ## Updates
                yum update -y

                ## Fix time
                ln -sf /usr/share/zoneinfo/Australia/Melbourne /etc/localtime

                ## ECR Repo
                EC2_REPO="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/"
...

答案 1 :(得分:0)

我发现以下可与python一起使用

from aws_cdk import (
  aws_ec2,
  core
)

host = aws_ec2.Instance(...)

host.add_user_data('', join([
  'yum install -y aws-cfn-bootstrap\n',
  f'/opt/aws/bin/cfn-init -v -s {core.Aws.STACK_NAME} -r {host.node.default_child.logical_id}\n'
])

meta_data = {
  'config': {
    'packages': {...},
    'files': {...},
     ...
    }
}

# for adding the meta data in a way that gets synth
host.node.default_child.add_overide('Metadata.AWS::CloudFormation::Init', meta_data)

这使用了从CfnInstance获得的host.node.default_child对象

答案 2 :(得分:-1)

您可以使用核心模块访问这些伪参数:

from aws_cdk import core

# other code...

EC2InstanceUserData = aws_ec2.UserData.for_windows()
EC2InstanceUserData.add_commands(
   "cfn-signal.exe --exit-code 0 ",
   f"--stack {core.Aws.STACK_ID}",
   f" --resource {EC2Instance}",  # Without more context, I'm not sure if this is exactly what you're wanting
   f" --region {core.Aws.REGION}",
)

# other code ...