Skip to content

AWS Systems Manager Automationでタグを条件にEC2インスタンスを停止する

はじめに

AWS リソースに対して設定されているタグを条件に自動で削除する方法を調べてみました。Lambda を使う方法も考えられますが、AWS Systems Manager Automation というものを見つけたので、この記事ではその使い方について学んだことをアウトプットします。

成果物

https://github.com/kntks/blog-code/tree/main/2024/10/aws-stop-instance-by-tag

環境

バージョン
MacSonoma 14.5
Terraform1.9.5

環境構築

Terraform で EC2 インスタンスを作成します。

Terminal window
.
├── backend.tf
├── main.tf
└── provider.tf
Terminal window
terraform init
terraform plan
terraform apply

AWS Systems Manager Automation とは

EC2、RDS、Redshift、S3 など、さまざまな AWS サービスのメンテナンス、デプロイ、修復などの作業を自動化できます。 自動化を定義するには、事前定義済みのランブックを使用するか、自分で作成したランブックを使用します。

シンプルな実行で EC2 インスタンスを停止する

事前定義済みのランブックを使用して EC2 インスタンスを停止してみます。

AWS のマネジメントコンソールから AWS Systems Manager Automation のページにアクセスします。 aws-system-manager-top-page

Execute automation ボタンを押します。 aws-system-manager-autometion-top-page

runbook を選択する画面が表示されるので、AWS-StopEC2Instance を選択します。ページ下部にある Next ボタンを押します。 aws-system-manager-autometion-choose-runbook

実際に Execute ボタンを押して runbook を実行してみます。設定は以下の画像のようにします。 stop-ec2-instance-automation-runbook-1 stop-ec2-instance-automation-runbook-2 stop-ec2-instance-automation-runbook-3

runbook を実行すると以下のような画面になります。 execute-stop-ec2-instance-runbook

時間が経つと処理が完了するので、EC2 インスタンスを確認すると停止されていることがわかりました。 after-execute-stop-ec2-instance

レート制御を使用して EC2 インスタンスを停止する

タグを使った複数リソースに対して一括で処理を行う場合は、ターゲットとレート制御を使用します。

terrafrom で EC2 インスタンスを2つにします。

resource "aws_instance" "web" {
count = 2
ami = data.aws_ami.amazon_linux_2023.id
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.example.id]
associate_public_ip_address = true
tags = {
Name = "WebServer"
Environment = "sandbox"
}
}

先ほど同じく、 AWS-StopEC2Instance を選択し、Next ボタンを押します。 aws-system-manager-autometion-choose-runbook

先ほどと違い今度は Rate control を選択します。 設定は以下の画像のようにし、実際に Execute ボタンを押して runbook を実行してみます。 stop-ec2-instance-automation-runbook-rate-control-1 TargetsTags を選択し、KeyEnvironmentValuesandbox を入力します。 stop-ec2-instance-automation-runbook-rate-control-2

Rate controlConcurrency を 1 に設定します。 stop-ec2-instance-automation-runbook-rate-control-3

runbook を実行すると以下のような画面になります。Concurrecy を 1 に設定したため、1つのインスタンスが停止されると次のインスタンスが停止されるようになります。 execute-stop-ec2-instance-runbook-rate-control

時間が経つと処理が完了するので、EC2 インスタンスを確認すると停止されていることがわかりました。 after-execute-stop-ec2-instance-rate-control

独自のランブックを作成する

ランブックでサポートされているアクションタイプを使用すると、独自のランブックを作成できます。

Systems Manager Automation アクションのリファレンスを参考に、独自のランブックを作成してみます。

CloudWatch logs にログを出力する

独自のランブックを作成しますが、aws:executeScript アクションタイプ を使用するため、ログを出力するための設定を行います。

Automations のページから Preferences を選択します。Edit ボタンを押し、Send output to CloudWatch Logs にチェックを入れます。今回はデフォルトのロググループにします。

aws-system-manager-autometion-preferences-cloudwatch-logs

参考:CloudWatch Logs を使用した自動アクション出力のログ記録 - AWS Documentation

ロールを作成する

ランブックを実行する際に Assume Role をするためのロールを作成します。

data "aws_caller_identity" "current" {}
data "aws_iam_policy_document" "instance_assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ssm.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = [data.aws_caller_identity.current.account_id]
}
condition {
test = "ArnLike"
variable = "aws:SourceArn"
values = ["arn:aws:ssm:*:${data.aws_caller_identity.current.account_id}:automation-execution/*"]
}
}
}
resource "aws_iam_role" "system_manager_automation" {
name = "system-manager-automation-role"
path = "/"
assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json
}
resource "aws_iam_role_policy" "system_manager_automation" {
name = "automation-policy"
role = aws_iam_role.system_manager_automation.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"ec2:Describe*",
]
Effect = "Allow"
Resource = "*"
},
{
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
]
Effect = "Allow"
Resource = "*"
},
]
})
}

参考:

ビジュアルデザインエクスペリエンスを使用する

AWS Systems Manager Automation のページから Create automation ボタンを押します。 aws-system-manager-autometion-create-runbook-button

ビジュアルデザインエクスペリエンスのページ上部でランブックの名前を編集できます。今回は ExampleAutomation とします。
aws-system-manager-autometion-visual-design-experience

{} コード を選択し、以下のコードを貼り付けます。やっていることは単純で、aws:executeAwsApiDescribeInstances を実行し、その結果を aws:executeScript で受け取るだけです。

schemaVersion: '0.3'
description: ''
parameters:
InstanceIds:
type: StringList
description: (Required) EC2 Instance(s) to describe
AutomationAssumeRole:
type: String
description: The ARN of the role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to run this runbook.
allowedPattern: ^arn:aws(-cn|-us-gov)?:iam::\d{12}:role\/[\w+=,.@_\/-]+|^$
assumeRole: '{{ AutomationAssumeRole }}'
mainSteps:
- name: describeInstance
action: aws:executeAwsApi
nextStep: RunScript
isEnd: false
onFailure: Abort
inputs:
Service: ec2
Api: DescribeInstances
InstanceIds: '{{ InstanceIds }}'
outputs:
- Type: String
Name: InstanceId
Selector: $..InstanceId
- Type: String
Name: LaunchTime
Selector: $..LaunchTime
- Type: MapList
Name: Tags
Selector: $.Reservations[0].Instances[0].Tags
- name: RunScript
action: aws:executeScript
isEnd: true
onFailure: Abort
inputs:
Runtime: python3.11
Handler: script_handler
Script: |-
def script_handler(events, context):
data = {
"InstanceId": events["InstanceId"],
"LaunchTime": events["LaunchTime"],
"Tags": events["Tags"]
}
return data
InputPayload:
InstanceId: '{{ describeInstance.InstanceId }}'
LaunchTime: '{{ describeInstance.LaunchTime }}'
Tags: '{{ describeInstance.Tags }}'
outputs:
- Type: String
Name: InstanceId
Selector: $.Payload.InstanceId
- Type: String
Name: LaunchTime
Selector: $.Payload.LaunchTime
- Type: MapList
Name: Tags
Selector: $.Payload.Tags

ランブックを実行する

AWS System Manager Automation のページから Execute automation ボタンを押します。 aws-system-manager-autometion-top-page-2

Owned by me のタブから ExampleAutomation を選択します。 aws-system-manager-autometion-select-runbook

Systems Manager のドキュメントページに飛ばされるので、オートメーションを実行する ボタンを押します。 aws-system-manager-document-execute-automation

Rate control を選択し、Environment: sandbox のタグを付いているインスタンスを対象にします。 example-automation-runbook-rate-control-1 example-automation-runbook-rate-control-2

AssumeRole の設定を忘れずに行います。 example-automation-runbook-rate-control-3

Execute ボタンを押すと、以下のような画面になります。 custom-runbook-detail

CloudWatch Logsを確認する

CloudWatch Logs のロググループに記録されるログは、aws:executeScript かつ、その outputs です。(スクリプト内でprintを実行してもログに記録されませんでした)

custom-runbook-cloudwatch-logs

Systems Manager は、aws:executeScript アクションを使用しないドキュメント用にはロググループまたはログストリームを作成しません。ドキュメントが aws:executeScript を使用する場合、CloudWatch Logs に送信される出力は、それらのアクションにのみ関係します。

デバッグ目的で使用したい場合は、スクリプト内で outputs を定義する必要がありそうです。

デバッグおよびトラブルシューティングの目的で CloudWatch Logs のロググループに保存された aws:executeScript アクション出力を使用できます。

引用:CloudWatch Logs を使用した自動アクション出力のログ記録 - AWS Documentation