SSM Automation、1年本番運用して気づいた設計の勘どころ
「後でやる」を繰り返した手作業オペミスをSSM Automationで潰すことになって1年。Runbook設計・IAM・IaC連携で実際にハマった失敗談と、そこから得た実践知見を正直に書きます。
SSM Automationと向き合うことになったきっかけ
うちのチームがSystems Manager Automation(以下、SSM Automation)を本格的に使い始めたのは、インシデント対応の自動化がきっかけだった。2024年末ごろ、EC2の定期パッチ適用が手作業で行われていて、オペミスが続発していた。「Runbookを書けばよい」という話が出るたびに誰かが「後でやる」とSlackに流して、そのまま忘れる……という負のループ。
転機はインシデント対応の改善に取り組んでいた時期と重なって、「手作業インシデントをシステムで潰そう」という機運が高まったこと。その流れでSSM Automationを本気で設計し直すことになった。今から振り返ると、最初の設計はかなり甘かったと思う。
1年間本番で運用してわかった設計の勘どころを、失敗談も交えて書く。正直まだ改善中の部分もあるけど、参考になれば。
SSM Automationの基本アーキテクチャと2026年時点の機能
まず、現時点の構成を整理しておく。2026年現在、SSM Automationは単なるRunbook実行エンジンにとどまらず、EventBridgeトリガー・OpsCenter統合・Change Calendarとの連携など、かなり「運用基盤」としての機能が充実してきた。
graph TB
subgraph Control["管理アカウント"]
EB[EventBridge Scheduler]
CW[CloudWatch Alarms]
CC[Change Calendar]
OC[OpsCenter]
end
subgraph SSM["SSM Automation基盤"]
direction TB
DOC[Automation Document\n(Runbook)]
EXE[Automation Execution]
LOG[Execution Logs]
end
subgraph TargetVPC["対象VPC (AZ-a / AZ-c)"]
subgraph AZa["AZ-a"]
EC2a[EC2 Instance\n(Private Subnet)]
end
subgraph AZc["AZ-c"]
EC2c[EC2 Instance\n(Private Subnet)]
end
RDS[(RDS Aurora\nCluster)]
end
subgraph Endpoints["VPCエンドポイント"]
EP_SSM[com.amazonaws.\nssm]
EP_EC2MSG[com.amazonaws.\nec2messages]
EP_SSMMSG[com.amazonaws.\nssmmessages]
end
subgraph Logging["ログ・監査"]
S3L[S3 Bucket\n(Execution Logs)]
CWL[CloudWatch Logs]
CT[CloudTrail]
end
EB -->|スケジュール起動| EXE
CW -->|アラーム起動| EXE
CC -->|メンテナンスウィンドウ確認| EXE
OC -->|OpsItem連携| EXE
EXE --> DOC
DOC --> EC2a
DOC --> EC2c
DOC --> RDS
EC2a <--> EP_SSM
EC2c <--> EP_SSM
EP_SSM <--> Endpoints
EXE --> LOG
LOG --> S3L
LOG --> CWL
EXE --> CT
この構成で痛感したのが、VPCエンドポイントの設定漏れが一番多いトラブル原因ということ。Privateサブネットにいるインスタンスに対してSSM経由で操作しようとして「エージェントが応答しない」状態になったことが何度かある。com.amazonaws.region.ssm、ec2messages、ssmmessages の3つが揃っていないと動かないので、Checklistに入れておくのを強くすすめる。
Runbook設計で失敗した話と、その後の改善パターン
最初期に作ったRunbookを今見ると、正直しんどくなる。すべての処理を1つのDocumentに詰め込んで、エラーハンドリングも「失敗したら終了」くらいしか考えていなかった。
やらかしパターン1: モノリシックRunbook地獄
最初に作った「EC2パッチ適用Runbook」は1ファイルに20ステップ以上入れていた。途中でコケると最初からやり直しになる仕様だったため、14ステップ目で失敗するたびに13ステップ分の処理が無駄になる。これが週に何度も起きるので、地味にじわじわ辛かった。
改善後は単機能Runbookの組み合わせに切り替えた。
# runbook-patch-ec2.yml (CDKで生成するSSM Document)
description: 'EC2 Patch Application Orchestrator'
schemaVersion: '0.3'
parameters:
InstanceIds:
type: StringList
description: 'Target EC2 instance IDs'
SnapshotBeforePatch:
type: String
default: 'true'
allowedValues:
- 'true'
- 'false'
mainSteps:
- name: checkChangeCalendar
action: aws:assertAwsResourceProperty
onFailure: Abort
inputs:
Service: ssm
Api: GetCalendarState
CalendarNames:
- 'arn:aws:ssm:ap-northeast-1:123456789012:document/MaintenanceCalendar'
PropertySelector: '$.State'
DesiredValues:
- OPEN
- name: createSnapshotIfRequired
action: aws:executeAutomation
onFailure: Abort
inputs:
DocumentName: 'MyOrg-CreateEBSSnapshot'
Parameters:
InstanceIds: '{{ InstanceIds }}'
nextStep: runPatchBaseline
- name: runPatchBaseline
action: aws:runCommand
onFailure: step:notifyFailure
inputs:
DocumentName: AWS-RunPatchBaseline
InstanceIds: '{{ InstanceIds }}'
Parameters:
Operation: Install
RebootOption: RebootIfNeeded
- name: verifyPatchCompliance
action: aws:executeAutomation
inputs:
DocumentName: 'MyOrg-VerifyPatchCompliance'
Parameters:
InstanceIds: '{{ InstanceIds }}'
- name: notifyFailure
action: aws:executeAwsApi
inputs:
Service: sns
Api: Publish
TopicArn: 'arn:aws:sns:ap-northeast-1:123456789012:ops-alert'
Message: 'Patch automation failed. Check execution: {{ automation:EXECUTION_ID }}'
isEnd: true
onFailure: step:notifyFailure でエラー時のフォールバックステップを指定できるのが地味に便利で、これを知らずに最初の半年を過ごしていたのが本当に悔やまれる。ドキュメントのどこかに書いてあったんだろうけど、見落としていた。
やらかしパターン2: IAMロールの設計が甘すぎた
Runbookに使うIAMロールに最初「AdministratorAccess」を雑につけていた。さすがにひどい。CDK NagやAspectsでセキュリティ検証を導入したときに全部指摘されて、穴があったら入りたい気持ちだった。
改善後のIAMロール設計はこんな感じ:
// CDK でRunbook用IAMロールを定義
const automationRole = new iam.Role(this, 'SSMAutomationRole', {
assumedBy: new iam.ServicePrincipal('ssm.amazonaws.com'),
description: 'Role for SSM Automation Runbooks',
roleName: 'SSMAutomationExecutionRole',
});
// 最小権限原則: Patchに必要なものだけ
automationRole.addToPolicy(new iam.PolicyStatement({
sid: 'AllowEC2Operations',
actions: [
'ec2:CreateSnapshot',
'ec2:DescribeInstances',
'ec2:DescribeSnapshots',
],
resources: ['*'],
conditions: {
StringEquals: {
'aws:RequestedRegion': 'ap-northeast-1',
},
},
}));
automationRole.addToPolicy(new iam.PolicyStatement({
sid: 'AllowSSMRunCommand',
actions: [
'ssm:SendCommand',
'ssm:GetCommandInvocation',
'ssm:ListCommandInvocations',
],
resources: [
`arn:aws:ec2:*:${this.account}:instance/*`,
'arn:aws:ssm:*:*:document/AWS-RunPatchBaseline',
`arn:aws:ssm:*:${this.account}:document/MyOrg-*`,
],
}));
automationRole.addToPolicy(new iam.PolicyStatement({
sid: 'AllowSNSPublish',
actions: ['sns:Publish'],
resources: [`arn:aws:sns:ap-northeast-1:${this.account}:ops-alert`],
}));
aws:RequestedRegion のConditionはAWS Organizationsと組み合わせると特に有効で、意図しないリージョンへの操作を防げる。Organizations SCPとの組み合わせ方はこちらの記事も参考になるかもしれない。
IaCとの組み合わせで変わったこと
SSM Automationのドキュメントをマネジメントコンソールで手作業で作っていた時期があって、あの頃は本当に「再現性がない」「誰が何を変えたかわからない」状態だった。CDKでDocument自体をコード管理するようにしてから、だいぶ改善した。
import * as ssm from 'aws-cdk-lib/aws-ssm';
import * as fs from 'fs';
import * as yaml from 'js-yaml';
// RunbookをYAMLファイルとして管理し、CDKで登録
const patchRunbookContent = yaml.dump(
yaml.load(fs.readFileSync('./runbooks/patch-ec2.yml', 'utf8'))
);
const patchRunbook = new ssm.CfnDocument(this, 'PatchRunbook', {
name: 'MyOrg-PatchEC2Instances',
documentType: 'Automation',
documentFormat: 'YAML',
content: patchRunbookContent,
updateMethod: 'NewVersion', // 大事。Replace だとダウンタイムが生じることがある
tags: [
{ key: 'ManagedBy', value: 'CDK' },
{ key: 'Team', value: 'Platform' },
{ key: 'Environment', value: 'Production' },
],
});
// EventBridge SchedulerでRunbookを定期実行
const schedulerRole = new iam.Role(this, 'SchedulerRole', {
assumedBy: new iam.ServicePrincipal('scheduler.amazonaws.com'),
});
schedulerRole.addToPolicy(new iam.PolicyStatement({
actions: ['ssm:StartAutomationExecution'],
resources: [
`arn:aws:ssm:ap-northeast-1:${this.account}:automation-definition/MyOrg-PatchEC2Instances:*`,
],
}));
const patchSchedule = new scheduler.CfnSchedule(this, 'PatchSchedule', {
name: 'weekly-patch-schedule',
scheduleExpression: 'cron(0 2 ? * SUN *)', // 毎週日曜2時
flexibleTimeWindow: { mode: 'FLEXIBLE', maximumWindowInMinutes: 30 },
target: {
arn: 'arn:aws:ssm:ap-northeast-1::automation-definition/MyOrg-PatchEC2Instances',
roleArn: schedulerRole.roleArn,
input: JSON.stringify({
InstanceIds: ['i-xxxxxxxxxxxx', 'i-yyyyyyyyyyyy'],
SnapshotBeforePatch: 'true',
}),
},
});
updateMethod: 'NewVersion' の設定は地味に重要で、ReplaceにするとRunbook実行中にデプロイが走ったとき実行が中断されることがある。これも最初は知らなくてやらかした。
実行結果のログをS3に落として、Athenaでクエリできるようにもしている。
-- Athena: 過去30日間のAutomation実行失敗率を確認
SELECT
date_trunc('day', from_iso8601_timestamp(executionStartTime)) AS execution_date,
documentName,
COUNT(*) AS total_executions,
SUM(CASE WHEN status = 'Failed' THEN 1 ELSE 0 END) AS failed_count,
ROUND(
100.0 * SUM(CASE WHEN status = 'Failed' THEN 1 ELSE 0 END) / COUNT(*),
2
) AS failure_rate_pct
FROM ssm_automation_logs
WHERE
from_iso8601_timestamp(executionStartTime) >= current_date - interval '30' day
GROUP BY 1, 2
ORDER BY 1 DESC, failure_rate_pct DESC;
このクエリで月次レビューするようにしたら、特定のRunbookが週に2〜3回コケていることが発見されて、原因追跡につながった。地味だけど、個人的にはこのダッシュボードが一番費用対効果高かった。
1年運用してわかったベストプラクティスと、正直まだ悩んでいること
実測値:導入前後の比較
数字で見ると劇的に見えるけど、正直最初の3ヶ月は導入コストが高くて「本当に良くなってるのか?」と疑問に思っていた時期もある。Runbook設計を何度も書き直したり、IAMトラブルシューティングで丸1日潰したこともあった。それでも1年続けたら、こうなった。
xychart-beta
title "SSM Automation導入前後の比較(月次平均)"
x-axis ["手作業工数(h)", "インシデント件数", "パッチ適用失敗数", "対応時間(h)"]
y-axis "値" 0 --> 80
bar [68, 12, 8, 45]
bar [14, 3, 1, 8]
※ 左が導入前、右が導入後(約1年後の平均値)
設計原則として落ち着いたもの
1. Runbookは単機能に保つ
1つのDocumentは1つの責務。パッチ適用、スナップショット作成、コンプライアンス確認はそれぞれ別Runbook。aws:executeAutomationでオーケストレーションする。最初はまとめたくなるけど、後悔するので我慢。
2. Change Calendarを必ず確認ステップに入れる 本番環境へのRunbook実行は必ずChange Calendarの状態確認ステップを先頭に置く。「誰かが手動でRunbookをトリガーした結果、リリースフリーズ期間中にパッチが当たった」という事故が1回あって、それ以来徹底している。
3. 実行ロールはRunbookごとに分ける
最初は全RunbookをまとめたIAMロールで済ませていたけど、最小権限の観点から1Runbook=1Roleに変えた。CDKでgrantXxx()メソッドを使うと楽に書ける。
4. 実行結果は必ずS3 + CloudWatch Logsに二重保存 SSM Automationのコンソールは過去90日分しか実行履歴が残らない。監査目的でS3とCloudWatch Logsの両方に出力する設定を入れると安心できる。
ツール比較:SSM Automation vs 他のアプローチ
どのツールを選ぶかで結構迷ったので、整理しておく。
| 項目 | SSM Automation | AWS Lambda | EventBridge + Step Functions |
|---|---|---|---|
| EC2操作の容易さ | ◎ | △(SSM経由が必要) | △ |
| マネコン視認性 | ◎ | △ | ◎ |
| 実行履歴の管理 | ○(90日) | △(CloudWatch依存) | ◎ |
| パラメータ管理 | ◎(Parameter Store連携) | ○ | ○ |
| 学習コスト | ○ | ◎ | △(高め) |
| マルチアカウント実行 | ◎(built-in) | △ | △ |
| IaC管理のしやすさ | ○(CDK対応) | ◎ | ◎ |
EC2の運用自動化においてはSSM Automationの強みは明確で、特にマルチアカウント実行がネイティブサポートされているのはかなり大きい。逆に複雑なデータ処理や条件分岐が多い場合はStep Functionsの方が向いているかもしれない。ここは正直、好みと用途次第だと思う。
まだ悩んでいること
正直まだ検証中なんだけど、マルチリージョンRunbookの実行順序制御が課題になってきた。東京リージョンと大阪リージョンで並列実行すると、ログの突き合わせが面倒で、今は手動でクエリを書いて確認している。EventBridge Pipesでうまく連携できないか試しているところ。
あとはTerraformとの共存も議論になっている。うちのチームはメインのインフラはTerraform、SSM DocumentはCDK、という微妙に分かれた構成で、統一されていないのが個人的にずっと気持ち悪い。皆さんのチームはどうしてます?
まとめ
1年間SSM Automationを本番で使い続けて、特に効果があったのはこの5点だった:
- RunbookはYAMLでGit管理・CDKでデプロイ — 「誰が何を変えたかわからない」問題が消えた
- 単機能Runbook + オーケストレーション分離 — 再利用性が上がり、デバッグが劇的に楽になった
- IAMロールはRunbookごとに最小権限で — セキュリティ審査で指摘されなくなった
- Change Calendarをガード条件に — リリースフリーズ中の誤操作がゼロになった
- 実行ログのS3 + Athena連携 — 運用問題の早期発見ができるようになった
まだ手作業でパッチ適用をやっているチームには、まず AWS-RunPatchBaseline を使った小さなRunbookを1つ作って、EventBridgeスケジューラーでトリガーするところから始めるのをおすすめしたい。IaC管理は後からでもキャッチアップできるので、「動くRunbookを1本作る」ことを優先した方がモチベーションが続く。
SSM Automationは地味だけど、運用自動化の核になるサービスだと思っている。まだ使ったことない方は、ぜひ試してみてほしい。