Edited at

AWS::CloudFormation::Init タイプを使ってEC2インスタンスの環境構築ができるようにしてみた

cloudpack あら便利カレンダー 2019 の記事となります。誕生秘話 はこちら。

AWS CloudFormation(CFn)でEC2インスタンスを作成するのに、AWS::CloudFormation::Init タイプを利用すると、インスタンス起動後にパッケージのインストールやファイル作成、コマンド実行を含めることができて便利そうだったのでお試ししてみました。

個人的に一番のメリットはスタック更新時にメタデータの変更も検知して再実行させることができる点です。

AWS::CloudFormation::Init - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-init.html

利用するのにいくつかのドキュメントを渡り歩く必要があったので、必要最低限のテンプレートと利用方法、ポイントをまとめました。


前提


  • AWSアカウントがある

  • AWS CLIがインストール済みで利用可能

  • CFn、EC2関連の権限がある


テンプレート

EC2インスタンスを作成・管理するテンプレートとなります。

EC2インスタンスを起動するVPCやサブネットは既存のリソースを利用前提となります。

SSHアクセスする際のキーペアは事前に作成し、セキュリティグループはインスタンスとあわせて作成しています。

作成するリージョンは検証なので、us-east-1 のみとしています。


cfn-template.yaml

Parameters:

VpcId:
Type: AWS::EC2::VPC::Id
SubnetId:
Type: AWS::EC2::Subnet::Id
EC2KeyPairName:
Type: AWS::EC2::KeyPair::KeyName
InstanceType:
Type: String
Default: t3.small
MyInstanceSSHCidrIp:
Type: String
Default: '0.0.0.0/0'

Mappings:
AWSRegionToAMI:
us-east-1:
HVM64: ami-0080e4c5bc078760e

Resources:
MyInstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join
- " "
- - !Ref AWS::StackName
- "SecurityGroup"
GroupDescription: "MyInstance SecurityGroup"
VpcId: !Ref VpcId
SecurityGroupEgress:
-
CidrIp: "0.0.0.0/0"
IpProtocol: "-1"
SecurityGroupIngress:
-
CidrIp: !Ref MyInstanceSSHCidrIp
Description: !Join
- " "
- - !Ref AWS::StackName
- "SSH Port"
IpProtocol: "tcp"
FromPort: 22
ToPort: 22
Tags:
-
Key: "Name"
Value: !Join
- " "
- - !Ref AWS::StackName
- "SecurityGroup"

MyInstance:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
files:
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack = ${AWS::StackName}
region = ${AWS::Region}
interval = 1
mode: "000400"
owner: "root"
group: "root"

/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers = post.update
path = Resources.MyInstance.Metadata.AWS::CloudFormation::Init
action = /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
runas = root
mode: "000400"
owner: "root"
group: "root"
commands:
test:
command: "echo $STACK_NAME test"
env:
STACK_NAME: !Ref AWS::StackName
services:
sysvinit:
cfn-hup:
enabled: "true"
files:
- /etc/cfn/cfn-hup.conf
- /etc/cfn/hooks.d/cfn-auto-reloader.conf

Properties:
InstanceType: !Ref InstanceType
KeyName: !Ref EC2KeyPairName
ImageId: !FindInMap [ AWSRegionToAMI, !Ref "AWS::Region", HVM64 ]
IamInstanceProfile: !Ref AWS::NoValue
NetworkInterfaces:
- AssociatePublicIpAddress: True
DeviceIndex: 0
GroupSet:
- !Ref MyInstanceSecurityGroup
SubnetId: !Ref SubnetId
Tags:
- Key: 'Name'
Value: !Ref AWS::StackName
UserData:
Fn::Base64:
!Sub |
#!/bin/bash
echo "start UserData"
/opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
echo "finish UserData"

CreationPolicy:
ResourceSignal:
Timeout: PT5M

Outputs:
ClientInstanceId:
Value: !Ref MyInstance
ClientPublicIp:
Value: !GetAtt MyInstance.PublicIp



ポイント


AWS::CloudFormation::Init タイプを利用する

AWS::CloudFormation::Init タイプを利用すると、EC2インスタンスでcfn-init ヘルパースクリプト用のメタデータとして取り込まれて実行されます。

cfn-init ヘルパースクリプトはメタデータに応じて以下のような操作を行います。


  • CFnのメタデータの取得と解析

  • パッケージのインストール

  • ディスクへのファイルの書き込み

  • Linux/UNIXグループ・ユーザー作成

  • ソースのダウンロード・展開

  • コマンド実行

  • サービスの有効化/無効化と開始/停止

詳しくは下記が参考になります。

AWS::CloudFormation::Init - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-init.html

cfn-init - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-init.html

今回のテンプレートではメタデータでファイル書き込み、コマンド実行とサービス有効化する定義をしています。詳細は後ほど。


cfn-template.yaml_一部抜粋

  MyInstance:

Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
files:
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack = ${AWS::StackName}
region = ${AWS::Region}
interval = 1
mode: "000400"
owner: "root"
group: "root"

/etc/cfn/hooks.d/cfn-auto-reloader.conf:
(略)
commands:
test:
command: "echo $STACK_NAME test2"
env:
STACK_NAME: !Ref AWS::StackName
services:
sysvinit:
cfn-hup:
(略)



cfn-init ヘルパースクリプトをUserData で実行する

AWS::CloudFormation::Init タイプを利用してメタデータを定義しただけだと、スタック作成時にcfn-init ヘルパースクリプトは実行されないため、ユーザーデータを定義して実行します。

ユーザーデータについては下記が参考になります。

Linux インスタンスでの起動時のコマンドの実行 - Amazon Elastic Compute Cloud

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html

今回のテンプレートでは/opt/aws/bin/cfn-init でエラーとなった場合、リソース作成失敗となるように/opt/aws/bin/cfn-signal も実行するようにしています。/opt/aws/bin/cfn-signal を利用する場合には、CreationPolicy を定義しておく必要があります。

cfn-signal ヘルパースクリプトについては下記が参考になります。

cfn-signal - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-signal.html


cfn-template.yaml_一部抜粋

  MyInstance:

(略)
Properties:
(略)
UserData:
Fn::Base64:
!Sub |
#!/bin/bash
echo "start UserData"
/opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
echo "finish UserData"
CreationPolicy:
ResourceSignal:
Timeout: PT5M


cfn-hup ヘルパーでメタデータ更新時に対応する

メタデータとユーザーデータの定義だけだと、メタデータを変更してスタック更新しても変更が検知されず、EC2インスタンスに反映されないため、cfn-hup ヘルパーを利用する必要があります。cfn-hup ヘルパーはEC2インスタンスでデーモンとして実行が可能です。

cfn-hup デーモンを実行するためにはcfn-hup.confhooks.conf ファイルを用意する必要があるため、メタデータで定義しています。

cfn-hup ヘルパーについては下記が参考になります。

cfn-hup - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-hup.html


cfn-template.yaml_一部抜粋

  MyInstance:

Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
files:
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack = ${AWS::StackName}
region = ${AWS::Region}
interval = 1
mode: "000400"
owner: "root"
group: "root"
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers = post.update
path = Resources.MyInstance.Metadata.AWS::CloudFormation::Init
action = /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
runas = root
mode: "000400"
owner: "root"
group: "root"
commands:
(略)
services:
sysvinit:
cfn-hup:
enabled: "true"
files:
- /etc/cfn/cfn-hup.conf
- /etc/cfn/hooks.d/cfn-auto-reloader.conf


利用方法


パラメータ値を取得


キーペア

EC2インスタンスへSSHログインするのに利用するキーペアを作成します。

既存のキーペアを利用する場合は作成不要です。

# Create KeyPair

> aws ec2 create-key-pair \
--key-name cfn-init-test-ec2-key \
--query "KeyMaterial" \
--output text > cfn-init-test-ec2-key.pem

> chmod 400 cfn-init-test-ec2-key.pem


VPC、サブネット

既存のVPC、サブネット配下にEC2インスタンスを作成する前提ですので各IDを取得します。

# VpcId

> aws ec2 describe-vpcs \
--query "Vpcs"

[
{
"CidrBlock": "172.31.0.0/16",
"DhcpOptionsId": "dopt-b06bd8c8",
"State": "available",
"VpcId": "vpc-xxxxxxxx",
"OwnerId": "xxxxxxxxxxxx",
"InstanceTenancy": "default",
"CidrBlockAssociationSet": [
{
"AssociationId": "vpc-cidr-assoc-2b23e646",
"CidrBlock": "172.31.0.0/16",
"CidrBlockState": {
"State": "associated"
}
}
],
"IsDefault": true
},
]

# SubnetId
> aws ec2 describe-subnets \
--filters '{"Name": "vpc-id", "Values": ["vpc-xxxxxxxx"]}' \
--query "Subnets"

[
{
"AvailabilityZone": "us-east-1a",
"AvailabilityZoneId": "use1-az2",
"AvailableIpAddressCount": 4089,
"CidrBlock": "172.31.80.0/20",
"DefaultForAz": true,
"MapPublicIpOnLaunch": true,
"State": "available",
"SubnetId": "subnet-xxxxxxxx",
"VpcId": "vpc-xxxxxxxx",
"OwnerId": "xxxxxxxxxxxx",
"AssignIpv6AddressOnCreation": false,
"Ipv6CidrBlockAssociationSet": [],
"SubnetArn": "arn:aws:ec2:us-east-1:xxxxxxxxxxxx:subnet/subnet-xxxxxxxx"
},
]


自身のグローバルIP

SSHアクセスするためのセキュリティグループに指定するIPアドレスを取得します。

ここではifconfig.io を利用していますが他の手段でもOKです。

## GlobalIP

> curl ifconfig.io

xxx.xxx.xxx.xxx


スタック作成

aws cloudformation create-stack コマンドを利用してスタック作成します。

AWSマネジメントコンソールから作成してもOKです。

> aws cloudformation create-stack \

--stack-name cfn-init-test \
--template-body file://cfn-init-template.yaml \
--capabilities CAPABILITY_IAM \
--region us-east-1 \
--parameters '[
{
"ParameterKey": "VpcId",
"ParameterValue": "vpc-xxxxxxxx"
},
{
"ParameterKey": "SubnetId",
"ParameterValue": "subnet-xxxxxxxx"
},
{
"ParameterKey": "EC2KeyPairName",
"ParameterValue": "cfn-init-test-ec2-key"
},
{
"ParameterKey": "MyInstanceSSHCidrIp",
"ParameterValue": "xxx.xxx.xxx.xxx/32"
}
]'


スタック作成結果を確認

スタック作成ができたか確認します。

 > aws cloudformation describe-stacks \

--stack-name cfn-init-test

{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-init-test/a1dc6aa0-a9e6-11e9-b171-0a515b01a4a4",
"StackName": "cfn-init-test",
"Parameters": [
{
"ParameterKey": "MyInstanceSSHCidrIp",
"ParameterValue": "xxx.xxx.xxx.xxx/32"
},
{
"ParameterKey": "VpcId",
"ParameterValue": "vpc-xxxxxxxx"
},
{
"ParameterKey": "EC2KeyPairName",
"ParameterValue": "cfn-init-test-ec2-key"
},
{
"ParameterKey": "SubnetId",
"ParameterValue": "subnet-xxxxxxxx"
},
{
"ParameterKey": "InstanceType",
"ParameterValue": "t3.small"
}
],
"CreationTime": "2019-07-19T05:32:43.477Z",
"RollbackConfiguration": {},
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Capabilities": [
"CAPABILITY_IAM"
],
"Outputs": [
{
"OutputKey": "ClientPublicIp",
"OutputValue": "xxx.xxx.xxx.xxx"
},
{
"OutputKey": "ClientInstanceId",
"OutputValue": "i-xxxxxxxxxxxxxxxxx"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}


EC2インスタンスで確認

テンプレートのOutputs でEC2インスタンスのIPアドレス('ClientPublicIp' )が出力されるようにしているので、SSHログインして各ログを確認します。

CFnでEC2インスタンスを作成するといくつかのログファイルが作成されますが、下記のファイル確認しておくとだいたいは把握できます。

> ssh -i cfn-init-test-ec2-key.pem ec2-user@xxx.xxx.xxx.xxx


$ cat /var/log/cfn-init.log

2019-07-19 06:53:00,454 [INFO] -----------------------Starting build-----------------------
2019-07-19 06:53:00,455 [INFO] Running configSets: default
2019-07-19 06:53:00,455 [INFO] Running configSet default
2019-07-19 06:53:00,456 [INFO] Running config config
2019-07-19 06:53:00,460 [INFO] Command test succeeded
2019-07-19 06:53:00,467 [INFO] enabled service cfn-hup
2019-07-19 06:53:00,768 [INFO] Restarted cfn-hup successfully
2019-07-19 06:53:00,769 [INFO] ConfigSets completed
2019-07-19 06:53:00,769 [INFO] -----------------------Build complete-----------------------
2019-07-19 06:53:01,055 [DEBUG] CloudFormation client initialized with endpoint https://cloudformation.us-east-1.amazonaws.com
2019-07-19 06:53:01,055 [DEBUG] Signaling resource MyInstance in stack cfn-init-test with unique ID i-xxxxxxxxxxxxxxxxx and status SUCCESS

$ cat /var/log/cfn-init-cmd.log

2019-07-19 06:53:00,455 P2208 [INFO] ************************************************************
2019-07-19 06:53:00,455 P2208 [INFO] ConfigSet default
2019-07-19 06:53:00,456 P2208 [INFO] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2019-07-19 06:53:00,456 P2208 [INFO] Config config
2019-07-19 06:53:00,457 P2208 [INFO] ============================================================
2019-07-19 06:53:00,457 P2208 [INFO] Command test
2019-07-19 06:53:00,460 P2208 [INFO] -----------------------Command Output-----------------------
2019-07-19 06:53:00,460 P2208 [INFO] cfn-init-test test
2019-07-19 06:53:00,460 P2208 [INFO] ------------------------------------------------------------
2019-07-19 06:53:00,460 P2208 [INFO] Completed successfully.

$ cat /var/log/cloud-init-output.log

(略)
Updated:
java-1.7.0-openjdk.x86_64 1:1.7.0.211-2.6.17.1.79.amzn1
kernel-tools.x86_64 0:4.14.128-87.105.amzn1
perl.x86_64 4:5.16.3-294.43.amzn1
perl-Pod-Escapes.noarch 1:1.04-294.43.amzn1
perl-libs.x86_64 4:5.16.3-294.43.amzn1
perl-macros.x86_64 4:5.16.3-294.43.amzn1
python27-jinja2.noarch 0:2.7.2-3.16.amzn1
wget.x86_64 0:1.18-5.30.amzn1

Complete!
Cloud-init v. 0.7.6 running 'modules:final' at Fri, 19 Jul 2019 05:33:29 +0000. Up 19.60 seconds.
start UserData
finish UserData
Cloud-init v. 0.7.6 finished at Fri, 19 Jul 2019 05:33:31 +0000. Datasource DataSourceEc2. Up 20.79 seconds

スタック更新に関わるログとファイルは以下となります。

cfn-hup デーモンが定期的にCFnスタックのメタデータを取得しmetadata_db.json に保存、メタデータとファイルに差分があるとcfn-auto-reloader.conf で指定したアクション(コマンド)が実行される仕組みになっています。

なので、スタック作成後、cfn-hup デーモンが1度もメタデータ取得していないタイミングでスタック更新しても、metadata_db.jsonが存在していないと変更が検知されないみたいです。(1敗

$ cat /var/log/cfn-hup.log


2019-07-19 06:53:00,750 [DEBUG] CloudFormation client initialized with endpoint https://cloudformation.us-east-1.amazonaws.com
2019-07-19 06:53:00,750 [DEBUG] Creating /var/lib/cfn-hup/data
2019-07-19 06:53:00,756 [INFO] No umask value specified in config file. Using the default one: 022

$ sudo cat /var/lib/cfn-hup/data/metadata_db.json

{"cfn-auto-reloader-hook|Resources.MyInstance.Metadata.AWS::CloudFormation::Init": {"config": {"files": {"/etc/cfn/cfn-hup.conf": {"owner": "root", "content": "[main]\nstack = cfn-init-test\nregion = us-east-1\ninterval = 1\n", "group": "root", "mode": "000400"}, "/etc/cfn/hooks.d/cfn-auto-reloader.conf": {"owner": "root", "content": "[cfn-auto-reloader-hook]\ntriggers = post.update\npath = Resources.MyInstance.Metadata.AWS::CloudFormation::Init\naction = /opt/aws/bin/cfn-init -v --stack cfn-init-test --resource MyInstance --region us-east-1\nrunas = root\n", "group": "root", "mode": "000400"}}, "services": {"sysvinit": {"cfn-hup": {"files": ["/etc/cfn/cfn-hup.conf", "/etc/cfn/hooks.d/cfn-auto-reloader.conf"], "enabled": "true"}}}, "commands": {"test": {"command": "echo $STACK_NAME test", "env": {"STACK_NAME": "cfn-init-test"}}}}}}


メタデータを編集してスタック更新する

メタデータに実行するコマンドを追加してスタック更新してみます。


cfn-template.yaml_一部抜粋

(略)

commands:
test:
command: "echo $STACK_NAME test"
env:
STACK_NAME: !Ref AWS::StackName
test2:
command: "echo $STACK_NAME test2"
env:
STACK_NAME: !Ref AWS::StackName
(略)

aws cloudformation update-stack コマンドを実行してスタック更新します。

> aws cloudformation update-stack \

--stack-name cfn-init-test \
--template-body file://cfn-init-template.yaml \
--capabilities CAPABILITY_IAM \
--region us-east-1 \
--parameters '[
{
"ParameterKey": "VpcId",
"ParameterValue": "vpc-xxxxxxxx"
},
{
"ParameterKey": "SubnetId",
"ParameterValue": "subnet-xxxxxxxx"
},
{
"ParameterKey": "EC2KeyPairName",
"ParameterValue": "cfn-init-test-ec2-key"
},
{
"ParameterKey": "MyInstanceSSHCidrIp",
"ParameterValue": "xxx.xxx.xxx.xxx/32"
}
]'


スタック更新結果を確認

> aws cloudformation describe-stacks \

--stack-name cfn-init-test

{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-init-test/a1dc6aa0-a9e6-11e9-b171-0a515b01a4a4",
"StackName": "cfn-init-test",
"Parameters": [
{
"ParameterKey": "MyInstanceSSHCidrIp",
"ParameterValue": "xxx.xxx.xxx.xxx/32"
},
{
"ParameterKey": "VpcId",
"ParameterValue": "vpc-xxxxxxxx"
},
{
"ParameterKey": "EC2KeyPairName",
"ParameterValue": "cfn-init-test-ec2-key"
},
{
"ParameterKey": "SubnetId",
"ParameterValue": "subnet-xxxxxxxx"
},
{
"ParameterKey": "InstanceType",
"ParameterValue": "t3.small"
}
],
"CreationTime": "2019-07-19T05:32:43.477Z",
"LastUpdatedTime": "2019-07-19T05:41:40.446Z",
"RollbackConfiguration": {},
"StackStatus": "UPDATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Capabilities": [
"CAPABILITY_IAM"
],
"Outputs": [
{
"OutputKey": "ClientPublicIp",
"OutputValue": "xxx.xxx.xxx.xxx"
},
{
"OutputKey": "ClientInstanceId",
"OutputValue": "i-xxxxxxxxxxxxxxxxx"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}


EC2インスタンスで確認

CFnのリソース更新されてもEC2インスタンス側への変更反映は、cfn-hup ヘルパーが担当しますので、反映にはタイムラグが発生します。cfn-hup ヘルパーがメタデータの変更確認する間隔は初期設定で15分となっており、/etc/cfn/cfn-hup.conf ファイルのinterval で指定ができます。(今回は1分で指定)

cfn-hup - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-hup.html

> ssh -i cfn-init-test-ec2-key.pem ec2-user@xxx.xxx.xxx.xxx

$ cat /var/log/cfn-init.log

2019-07-19 06:53:00,454 [INFO] -----------------------Starting build-----------------------
2019-07-19 06:53:00,455 [INFO] Running configSets: default
2019-07-19 06:53:00,455 [INFO] Running configSet default
2019-07-19 06:53:00,456 [INFO] Running config config
2019-07-19 06:53:00,460 [INFO] Command test succeeded
2019-07-19 06:53:00,467 [INFO] enabled service cfn-hup
2019-07-19 06:53:00,768 [INFO] Restarted cfn-hup successfully
2019-07-19 06:53:00,769 [INFO] ConfigSets completed
2019-07-19 06:53:00,769 [INFO] -----------------------Build complete-----------------------
2019-07-19 06:53:01,055 [DEBUG] CloudFormation client initialized with endpoint https://cloudformation.us-east-1.amazonaws.com
2019-07-19 06:53:01,055 [DEBUG] Signaling resource MyInstance in stack cfn-init-test with unique ID i-xxxxxxxxxxxxxxxxx and status SUCCESS
2019-07-19 06:56:01,457 [DEBUG] CloudFormation client initialized with endpoint https://cloudformation.us-east-1.amazonaws.com
2019-07-19 06:56:01,458 [DEBUG] Describing resource MyInstance in stack cfn-init-test
2019-07-19 06:56:01,522 [INFO] -----------------------Starting build-----------------------
2019-07-19 06:56:01,523 [DEBUG] Not setting a reboot trigger as scheduling support is not available
2019-07-19 06:56:01,524 [INFO] Running configSets: default
2019-07-19 06:56:01,525 [INFO] Running configSet default
2019-07-19 06:56:01,525 [INFO] Running config config
2019-07-19 06:56:01,526 [DEBUG] No packages specified
2019-07-19 06:56:01,526 [DEBUG] No groups specified
2019-07-19 06:56:01,526 [DEBUG] No users specified
2019-07-19 06:56:01,526 [DEBUG] No sources specified
2019-07-19 06:56:01,526 [DEBUG] /etc/cfn/cfn-hup.conf already exists
2019-07-19 06:56:01,526 [DEBUG] Moving /etc/cfn/cfn-hup.conf to /etc/cfn/cfn-hup.conf.bak
2019-07-19 06:56:01,526 [DEBUG] Writing content to /etc/cfn/cfn-hup.conf
2019-07-19 06:56:01,526 [DEBUG] Setting mode for /etc/cfn/cfn-hup.conf to 000400
2019-07-19 06:56:01,526 [DEBUG] Setting owner 0 and group 0 for /etc/cfn/cfn-hup.conf
2019-07-19 06:56:01,527 [DEBUG] /etc/cfn/hooks.d/cfn-auto-reloader.conf already exists
2019-07-19 06:56:01,527 [DEBUG] Moving /etc/cfn/hooks.d/cfn-auto-reloader.conf to /etc/cfn/hooks.d/cfn-auto-reloader.conf.bak
2019-07-19 06:56:01,527 [DEBUG] Writing content to /etc/cfn/hooks.d/cfn-auto-reloader.conf
2019-07-19 06:56:01,527 [DEBUG] Setting mode for /etc/cfn/hooks.d/cfn-auto-reloader.conf to 000400
2019-07-19 06:56:01,527 [DEBUG] Setting owner 0 and group 0 for /etc/cfn/hooks.d/cfn-auto-reloader.conf
2019-07-19 06:56:01,527 [DEBUG] Running command test
2019-07-19 06:56:01,527 [DEBUG] No test for command test
2019-07-19 06:56:01,531 [INFO] Command test succeeded
2019-07-19 06:56:01,531 [DEBUG] Command test output: cfn-init-test test

2019-07-19 06:56:01,531 [DEBUG] Running command test2
2019-07-19 06:56:01,531 [DEBUG] No test for command test2
2019-07-19 06:56:01,535 [INFO] Command test2 succeeded
2019-07-19 06:56:01,536 [DEBUG] Command test2 output: cfn-init-test test!

2019-07-19 06:56:01,536 [DEBUG] Using service modifier: /sbin/chkconfig
2019-07-19 06:56:01,536 [DEBUG] Setting service cfn-hup to enabled
2019-07-19 06:56:01,538 [INFO] enabled service cfn-hup
2019-07-19 06:56:01,539 [DEBUG] Not modifying running state of service cfn-hup
2019-07-19 06:56:01,539 [INFO] ConfigSets completed
2019-07-19 06:56:01,539 [DEBUG] Not clearing reboot trigger as scheduling support is not available
2019-07-19 06:56:01,539 [INFO] -----------------------Build complete-----------------------

$ cat /var/log/cfn-init-cmd.log

2019-07-19 06:53:00,455 P2208 [INFO] ************************************************************
2019-07-19 06:53:00,455 P2208 [INFO] ConfigSet default
2019-07-19 06:53:00,456 P2208 [INFO] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2019-07-19 06:53:00,456 P2208 [INFO] Config config
2019-07-19 06:53:00,457 P2208 [INFO] ============================================================
2019-07-19 06:53:00,457 P2208 [INFO] Command test
2019-07-19 06:53:00,460 P2208 [INFO] -----------------------Command Output-----------------------
2019-07-19 06:53:00,460 P2208 [INFO] cfn-init-test test
2019-07-19 06:53:00,460 P2208 [INFO] ------------------------------------------------------------
2019-07-19 06:53:00,460 P2208 [INFO] Completed successfully.
2019-07-19 06:56:01,525 P2337 [INFO] ************************************************************
2019-07-19 06:56:01,525 P2337 [INFO] ConfigSet default
2019-07-19 06:56:01,525 P2337 [INFO] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2019-07-19 06:56:01,526 P2337 [INFO] Config config
2019-07-19 06:56:01,527 P2337 [INFO] ============================================================
2019-07-19 06:56:01,527 P2337 [INFO] Command test
2019-07-19 06:56:01,530 P2337 [INFO] -----------------------Command Output-----------------------
2019-07-19 06:56:01,531 P2337 [INFO] cfn-init-test test
2019-07-19 06:56:01,531 P2337 [INFO] ------------------------------------------------------------
2019-07-19 06:56:01,531 P2337 [INFO] Completed successfully.
2019-07-19 06:56:01,531 P2337 [INFO] ============================================================
2019-07-19 06:56:01,531 P2337 [INFO] Command test2
2019-07-19 06:56:01,535 P2337 [INFO] -----------------------Command Output-----------------------
2019-07-19 06:56:01,535 P2337 [INFO] cfn-init-test test!
2019-07-19 06:56:01,535 P2337 [INFO] ------------------------------------------------------------
2019-07-19 06:56:01,535 P2337 [INFO] Completed successfully.

$ cat /var/log/cloud-init-output.log

(略)
Updated:
java-1.7.0-openjdk.x86_64 1:1.7.0.211-2.6.17.1.79.amzn1
kernel-tools.x86_64 0:4.14.128-87.105.amzn1
perl.x86_64 4:5.16.3-294.43.amzn1
perl-Pod-Escapes.noarch 1:1.04-294.43.amzn1
perl-libs.x86_64 4:5.16.3-294.43.amzn1
perl-macros.x86_64 4:5.16.3-294.43.amzn1
python27-jinja2.noarch 0:2.7.2-3.16.amzn1
wget.x86_64 0:1.18-5.30.amzn1

Complete!
Cloud-init v. 0.7.6 running 'modules:final' at Fri, 19 Jul 2019 06:52:59 +0000. Up 18.81 seconds.
start UserData
finish UserData
Cloud-init v. 0.7.6 finished at Fri, 19 Jul 2019 06:53:01 +0000. Datasource DataSourceEc2. Up 20.07 seconds

メタデータに追加したコマンドが実行されるのを確認できました。

やったぜ。

スタック更新時にメタデータで定義しているすべての処理を実行したくない場合、Configset を用いるとグルーピングや実行順の制御をして特定の処理ができます。詳細は下記をご参考ください。

AWS::CloudFormation::Init - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-init.html


まとめ

CFnでEC2インスタンスを作成する際に、インスタンス起動後の処理もテンプレートで定義して管理できる仕組みになっていますので、非常に使い勝手が良さそうです。


参考

AWS::CloudFormation::Init - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-init.html

AWS::CloudFormation::Init - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-init.html

cfn-init - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-init.html

Linux インスタンスでの起動時のコマンドの実行 - Amazon Elastic Compute Cloud

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html

cfn-signal - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-signal.html

cfn-hup - AWS CloudFormation

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-hup.html