参考资料
Running bash commands in AWS CloudFormation templates
如何使用 AWSUtility::CloudFormation::CommandRunner 在 CloudFormation 堆栈中的资源之前或之后运行命令?
由于cloudformation语法和资源的限制,有些场景下我们可能会希望执行一些自定义逻辑。实际上cloudformation已经提供了自定义资源和宏实现这个目的。而CommandRunner
则从另一个角度解决此问题。
commandrunner本质上就是启动额外的ec2来执行指定的逻辑,和自定义资源的lambda类似的思路,只是资源类型不同
安装commandrunner
git clone https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-awsutilities-commandrunner.git
cd aws-cloudformation-resource-providers-awsutilities-commandrunner
curl -LO https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-awsutilities-commandrunner/releases/latest/download/awsutility-cloudformation-commandrunner.zip
./scripts/register.sh --set-default
具体的执行过程如下,核心具体就是将awsutility-cloudformation-commandrunner.zip
注册到cfn中,需要临时s3桶存储该zip文件
Creating Execution Role...
Waiting for execution role stack to complete...
Waiting for execution role stack to complete...
Creating/Updating Execution Role complete.
Creating temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d...
Creating temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d complete.
Configuring S3 Bucket Policy for temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d...
An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: Policy has invalid resource
Configuring S3 Bucket Policy for temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d complete.
Copying Schema Handler Package to temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d...
Copying Schema Handler Package to temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d complete.
Creating CommandRunner Log Group called awsutility-cloudformation-commandrunner-logs2...
Creating CommandRunner Log Group complete.
Registering AWSUtility::CloudFormation::CommandRunner to AWS CloudFormation...
RegistrationToken: 5a22a5d4-723e-4d2a-b20d-eff95352830c
Waiting for registration to complete...
Waiting for registration to complete...
Waiting for registration to complete...
Waiting for registration to complete...
Registering AWSUtility::CloudFormation::CommandRunner to AWS CloudFormation complete.
Setting current version as default...
Setting current version as default complete. (Current Version is 00000001)
Cleaning up temporary S3 Bucket...
Deleting SchemaHandlerPackage from temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d...
Deleting SchemaHandlerPackage from temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d complete.
Cleaning up temporary S3 Bucket complete.
以上命令会使用默认的awscli凭证在账户中创建cfn资源
之后可以在cfn模板中直接使用,这里创建简单的s3资源
Resources:S3Bucket:Type: AWS::S3::BucketProperties:VersioningConfiguration:Status: SuspendedBucketEncryption:ServerSideEncryptionConfiguration:- ServerSideEncryptionByDefault:SSEAlgorithm: AES256CommandRunner1:DependsOn: S3BucketType: AWSUtility::CloudFormation::CommandRunnerProperties:Command: echo justfortest > /command-output.txtRole: MyEc2AdministratorAccessSubnetId: subnet-0270xxxxxxxdCommandRunner2:Type: AWSUtility::CloudFormation::CommandRunnerProperties:Role: MyEc2AdministratorAccessCommand: echo helloworld > /command-output.txtSubnetId: subnet-02xxxxxxxdOutputs:Output1:Description: The output of the CommandRunner.Value: !GetAtt CommandRunner1.OutputOutput2:Description: The output of the CommandRunner.Value: !GetAtt CommandRunner2.Output
由于创建了两个commandrunner资源,因此会创建额外的两个堆栈
启动两台ec2示例,默认为t2.medium
以下是AWSUtility-CloudFormation-CommandRunner
资源的模板截取,逻辑较为简单,只需要关注userdata部分,相关的概念的命令在之前的文章中都提到过,不赘述
command-output.txt
文件中的内容Parameters:
...略Command:Type: StringDefault: >-yum install jq -y && aws ssm get-parameter --name RepositoryName --regionus-east-1 | jq -r .Parameter.Value > /commandrunner-output.txtLogGroup:Type: StringDefault: cloudformation-commandrunner-log-group
Resources:SecurityGroup:Condition: CreateSecurityGroupType: AWS::EC2::SecurityGroupEC2Instance:Type: AWS::EC2::InstanceMetadata:AWS::CloudFormation::Init:config:packages:yum:awslogs: []files:/etc/awslogs/awslogs.conf:content:Fn::Sub: |[general]state_file= /var/awslogs/state/agent-state[/var/log/cloud-init.log]file = /var/log/cloud-init.log\nlog_group_name = ${LogGroup}log_stream_name = {instance_id}/cloud-init.log... 略/etc/awslogs/awscli.conf:content:Fn::Sub: |[plugins]cwlogs = cwlogs[default]region = ${AWS::Region}commands:01_create_state_directory:command: mkdir -p /var/awslogs/stateservices:sysvinit:awslogsd:enabled: trueensureRunning: truefiles:- /etc/awslogs/awslogs.confProperties:UserData:Fn::Base64:Fn::Sub: >-#!/bin/bashyum install -y aws-cfn-bootstrap/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resourceEC2Instance --region ${AWS::Region}${Command}/opt/aws/bin/cfn-signal -r 'Command ran successfully.' -e 0 --id'Command Output' --data "$(cat /command-output.txt)"'${WaitConditionHandle}'echo Contents of /command-output.txt = $(cat /command-output.txt)WaitConditionHandle:Type: AWS::CloudFormation::WaitConditionHandleWaitCondition:Type: AWS::CloudFormation::WaitConditionProperties:Count: 1Handle:Ref: WaitConditionHandleTimeout:Ref: Timeout
Outputs:Result:Description: The output of the commandrunner.Value:Fn::Select:- 3- Fn::Split:- '"'- Fn::GetAtt:- WaitCondition- Data
最终能够获取到output
总结commandrunner的特点如下
(1)waitcondition超时
可能是由于ec2没有权限,ec2网络配置有误,在syslog中能看到相关信息
(2)输出结果无效,cli命令要确保写入到文件中
Resource handler returned message: "Either the command failed to execute, the value written to /command-output.txt was invalid or the Subnet specified did not have internet access. The value written to /command-output.txt must be a non-empty single word value without quotation marks. Check cloud-init.log in the LogGroup specified for more information." (RequestToken: c7ebb7d1-ec0a-43e0-ea18-f13a331fc00d, HandlerErrorCode: NotStabilized)