I would like to convert this working CloudFormation Code into a proper Python AWS CDK version. The EC2 shall be launched within a VPC. The user data are used to install apps. After completion, I need a call back to Cloudformation.
UserData:
Fn::Base64: !Sub |
<script>
cfn-signal.exe --exit-code 0 --stack ${AWS::StackId} --resource EC2Instance --region ${AWS::Region}
</script>
I tried to use the direct way with aws_cdk.core.Fn.base64
which is not working with regard to pseudo-parameters declaration within the EC2 the user data.
This is my current state:
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
)
Pros:
core.Aws
object such as Region or Account ID.Cons:
user_data.sh
script with $!{
rather than just ${
.Use a mappings dict and parse it into the Fn.sub function. Personally I like these declared at the top of my user_data.sh
script rather than substituted throughout so use a double underscore as a prefix and as a suffix. Note that you still need to consider the mappings as variables rather than strings.
ie
$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/"
...
In my stack declaration I then place the following dict:
mappings = {"__ACCOUNT_ID__": self.account,
"__REGION__": self.region}
And read the user_data.sh
into the sub function, with the mappings dict as the second parameter
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)
Then use the custom attribute from the UserData module.
# Import substitution object into user_data set
user_data = ec2.UserData.custom(user_data_sub)
Pros:
Cons:
core.Aws
object. Such as AccountID and Region.You can run print statements inside the cdk workflow to assist you in determining what the variables such as core.Aws.ACCOUNT_ID
and core.Aws.REGION
are evaluated to and use these inside the user_data script. (I'm writing my deployment in python and have based it from the ec2 on an existing VPC from the aws official examples repo .
ie:
host = ec2.Instance(...)
print(core.aws.ACCOUNT_ID)
print(core.Aws.REGION)
I then run cdk synth
which yields:
${Token[AWS::AccountId.0]}
${Token[AWS::Region.4]}
Resources:...
From here I can use these in my user_data script: ie
#!/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
Note now when re-running cdk synth
these are recognised as special by the yaml constructor (The yaml double spacing is a known cdk bug):
...
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/"
...
I found the following to work with 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)
This is using the CfnInstance
object you get from host.node.default_child
You can access those pseudo-params by using the core module:
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 ...
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.