不管使用 CDK 或用 CloudFormation,如果想要在生成某些 AWS Resources 後,接著執行與這些 Resources 相關的指令或一些自訂指令時,大都需要手動執行而無法並自動化,這篇文章推薦使用 AWSUtility::CloudFormation::CommandRunner 這個 CloudFormation Custom Resource Type,以下就讓我們來看怎麼使用
應用情境
沒有應用情境可能沒感覺,這邊舉一個最近完成的 npm package cdk-aws-codedeploy-on-premises
這個 CDK Construct 的目的,就是在 IDC(地端) 使用 AWS CodeDeploy 的情境,CDK Construct 可以完成建置 AWS CodeDeploy Application、CodeDeploy Deployment Group 和 Instance,以及產生相關需要 AWS IAM Role 和 User
但最後需要一個動作,必需用 CLI 執行 aws deploy register
去註冊 instance 後,才能運作正常 (或許之後 CloudFormation 會支援,但目前沒有),所以才會需要用到 CommandRunner
註冊 CommandRunner
註冊 CloudFormation 的 AWSUtility::CloudFormation::CommandRunner,在本地執行,或是用現在很潮的 CloudShell 來執行也可以,那執行身份需要有以下這些權限
- s3:CreateBucket
- s3:DeleteBucket
- s3:PutBucketPolicy
- s3:PutObject
- cloudformation:RegisterType
- cloudformation:DescribeTypeRegistration
- iam:createRole
AWS CLI Credential 準備好沒問題後,就可以來開始註冊
1. git clone
git clone https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-awsutilities-commandrunner.git
2. 進入 repo 目錄內
cd aws-cloudformation-resource-providers-awsutilities-commandrunner
3. 下載預先編譯好的套件包
curl -LO https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-awsutilities-commandrunner/releases/latest/download/awsutility-cloudformation-commandrunner.zip
如果不下載,應該也可以自己執行 scripts/build.sh
來編譯
4. 執行註冊
./scripts/register.sh
過程中,macos 的 console 會跳成編輯模式,只要按 q
鍵就可以,最後看到以下訊息,表示成功
AWSUtility::CloudFormation::CommandRunner is ready to use.
也可以再到 AWS Console Mangement -> CloudFormation -> Registry,下拉選單選 Private,Resource Type 看到AWSUtility::CloudFormation::CommandRunner
,表示一切就緒
這邊再提一下,註冊的動作成功後,會在 CloudFormation 後台看到 awsutility-cloudformation-commandrunner-execution-role-stack
這個 stack,切記不要刪掉,如果真的不小心刪到,請用反註冊的 script 如下
./scripts/cleanup.sh
之後反註冊完後,再執行一次註冊的動作,應該就能正常
運作方式
先來看個流程圖

圖片來源: AWS Blog
從圖片很清楚可以看到,CommandRunner 是會自動開啟一台 EC2 (t3.micro),並指定 EC2 instance profile 去 assume role 來執行指令,執行指令之後再自動刪除 EC2,非常的方便,可以自動化一些 CloudFormation 還沒支援的項目,或是自訂的流程等等…
在 CDK 中引用 CommandRunner
CDK 非常厲害,不僅可以使用 imperative programming 的方式完成 AWS Infra 的建置,更可以完美的使用 CloudFormation Template,官方文件 Import or migrate an existing AWS CloudFormation template 已經說明很清楚,這邊引用 CommandRunner 來實作一次
這邊一樣用在應用情境中說明的 npm package 來做說明,Command Runner 的 CloudFormation template 如下
Parameters:
CommandRole:
Type: String
Description: 'https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-awsutilities-commandrunner#role'
Command:
Type: String
Description: 'https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-awsutilities-commandrunner#command'
Resources:
Command:
Type: 'AWSUtility::CloudFormation::CommandRunner'
Properties:
Command: !Ref Command
Role: !Ref CommandRole
這個 template 使用很簡單的設計,把要執行的 command 和 執行這個 command 需要的權限角色帶進參數來使用
在 CDK 中,可以很輕鬆的這樣引用
addCommandRunner(): CfnResource {
const runCommandRole = this.createRunCommandRole();
const template = new cfn_inc.CfnInclude(this, 'AddCommandRunner', {
templateFile: `${__dirname}/../template/command-runner.yml`,
preserveLogicalIds: false,
parameters: {
CommandRole: runCommandRole.roleName,
Command: this.commands,
}
});
return template.getResource('Command') as CfnResource;
}
Troubleshooting
使用上也有踩到一點雷,這邊也提醒一下
- Command 結尾一定要使用 > /command-output.txt
不使用這個結尾導出輸出到檔案 command-output.txt
,deploy 時就會出現類似以下錯誤
1 validation error detected: Value '' at 'value' failed to satisfy constraint: Member must have length greater than or equal to 1. (Service: AWSSimpleSystemsManagement; Status Code: 400; Error Code: ValidationExcept
ion; Request ID: 35d8c36c-dbaa-4cff-a7cf-dcce65bd53e5)
原因大概就是 CommandRunner 把輸出儲存在 SSM parameter,所以才需要這個檔案
- 注意 Role 的權限
如果執行的指令是操作 AWS 資源的相關指令,記得要給予 EC2 相關的權限,否則會造成部署失敗
以應用情境的例子,我需要這樣指定權限
inlinePolicies: {
'EC2RunCommandRole': new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: ['codedeploy:RegisterOnPremisesInstance'],
resources: ['*'],
}),
],
}),
},
這樣才能順利執行註冊 instance 的指令