Article

AWS Systems Managerで EC2インスタンスを一元管理

高田晃太郎
AWS Systems Managerで EC2インスタンスを一元管理

ゼロトラストなサーバ運用は、もはや選択ではなく前提です。とはいえ、既存手順やツールが足かせになり、踏み台やSSH前提の運用から脱却できない組織は少なくありません。AWS Systems Manager(以下SSM)は、エージェント(SSM Agent)とIAM(認証・認可)、CloudWatchやKMS(ログ・暗号化)とを組み合わせ、OSレベルの操作からパッチ適用、構成管理、自動化、可観測性までを一体で扱える運用基盤です。サーバ台数が増えるほど属人化のコストは膨らみますが、アクセスの統制、構成変更の標準化、操作記録の一元化ができれば、運用は組織のスケールに耐えるようになります。

なぜ今、SSMでEC2を一元管理するのか

SSMの価値は、単なる機能の寄せ集めではありません。IAMで人物・権限・行為をひも付け、Session Managerで踏み台なしのゼロトラストなリモート操作を提供し⁵、Run CommandとState Managerで変更を宣言的に適用、InventoryとComplianceで結果を継続的に可視化します。境界型のSSH/RDPに依存しない設計は、ポート解放、踏み台、キーローテーション、操作ログ不足といった古い負債をまとめて解消します⁵⁹。セッション入出力はCloudWatch LogsやS3に保存され、KMSで暗号化されるため、再現性と追跡可能性を担保できます³⁴。さらに、パッチ適用や再起動、アプリの段階的更新、構成ドリフト検出までを単一の制御面で扱えるため、ツール分断による責任の曖昧さを最小化できます。

導入時に語られる障壁は、既存運用の置き換えコストとネットワーク(プロキシ含む)の要件です。SSMはVPCエンドポイント(PrivateLink)により完全閉域でも動作し、ssm・ssmmessages・ec2messages・s3への到達性が確保できればインターネット経由を不要にできます²。標準AMIにはSSM Agentがプリインストールされており、その他のOSもパッケージを展開するだけで管理対象に加えられます。費用面では、EC2の管理に関する多くの機能が追加料金なしで使え⁶、Parameter Storeの高度パラメータやハイブリッド運用のAdvanced Instancesなど一部のみが従量課金です⁷。一般に、これらの組み合わせにより、手作業の削減や作業の再現性向上が期待できます。

接続・変更・記録を一体化する設計思想

SSMはアクセス(誰が触るか)と変更(何をどう変えるか)と記録(何が起きたか)を、IAMとKMSという同じ信頼基盤に集約します。人の対話セッションでもAutomationドキュメントの実行でも、同等のガバナンスと監査証跡が残せます¹⁰。従来の「SSHしてシェルを叩く」前提では、許可・実行・証跡が分断されがちでした。SSMはこの分断を技術的に解消します⁹。

導入要件と最小構成:IAM、VPC、Agent、ログ

最小構成はシンプルです。EC2にアタッチするインスタンスプロファイル(IAMロール)、閉域化するならVPCエンドポイント、SSM Agentの常駐、そしてセッション・コマンド・オートメーションのログ出力先。この四つを確実に揃えます。プロキシがある環境では、Agent側のプロキシ設定を合わせるだけで動作します。SSMはタグを一次キーのように扱えるため、管理対象のスコープ定義にタグ戦略を先に決めるのがコツです。環境、役割、コンプライアンス基準などをタグに正規化すると、Run CommandやState Managerのターゲティングが安定します。

インスタンスプロファイルの最小権限例

EC2に付与するIAMロールは、まずはAWS管理ポリシーAmazonSSMManagedInstanceCoreで開始し、必要に応じて最小権限のカスタムに絞る方針が扱いやすいです。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:UpdateInstanceInformation",
        "ssmmessages:CreateControlChannel",
        "ssmmessages:CreateDataChannel",
        "ssmmessages:OpenControlChannel",
        "ssmmessages:OpenDataChannel",
        "ec2messages:AcknowledgeMessage",
        "ec2messages:SendReply",
        "ec2messages:GetMessages",
        "ec2messages:DeleteMessage",
        "ec2messages:FailMessage"
      ],
      "Resource": "*"
    }
  ]
}

ログはCloudWatch LogsまたはS3に集約し、KMSキーで暗号化します。これは監査の必須要件であると同時に、障害時の再現性確保の要です。Session Managerは入出力全体の記録を有効化し、プレーンテキストの履歴だけに頼らない設定にします³⁴。

VPCエンドポイントと閉域構成

閉域構成ではInterface型エンドポイントをssm、ssmmessages、ec2messagesに対して作成し、S3はGateway型エンドポイントでエージェントの更新やドキュメント取得を行います²。オンプレミスや別アカウントからの利用が必要なら、PrivateLinkのエンドポイントサービス越しに運用用VPCをハブ化する設計も有効です。これにより境界ファイアウォールや踏み台の運用負荷が減り、「インバウンド0」の原則に近づけます⁸。

日常運用シナリオと実装:パッチ、構成、セッション、自動化

現場の価値は、反復作業をどれだけ安全かつ自動で回せるかで決まります。パッチ適用のカバレッジ、構成ドリフトの修復、オンデマンドの対話セッション、再現可能なRunbookという四つの場面に分けて、実装を示します。

パッチ適用のスキャンと適用

まずはスキャンから始めて、影響範囲と所要時間を把握するのが安全です。タグで対象を絞り、並列度と失敗許容を明示したRun Commandを発行します¹²。

import boto3
from botocore.exceptions import ClientError

ssm = boto3.client('ssm', region_name='ap-northeast-1')

def start_patch_scan(tag_key: str, tag_value: str) -> str:
    try:
        resp = ssm.send_command(
            Targets=[{"Key": f"tag:{tag_key}", "Values": [tag_value]}],
            DocumentName="AWS-RunPatchBaseline",
            Parameters={"Operation": ["Scan"]},
            MaxConcurrency="10%",
            MaxErrors="1%",
            TimeoutSeconds=3600
        )
        return resp['Command']['CommandId']
    except ClientError as e:
        raise RuntimeError(f"Failed to start patch scan: {e}")

if __name__ == "__main__":
    cmd_id = start_patch_scan("Env", "prod")
    print(f"Started patch scan: {cmd_id}")

適用も同じドキュメントでOperationをInstallに切り替えるだけです。メンテナンスウィンドウやState ManagerのAssociationを使えば、期日や順序を宣言的に管理できます。可用性要件が厳しい場合は、ターゲットをアプリ単位で細かくタグ分割し、段階的に波及させます。

構成ドリフトの防止と自動修復

State Managerで、SSM AgentやCloudWatch Agentのバージョン、OS設定、アプリのサービス状態などを継続的に収斂させます。Associationはドキュメントとパラメータの組み合わせで宣言し、タグにマッチしたインスタンスに自動適用されます。例として、特定のLinuxサービスが落ちていれば再起動し、状態を検証するAutomationドキュメントを示します¹¹。

---
schemaVersion: '0.3'
description: Restart and verify critical service
assumeRole: "{{ AutomationAssumeRole }}"
parameters:
  InstanceId:
    type: String
  ServiceName:
    type: String
mainSteps:
  - name: restartService
    action: aws:runCommand
    inputs:
      DocumentName: AWS-RunShellScript
      InstanceIds:
        - "{{ InstanceId }}"
      Parameters:
        commands:
          - "sudo systemctl restart {{ ServiceName }}"
  - name: verify
    action: aws:runCommand
    inputs:
      DocumentName: AWS-RunShellScript
      InstanceIds:
        - "{{ InstanceId }}"
      Parameters:
        commands:
          - "systemctl is-active {{ ServiceName }}"

このAutomationをCloudWatchアラームやEventBridgeのルールから呼び出せば、検知から修復までを自動でつなげられます¹¹。結果はOpsCenterにチケット化して、事後レビューに活用します。

SSHレス運用のセッション管理

Session Managerは短時間でセッションを開始でき、ポート開放や鍵配布が不要です⁵。アクセスはIAMロールで制御され、入出力はCloudWatch LogsやS3に保存されます³⁴。AWS IAM Identity Center(SSO)と連携すれば、入社・異動・退職に合わせた権限ライフサイクル管理が容易になります。CLIからの開始例は次の通りです。

aws ssm start-session \
  --region ap-northeast-1 \
  --target i-0123456789abcdef0 \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters host="127.0.0.1",portNumber="5432",localPortNumber="15432"

ポートフォワーディングを使えば、DBや内部HTTPのトラブルシュートにも踏み台なしで到達できます。セッションの通信はssmmessagesチャネル上で管理されるため、ネットワーク側の負担も小さくできます。

タグ駆動の一括変更(Run Command)

タグを軸にしたRun Commandは、フリートへの安全なファンアウトを可能にします。Pythonからの実行では、並列度・失敗許容・出力先を明示し、完了待ちと例外処理を実装します。

import boto3
import time
from botocore.exceptions import ClientError

ssm = boto3.client('ssm', region_name='ap-northeast-1')


def run_shell(tag_key: str, tag_value: str, commands: list[str]) -> None:
    try:
        resp = ssm.send_command(
            Targets=[{"Key": f"tag:{tag_key}", "Values": [tag_value]}],
            DocumentName="AWS-RunShellScript",
            Parameters={"commands": commands},
            CloudWatchOutputConfig={"CloudWatchOutputEnabled": True},
            MaxConcurrency="25%",
            MaxErrors="2%"
        )
        cmd_id = resp['Command']['CommandId']
    except ClientError as e:
        raise RuntimeError(f"send_command failed: {e}")

    while True:
        time.sleep(5)
        inv = ssm.list_command_invocations(CommandId=cmd_id, Details=True)
        statuses = {i['InstanceId']: i['Status'] for i in inv.get('CommandInvocations', [])}
        if statuses and all(s in ("Success", "Cancelled", "Failed", "TimedOut") for s in statuses.values()):
            failures = [k for k, v in statuses.items() if v != "Success"]
            if failures:
                raise RuntimeError(f"Failures on {failures}")
            print("All targets succeeded")
            return

if __name__ == "__main__":
    run_shell("Role", "web", ["sudo systemctl reload nginx"])

この形に合わせて、アプリのコンフィグ差し替えやローリング再起動を実行すれば、ログと監査証跡を保ったまま広範囲の変更を安全に適用できます。

継続的な健全性の強制(LambdaとInventory)

InventoryとComplianceのデータは、タグやメトリクスと突き合わせて自動是正に使えます。非準拠インスタンスにタグを付けて隔離し、修復をトリガするLambdaの例を示します。

import os
import json
import boto3
from botocore.exceptions import ClientError

essm = boto3.client('ssm')
ec2 = boto3.client('ec2')

NON_COMPLIANT_TAG = os.getenv('NON_COMPLIANT_TAG', 'Compliance')


def lambda_handler(event, context):
    try:
        result = essm.list_resource_compliance_summaries(ComplianceType='Patch')
        non_compliant = [s['ResourceId'] for s in result.get('ResourceComplianceSummaryItems', [])
                         if s['Status'] != 'COMPLIANT']
        if not non_compliant:
            return {"ok": True, "message": "all compliant"}
        ec2.create_tags(Resources=non_compliant, Tags=[{"Key": NON_COMPLIANT_TAG, "Value": "NonCompliant"}])
        # Optionally trigger Automation here
        return {"ok": True, "count": len(non_compliant)}
    except ClientError as e:
        return {"ok": False, "error": str(e)}

このように、ダッシュボードの可視化に留めず行動へ結びつけることで、運用品質は着実に向上します。

セキュリティ、可観測性、コスト、ROIを実務で語る

セッションの可視化、操作の再現性、ゼロトラストなリモート操作という三点は、SSM導入の中心目的です。Session Managerは入出力の完全記録をCloudWatch LogsやS3に残し、KMSキーで保護します³⁴。アクセスはAWS IAM Identity Centerで統合すれば、認証・認可の堅牢性がそのまま運用強度になります。Parameter Storeはアプリ設定の集中管理に便利ですが、秘匿性が高い値はSecrets Managerに置き、SSMドキュメントから参照する設計が整理されています。

コストは読みやすく、EC2管理の主要機能は追加料金なしで使えます⁶。Parameter Storeの標準パラメータには無料枠があり⁷、課金は高度パラメータやハイブリッドのAdvanced Instances、OpsCenterの一部高度機能などに限られます⁶。踏み台や商用リモートアクセスツールのライセンスが不要になり⁵、監査・証跡対応に費やす工数も抑えられる可能性があります。導入までの期間は、標準AMIとタグ戦略が整っていれば、まずは安全な最小構成に数日〜数週間で到達するのが一般的な目安です。

計測の進め方と運用指標(評価環境の設計例)

導入効果は自組織の環境で計測してこそ意味があります。例えば以下のような指標を評価環境(ap-northeast-1、t系インスタンス、Amazon Linux 2、VPCエンドポイント有など)で測定すると、ボトルネックを具体化できます。

  • セッション確立時間(Session Manager vs SSH)
  • SSM AgentのアイドルCPU使用率とRSSメモリ
  • Run Commandのファンアウト完了時間(タグ選択で数百台規模)
  • パッチスキャン/適用の成功率(MaxErrors設定ごと)
  • ログ取得の完全性(CloudWatch LogsとS3の二重保管) 計測結果はダッシュボード化し、SLO/KPI(例:セッション確立P50/P95、Run Command完了P95、コンプライアンス準拠率)として運用改善に反映します。こうした定量観測は、セキュリティレビューや監査対応時にも裏付けとして有効です。

運用リスクの押さえどころ

最大の落とし穴はタグ戦略の不整合です。同一意味のタグキーが複数あったり、自由記述の値が混ざると、ターゲティングが不安定になり、想定外のインスタンスへ変更が及ぶリスクが増します。タグ定義書を用意し、AMI作成時にタグ検証をCIで自動化すると、事故は目に見えて減ります。次に重要なのがロールの境界で、Session Managerの開始・ポートフォワーディング・ドキュメント実行を分けて許可し、原則禁止から必要最小限に開く方針にします⁸。VPCエンドポイントの可用性も監視し、ネットワーク障害時にセッションを張れないリスクを踏まえて、緊急時の現地オペレーション手段は運用設計に残しておきます⁸。

テンプレート化で広げる:IaCとドキュメント

導入をテンプレート化すれば、アカウントやリージョンが増えても再現性が担保されます。CloudFormationやTerraformで、VPCエンドポイント、CloudWatch Logs設定、KMSキー、SSMドキュメント、State Manager Associationまでを定義すれば、監査やセキュリティレビューも簡素化されます。参考のCloudFormationスニペットを示します。

Resources:
  SSMLogs:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: "/aws/ssm/session"
      RetentionInDays: 30
  SessionKms:
    Type: AWS::KMS::Key
    Properties:
      EnableKeyRotation: true
  SessionPreferences:
    Type: AWS::SSM::Document
    Properties:
      DocumentType: Session
      Content:
        schemaVersion: '1.0'
        sessionType: Standard_Stream
        inputs:
          s3BucketName: my-ops-bucket
          cloudWatchLogGroupName: /aws/ssm/session
          kmsKeyId: !Ref SessionKms

このテンプレートをアカウント初期化の標準手順に組み込めば、チーム間の差異を減らし、改善サイクルを加速できます。

内部ガイドの整備と移行プラン

移行は、新規サーバからSSM標準を徹底し、既存はパッチウィンドウやアプリ更新に合わせて段階的に切り替えるのが現実的です。開発・SRE・セキュリティの三者で合意した短い運用ガイドを作り、原則としてSSM経由以外の操作を禁止すると、例外管理のコストが下がります。ガイドは社内ポータルにまとめ、CLIとコンソールの両方で同等の手順を提供すると習熟が早まります。必要に応じて、/devops/zero-trust-sshless、/devops/patching-baseline-design、/devops/tagging-strategy-for-aws なども参照してください。

まとめ:アクセス統制・変更管理・証跡を標準化し、運用を組織の力に

システムを動かすのは人ですが、品質を支えるのは仕組みです。AWS Systems Managerは、アクセス統制と構成変更と操作記録を一体化し、運用を個人技から組織の能力へ引き上げます。SSHを閉じることに不安があるなら、まずは一部ノードと限定業務から始め、セッション記録とRunbookの価値をチームで体感してください。タグに基づくターゲティング、State Managerによる継続的収斂、CloudWatchやS3への証跡統合が回り始めれば、日々の雑務が減り、本来注力すべき改善や信頼性向上に時間を振り向けられるはずです。今日取り組める最初の一歩は、インスタンスプロファイルの整備とSession Managerのログ有効化です。次のアプリ更新サイクルで、パッチ適用をRun Commandに置き換えてみませんか。自動化の第一歩が、チームの時間と安全性を取り戻します。

参考文献

  • [2] AWS Docs: Setting up VPC endpoints for Systems Manager (PrivateLink)
  • [3] AWS Docs: Logging session data to Amazon CloudWatch Logs
  • [4] AWS Docs: Logging session data to Amazon S3
  • [5] AWS Blog: Replacing a bastion host with Amazon EC2 Systems Manager
  • [6] AWS Systems Manager – Pricing
  • [7] AWS Systems Manager – Parameter Store Pricing(Standard パラメータ無料枠/Advanced課金)
  • [8] AWS Docs: AWS Systems Manager security best practices
  • [9] AWS Blog: Replacing a bastion host…(SSH activity isn’t natively logged の解説箇所)
  • [10] AWS Blog: Replacing a bastion host…(The principal who executed it の解説箇所)
  • [11] AWS Blog: Reducing configuration drift with State Manager and Amazon CloudWatch Events
  • [12] Classmethod: Systems Manager Patch ManagerでLinuxのパッチ運用を行う