はじめに
EventBridgeを使ってEC2の定期起動・停止するCFnを作る時たくさん悩んだので、未来の私がCFn見直した時に思い出せるようにするためのメモ。
※投稿してしまったのですが確認したら今CFnが思うとおりに動いていないので修正中です(2020/07/30)
=>修正しました!(2020/08/13)
CloudFormation
1. このCFnでつくられるもの。
■ CloudWatch Event Rule
- 起動させるイベントルール
- 停止させるイベントルール
■ IAM Role
- 自動化用のAutomationAssumeRole
- AWSマネージドポリシー
- 起動イベント用のロール
- カスタムポリシー
- 停止イベント用のロール
- カスタムポリシー
2. つくったテンプレート。
AWSTemplateFormatVersion: 2010-09-09
Description: EC2_Start_Stop
Parameters:
EC2InstanceID1:
Type: String
Description: EC2InstanceID1
Default: "i-xxxxxxxxxxxxxxx"
EC2InstanceID2:
Type: String
Description: EC2InstanceID2
Default: "i-yyyyyyyyyyyyyy"
StartTime:
Type: String
Description: start time of Ec2
Default: 00 00 ? * MON-FRI *
StopTime:
Type: String
Description: stop time of Ec2
Default: 00 09 ? * MON-FRI *
Resources:
# ------------------------------------------------------------#
# Event
# ------------------------------------------------------------#
EventRuleEc2Start:
Type: AWS::Events::Rule
Properties:
Name: Ec2StartRule
Description: !Sub ${StartTime} Start
ScheduleExpression: !Sub 'cron(${StartTime})'
State: ENABLED
Targets:
- Arn: arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StartEC2Instance:$DEFAULT
Id: TargetStartEC2Instance1
RoleArn: !Sub ${StartEc2Role.Arn}
Input: !Sub "{\"InstanceId\":[\"${EC2InstanceID1}\"],\"AutomationAssumeRole\":[\"${EC2AmazonSSMAutomationRole.Arn}\"]}"
- Arn: arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StartEC2Instance:$DEFAULT
Id: TargetStartEC2Instance2
RoleArn: !Sub ${StartEc2Role.Arn}
Input: !Sub "{\"InstanceId\":[\"${EC2InstanceID2}\"],\"AutomationAssumeRole\":[\"${EC2AmazonSSMAutomationRole.Arn}\"]}"
EventRuleEc2Stop:
Type: AWS::Events::Rule
Properties:
Name: Ec2StopRule
Description: !Sub ${StopTime} Stop
ScheduleExpression: !Sub 'cron(${StopTime})'
State: ENABLED
Targets:
- Arn: arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StopEC2Instance:$DEFAULT
Id: TargetStopEC2Instance1
RoleArn: !Sub ${StopEc2Role.Arn}
Input: !Sub "{\"InstanceId\":[\"${EC2InstanceID1}\"],\"AutomationAssumeRole\":[\"${EC2AmazonSSMAutomationRole.Arn}\"]}"
- Arn: arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StopEC2Instance:$DEFAULT
Id: TargetStopEC2Instance2
RoleArn: !Sub ${StopEc2Role.Arn}
Input: !Sub "{\"InstanceId\":[\"${EC2InstanceID2}\"],\"AutomationAssumeRole\":[\"${EC2AmazonSSMAutomationRole.Arn}\"]}"
# ------------------------------------------------------------#
# AutomationAssumeRole
# ------------------------------------------------------------#
EC2AmazonSSMAutomationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ssm.amazonaws.com
- ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole
Path: "/"
#------------------------------------------------------------#
# StartEc2
# ------------------------------------------------------------#
StartEc2Role:
Type: AWS::IAM::Role
Properties:
RoleName: "StartEC2Role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "events.amazonaws.com"
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: StartEc2Role_policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:StartAutomationExecution
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/AWS-StartEC2Instance:$DEFAULT
- Effect: Allow
Action: "iam:PassRole"
Resource: !GetAtt EC2AmazonSSMAutomationRole.Arn
Condition:
StringLikeIfExists:
iam:PassedToService: ssm.amazonaws.com
# ------------------------------------------------------------#
# StopEc2
# ------------------------------------------------------------#
StopEc2Role:
Type: AWS::IAM::Role
Properties:
RoleName: "StopEC2Role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "events.amazonaws.com"
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: StopEc2Role_policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: ssm:StartAutomationExecution
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/AWS-StopEC2Instance:$DEFAULT
- Effect: Allow
Action: "iam:PassRole"
Resource: !GetAtt EC2AmazonSSMAutomationRole.Arn
Condition:
StringLikeIfExists:
iam:PassedToService: ssm.amazonaws.com
3. テンプレートのなかみ。
■ parameter
Parameters:
EC2InstanceID1:
Type: String
Description: EC2InstanceID1
Default: "i-xxxxxxxxxxxxxxx"
EC2InstanceID2:
Type: String
Description: EC2InstanceID2
Default: "i-yyyyyyyyyyyyyy"
StartTime:
Type: String
Description: start time of Ec2
Default: 00 00 ? * MON-FRI *
StopTime:
Type: String
Description: stop time of Ec2
Default: 00 09 ? * MON-FRI *
定期起動・停止したいインスタンスのインスタンスIDと、実行する時間をparameterで出してます。
同じ時間で起動・停止させるEC2インスタンス2つに対して設定したかったのでIDを入力するところは2つ。
時間はcron式で指定します。このテンプレートはデフォルトで日本時間の平日9:00に起動、18:00に停止するようになってます。
■ イベント
EventRuleEc2Start:
Type: AWS::Events::Rule
Properties:
Name: Ec2StartRule
Description: !Sub ${StartTime} Start
ScheduleExpression: !Sub 'cron(${StartTime})'
State: ENABLED
Targets:
- Arn: arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StartEC2Instance:$DEFAULT
Id: TargetStartEC2Instance1
RoleArn: !Sub ${StartEc2Role.Arn}
Input: !Sub "{\"InstanceId\":[\"${EC2InstanceID1}\"],\"AutomationAssumeRole\":[\"${EC2AmazonSSMAutomationRole.Arn}\"]}"
- Arn: arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StartEC2Instance:$DEFAULT
Id: TargetStartEC2Instance2
RoleArn: !Sub ${StartEc2Role.Arn}
Input: !Sub "{\"InstanceId\":[\"${EC2InstanceID2}\"],\"AutomationAssumeRole\":[\"${EC2AmazonSSMAutomationRole.Arn}\"]}"
AWS::Events::Rule - AWS CloudFormation
トリガーとなるイベントの作成。
Targets
にルールがトリガーされたときに呼び出されるリソースをリスト型で書いていきます。
EventRuleEc2Startでは起動させるロールを割り当てたいので、Arn
にStartEC2Instanceを参照させて、RoleArn
にはルールがトリガーされた時にこのターゲットに使用されるIAMロールの ARNを書きます。ここには、同じCFn内のStartEc2Roleを使いたいので、!Sub ${StartEc2Role.Arn}
でStartEc2RoleのARNを参照しています。(!Sub
と.Arn
でARN取ってこれるの知らなかった。)
同様にEventRuleEc2Stopでは停止させるイベントを作成します。Startの部分がStopになるくらいなので割愛。
■ 自動化用ロール
EC2AmazonSSMAutomationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ssm.amazonaws.com
- ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole
Path: "/"
マネージドポリシーのAmazonSSMAutomationRoleを作成します。
AssumeRolePolicyDocument
にこのロールに関連付けられている信頼ポリシーを書くのですが、ssm.amazonaws.com
とec2.amazonaws.com
をこのロールを引き受けることができるエンティティに指定したいのでService
にリスト型で書いておきます。
■ 起動・停止用ロール
StartEc2Role:
Type: AWS::IAM::Role
Properties:
RoleName: "StartEC2Role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "events.amazonaws.com"
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: StartEc2Role_policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:StartAutomationExecution
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/AWS-StartEC2Instance:$DEFAULT
- Effect: Allow
Action: "iam:PassRole"
Resource: !GetAtt EC2AmazonSSMAutomationRole.Arn
Condition:
StringLikeIfExists:
iam:PassedToService: ssm.amazonaws.com
AWS::IAM::Role - AWS CloudFormation
起動・停止用のカスタムロールを作成します。
イベントをトリガーにしているので信頼されるエンティティはevents.amazonaws.com
。
そして、ロールにStartEc2Role_policyというインラインポリシーを追加させるためにPolicies
以下に定義していきます。AWSサービスにロールを渡すにはPassRoleのアクセス許可が必要なので、ここでEC2AmazonSSMAutomationRoleにアクセス許可を付与しています。
イベントと同じくStopの方は割愛。
4. テンプレート展開してみる。
指定した時間通り動いた!わーい!
参考
公式ドキュメントと先人と先輩に感謝。
- AWS サービスにロールを渡すアクセス許可をユーザーに許可する - AWS Identity and Access Management
- AWS-StartEC2Instance - AWS Systems Manager
- AWS Systems Manager AutomationでEC2の自動停止 | ヤマムギ
- EC2インスタンスのスケジュール起動がお手軽に実現できるようになっていた!-Qiita
おわりに
CloudFormationでリソースを作成できても一向にEC2が起きてこなかったり、1つは起動するのにもう1つが起動しなかったりとたくさんはまりました。
間違いや修正点等あったら教えてください!
早くCloudFormationと仲良くなりたい、、