管理アカウントからSSM Automationを使用して、複数のメンバーアカウントのEC2インスタンスに特定のコマンドを実行してみた

管理アカウントからSSM Automationを使用して、複数のメンバーアカウントのEC2インスタンスに特定のコマンドを実行してみた

Clock Icon2025.01.20

はじめに

自作のコマンドをオートメーションランブックとして作成し、管理アカウントからSSMオートメーションを使用して複数のメンバーアカウントのEC2インスタンスで実行する方法をまとめました。

automation-multi-region-and-multi-account (1)
引用元

SSMドキュメントにはいくつかのタイプがあり、その中でSSMオートメーションで利用するドキュメントは「オートメーションランブック」として分類されています。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/documents.html

そのため、本記事では、SSMオートメーションで利用するSSMドキュメントを「オートメーションランブック」と呼びます。

前提条件

  • 対象のEC2インスタンスがマネージドインスタンス化されていること
    • 今回は、OSがLinuxのEC2インスタンスを用意しています。

IAMロール作成

管理アカウントからメンバーアカウントに対して、SSMオートメーションを実行するには、管理アカウントとメンバーアカウントの両方にIAMロールを作成する必要があります。

以下のドキュメントには、2つのCloudFormationテンプレートが用意されています。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/running-automations-multiple-accounts-regions.html#setup-management-account-iam-roles

AWS-SystemsManager-AutomationAdministrationRole (org).yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Configure the AWS-SystemsManager-AutomationAdministrationRole to allow multi-region and multi-account automations for your organization."
Resources:
  MasterAccountRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - ssm.amazonaws.com
          Action: sts:AssumeRole
          Condition:
            ArnLike:
              aws:SourceArn: 
                Fn::Sub: arn:${AWS::Partition}:ssm:*:*:automation-execution/*
      Path: "/"
      RoleName: AWS-SystemsManager-AutomationAdministrationRole
      Policies:
        - PolicyName: 'AssumeRole-AWSSystemsManagerAutomationExecutionRole'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Action: sts:AssumeRole
              Resource: 
                Fn::Sub: arn:${AWS::Partition}:iam::*:role/AWS-SystemsManager-AutomationExecutionRole
            - Effect: Allow
              Action: 
              - organizations:ListAccountsForParent
              - organizations:ListChildren
              Resource: '*'
AWS-SystemsManager-AutomationExecutionRole (org).yml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Configure the AWS-SystemsManager-AutomationExecutionRole.'
Parameters:
  AdminAccountId:
    Type: String
    Description: "The ID of the primary account from which automations will be initiated for your organization."
    MaxLength: 12
    MinLength: 12
  OrganizationID:
    Type: String
    Description: AWS Organizations ID.
    AllowedPattern: "^o-[a-z0-9]{10,32}$"
Resources:
  AWSSystemsManagerAutomationExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: AWS-SystemsManager-AutomationExecutionRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            AWS:
              Fn::Sub:
              - arn:${AWS::Partition}:iam::${AdminAccountId}:role/AWS-SystemsManager-AutomationAdministrationRole
              - AdminAccountId:
                  Ref: AdminAccountId
          Action: sts:AssumeRole
          Condition:
            StringEquals:
              aws:PrincipalOrgID:
                Ref: OrganizationID
        - Effect: Allow
          Principal:
            Service: ssm.amazonaws.com
          Action:
          - sts:AssumeRole
          Condition:
            StringEquals:
              aws:SourceAccount: 
                Fn::Sub: ${AWS::AccountId}
            ArnLike:
              aws:SourceArn: 
                Fn::Sub: arn:${AWS::Partition}:ssm:*:${AWS::AccountId}:automation-execution/*
      ManagedPolicyArns:
      - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole
      Path: "/"
      Policies:
      - PolicyName: ExecutionPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - resource-groups:ListGroupResources
            - tag:GetResources
            - ec2:DescribeInstances
            Resource: "*"
          - Effect: Allow
            Action:
            - iam:PassRole
            Resource:
              Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:role/AWS-SystemsManager-AutomationExecutionRole

CloudFormationでIAMロール作成

管理アカウントで作成するIAMロールは、以下の設定を使用してCloudFormationスタックを作成します。
作成されるIAMロール名は、AWS-SystemsManager-AutomationAdministrationRoleです。

  • スタック名:AWS-SystemsManager-AutomationAdministrationRole
  • テンプレートの指定:AWS-SystemsManager-AutomationAdministrationRole (org).yml

メンバーアカウントで作成が必要なIAMロールは、以下の設定を使用してCloudFormation StackSetsを作成します。
作成されるIAMロール名は、AWS-SystemsManager-AutomationExecutionRoleです。

  • アクセス許可モデル:サービスマネージドアクセス許可(SERVICE_MANAGED)
  • テンプレートの指定:AWS-SystemsManager-AutomationExecutionRole (org).yml
  • StackSet 名:AWS-SystemsManager-AutomationExecutionRole
  • パラメータ
    • AdminAccountId:管理アカウントのAWSアカウントID
    • OrganizationID :管理アカウントのAWS Organizationsページで確認できる「組織 ID」
  • デプロイターゲット:組織単位 (OU) へのデプロイ
    • AWS OU ID:対象アカウントを含むOU ID
  • リージョンの指定:東京リージョン

オートメーションランブックを作成

管理アカウントで、SSMドキュメントに遷移し、[ドキュメントの作成]から[オートメーション]を選択します。

cm-hirai-screenshot 2025-01-17 17.16.16

以下のコードを貼り付け、check-osという名前でオートメーションランブックを作成します。

check-os
schemaVersion: '0.3'
description: Check OS settings using SSM Automation.
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
  AutomationAssumeRole:
    type: String
    description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
  InstanceId:
    type: String
    description: The ID of the EC2 instance to check.
  CheckHostname:
    type: String
    default: check
    description: Check hostname or skip.
    allowedValues:
      - check
      - skip
  CheckTimezone:
    type: String
    default: check
    description: Check timezone or skip.
    allowedValues:
      - check
      - skip
  CheckLocale:
    type: String
    default: check
    description: Check locale or skip.
    allowedValues:
      - check
      - skip
  CheckUpdate:
    type: String
    default: check
    description: Check yum updates or skip.
    allowedValues:
      - check
      - skip
  CheckNTP:
    type: String
    default: check
    description: Check NTP status or skip.
    allowedValues:
      - check
      - skip
mainSteps:
  - name: CheckHostname
    action: aws:runCommand
    maxAttempts: 1
    timeoutSeconds: 10
    nextStep: CheckTimezone
    isEnd: false
    onFailure: Abort
    inputs:
      DocumentName: AWS-RunShellScript
      Parameters:
        commands:
          - if [[ "{{ CheckHostname }}" == "check" ]]; then hostname; else echo "Skipping hostname check"; fi
      InstanceIds:
        - '{{ InstanceId }}'
    outputs:
      - Name: HostnameOutput
        Selector: $.CommandPlugins[0].Output
        Type: String
  - name: CheckTimezone
    action: aws:runCommand
    maxAttempts: 1
    timeoutSeconds: 10
    nextStep: CheckLocale
    isEnd: false
    onFailure: Abort
    inputs:
      DocumentName: AWS-RunShellScript
      Parameters:
        commands:
          - if [[ "{{ CheckTimezone }}" == "check" ]]; then timedatectl status; else echo "Skipping timezone check"; fi
      InstanceIds:
        - '{{ InstanceId }}'
    outputs:
      - Name: TimezoneOutput
        Selector: $.CommandPlugins[0].Output
        Type: String
  - name: CheckLocale
    action: aws:runCommand
    maxAttempts: 1
    timeoutSeconds: 10
    nextStep: CheckUpdate
    isEnd: false
    onFailure: Abort
    inputs:
      DocumentName: AWS-RunShellScript
      Parameters:
        commands:
          - if [[ "{{ CheckLocale }}" == "check" ]]; then localectl status; else echo "Skipping locale check"; fi
      InstanceIds:
        - '{{ InstanceId }}'
    outputs:
      - Name: LocaleOutput
        Selector: $.CommandPlugins[0].Output
        Type: String
  - name: CheckUpdate
    action: aws:runCommand
    maxAttempts: 1
    timeoutSeconds: 10
    nextStep: CheckNTP
    isEnd: false
    onFailure: Abort
    inputs:
      DocumentName: AWS-RunShellScript
      Parameters:
        commands:
          - if [[ "{{ CheckUpdate }}" == "check" ]]; then yum check-update; else echo "Skipping update check"; fi
      InstanceIds:
        - '{{ InstanceId }}'
    outputs:
      - Name: UpdateOutput
        Selector: $.CommandPlugins[0].Output
        Type: String
  - name: CheckNTP
    action: aws:runCommand
    maxAttempts: 1
    timeoutSeconds: 10
    isEnd: true
    onFailure: Abort
    inputs:
      DocumentName: AWS-RunShellScript
      Parameters:
        commands:
          - if [[ "{{ CheckNTP }}" == "check" ]]; then chronyc sources; else echo "Skipping NTP check"; fi
      InstanceIds:
        - '{{ InstanceId }}'
    outputs:
      - Name: NTPOutput
        Selector: $.CommandPlugins[0].Output
        Type: String

cm-hirai-screenshot 2025-01-22 15.50.39

以下の記事のSSMドキュメント(コマンドドキュメント)を参考にしました。
https://dev.classmethod.jp/articles/check-os-setting-ssm-doc-al2/

タイムアウト

今回作成したオートメーションランブックでは、各ステップごとのコマンド実行に対して、以下のようにタイムアウトを設定しました。

mainSteps:
  - name: CheckHostname
    action: aws:runCommand
    maxAttempts: 1
    timeoutSeconds: 10

cm-hirai-screenshot 2025-01-22 15.51.01

SSMオートメーションの対象EC2インスタンスは、起動中のマネージドインスタンス以外にもアンマネージドインスタンスや停止中のEC2インスタンスも実行対象となります。
タイムアウトを設定しないと、長時間オートメーション実行のステータスが進行中のままになってしまうため、基本的にはタイムアウトは設定した方がよいと思います。

onFailure

各ステップではonFailureAbortに設定しています。
これにより、いずれかのステップが失敗した場合、その時点で対象のEC2インスタンスの実行が「失敗」というステータスで終了します。

一方で、onFailureContinueに設定すると、どこかのステップが失敗しても後続のステップが実行されます。
この場合、全てのステップが終了した時点で、全体の実行ステータスは「成功」となりますが、失敗したステップは個別に「失敗」として記録されます。

onFailure
失敗時にオートメーションを中止するか、続行するか、別のステップに移行するかを示します。このオプションのデフォルト値は中止です。

型: 文字列

有効な値: Abort | Continue | step:step_name

必須: いいえ

cm-hirai-screenshot 2025-01-17 17.18.25
Continueを利用した場合のフロー

SSM Automationを実行

マネジメントコンソール上ではなく、AWS CLIでSSM Automationを実行します。

理由は、ターゲットアカウントを指定する際、OUで指定すると、OU直下のAWSアカウントしか対象にならず、指定したOUの子OU配下に存在するアカウントは対象にならないためです。

cm-hirai-screenshot 2025-01-17 17.27.43
Accounts and organizational units (OUs)

AWS CLIであれば、--target-locations"IncludeChildOrganizationUnits": true,を設定すると子や孫OUも対象になりますが、マネジメントコンソールでは設定できません。

IncludeChildOrganizationUnits
Indicates whether to include child organizational units (OUs) that are children of the targeted OUs. The default is false.
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/APIReference/API_TargetLocation.html

その点が問題なければ、マネジメントコンソール上で実行しても構いません。

実行するEC2インスタンスをタグで指定して実行する場合、AWS CloudShellから以下のコマンドで実行します。

aws ssm start-automation-execution \
    --document-name "check-os" \
    --parameters AutomationAssumeRole=arn:aws:iam::管理アカウントID:role/AWS-SystemsManager-AutomationAdministrationRole,CheckHostname=check,CheckTimezone=check,CheckLocale=check,CheckUpdate=check,CheckNTP=check \
    --target-parameter-name InstanceId \
    --targets Key=tag:ssm-automation,Values=true \
    --target-locations '[{
        "Accounts": ["ou-o2pk-xxxxxxxx"],
        "IncludeChildOrganizationUnits": true,
        "Regions": ["ap-northeast-1"],
        "ExecutionRoleName": "AWS-SystemsManager-AutomationExecutionRole"
    }]'
  • --parameters
    • AutomationAssumeRoleは管理アカウントで作成したIAMロールのARNの指定が必須です。
    • 以下はオートメーションランブックに設定されているパラメータです。
      • CheckHostname=check,CheckTimezone=check,CheckLocale=check,CheckUpdate=check,CheckNTP=check
  • --target-locations
    • Regions:対象リージョンを指定
    • Accounts:アカウントIDかOU IDを指定する
    • ExecutionRoleName:リソースアカウントに存在するIAMロール名を指定
  • --targets
    • タグキー名:ssm-automation
    • タグ値:true

実行後、AutomationExecutionIdが出力されます。

出力結果
{
    "AutomationExecutionId": "xxxxxxxx"
}

オートメーションランブックはデフォルトバージョンで実行されます。
明示的にオートメーションランブックのバージョンを指定したい場合、--document-version "6"のように追加するとよいです。

aws ssm start-automation-execution \
    --document-name "check-os" \
    --document-version "6" \
    --parameters AutomationAssumeRole=arn:aws:iam::管理アカウントID:role/AWS-SystemsManager-AutomationAdministrationRole,CheckHostname=check,CheckTimezone=check,CheckLocale=check,CheckUpdate=check,CheckNTP=check \
    --target-parameter-name InstanceId \
    --targets Key=tag:ssm-automation,Values=true \
    --target-locations '[{
        "Accounts": ["ou-o2pk-xxxxxxxx"],
        "IncludeChildOrganizationUnits": true,
        "Regions": ["ap-northeast-1"],
        "ExecutionRoleName": "AWS-SystemsManager-AutomationExecutionRole"
    }]'

タグを指定せず、対象アカウントの全てのEC2インスタンスを対象にする場合、--targets Key=AWS::EC2::Instance,Values=*と指定します。

aws ssm start-automation-execution \
    --document-name "check-os" \
    --parameters AutomationAssumeRole=arn:aws:iam::管理アカウントID:role/AWS-SystemsManager-AutomationAdministrationRole,CheckHostname=check,CheckTimezone=check,CheckLocale=check,CheckUpdate=check,CheckNTP=check \
    --target-parameter-name InstanceId \
    --targets Key=AWS::EC2::Instance,Values=* \
    --target-locations '[{
        "Accounts": ["ou-o2pk-xxxxxxxx"],
        "IncludeChildOrganizationUnits": true,
        "Regions": ["ap-northeast-1"],
        "ExecutionRoleName": "AWS-SystemsManager-AutomationExecutionRole"
    }]'

マネジメントコンソール上の場合、以下のように設定します。
cm-hirai-screenshot 2025-01-16 14.45.51

実行結果の内容を確認

実行結果(Execution ID)は以下のように作成されます

  • 管理アカウント
    • 親Execution IDは1つ
      • 各メンバーアカウントの実行結果をまとめたもの
  • メンバーアカウント
    • 子Execution IDは2つ以上作成される
      • 1つは、全EC2インスタンスの実行結果をまとめたもの
      • 残りは、各EC2インスタンスごとの実行結果詳細

管理アカウントでSSMオートメーションを実行していますので、メンバーアカウントに親Execution IDは存在しません

管理アカウントでは、実行した対象メンバーアカウントごとにExecuted stepsが作成されます。Step IDを選択します。

cm-hirai-screenshot 2025-01-22 15.49.38
実行詳細ページに遷移しますが、ExecutionIdは、メンバーアカウントのExecutionIdに紐づいているため、以下のスクリーンショットのExecutionIdである8df83893-10ba-4fa6-8e7f-1520667bbc34を選択しても実行結果内容(コマンド実行結果)は確認できません。

cm-hirai-screenshot 2025-01-22 15.51.53

以下のようにエラーになります。

cm-hirai-screenshot 2025-01-22 16.14.36

Automation自体の成功可否は管理アカウントからステータスで確認できますが、コマンド実行結果のOutputsについては管理アカウントから直接確認することはできません。
これらの詳細な情報を確認するには、メンバーアカウントにログインして確認する必要があります。

管理アカウントから確認させる方法は以下の記事をご参照ください

https://dev.classmethod.jp/articles/aws-ssm-automation-multi-account-result-check/

メンバーアカウントにログインし、ExecutionId(例: 8df83893-10ba-4fa6-8e7f-1520667bbc34)を選択して実行詳細ページに移動します。
このページでは、各EC2インスタンスごとに異なるStep execution IDが表示されます。
cm-hirai-screenshot 2025-01-22 15.52.32

次に、確認したいStep execution IDを選択します。
cm-hirai-screenshot 2025-01-22 15.52.53
これにより、オートメーションランブックの各Stepごとに実行されたコマンドの結果を確認できます。StatusPendingは、ステップの実行が開始されていないということです。ステップ4で失敗しているため、ステップ5はPendingになります。
cm-hirai-screenshot 2025-01-22 15.53.05

さらに、特定のStep execution IDを選択すると、そのStepで実行されたコマンドの詳細な結果を確認することができます。
cm-hirai-screenshot 2025-01-22 15.53.25

Windowsでの文字化け対策

OSがWindowsの場合、コマンド実行結果のOutputに文字化けが発生することがあります。これを解決するために、実行するコマンドの前にchcp 65001を追加します。

このコマンドは、コンソールの文字コードをUTF-8に設定します。
UTF-8は、日本語を含む多言語の文字を正しく表示できる文字エンコーディングです。

以下は、オートメーションランブックでの実装例です

  - name: xxxx
    action: aws:runCommand
    inputs:
      DocumentName: AWS-RunPowerShellScript
      Parameters:
        commands:
          - chcp 65001
          - # ここに特定のWindowsコマンドを記述

https://dev.classmethod.jp/articles/tsnote-ssm-runcommand-garbled-char/

管理アカウントのEC2インスタンスもAutomation対象にしたい場合

SSM Automation実行時に管理アカウントのEC2インスタンスも対象に含めたい場合、Root OUを指定すると以下のようなエラーが発生します。

aws ssm start-automation-execution \
    --document-name "check-os" \
    --parameters AutomationAssumeRole=arn:aws:iam::111111111111:role/AWS-SystemsManager-AutomationAdministrationRole,CheckHostname=check,CheckTimezone=check,CheckLocale=check,CheckUpdate=check,CheckNTP=check \
    --target-parameter-name InstanceId \
    --targets Key=tag:ssm-automation,Values=true \
    --target-locations '[{
        "Accounts": ["r-o2pk"],
        "IncludeChildOrganizationUnits": true,
        "Regions": ["ap-northeast-1"],
        "ExecutionRoleName": "AWS-SystemsManager-AutomationExecutionRole"
    }]'

Failure message
The provided role: arn:aws:iam::管理アカウントID:role/AWS-SystemsManager-AutomationExecutionRole can't be assumed. (Service: null; Status Code: 0; Error Code: null; Request ID: null; Proxy: null)

このエラーは、管理アカウントにAWS-SystemsManager-AutomationExecutionRoleが存在しないことが原因です。

管理アカウントにAWS-SystemsManager-AutomationExecutionRoleを作成するため、以下の設定でCloudFormationスタックを作成します。

  • スタック名:AWS-SystemsManager-AutomationExecutionRole-org
  • テンプレートの指定:AWS-SystemsManager-AutomationExecutionRole (org).yml
  • パラメータ
    • AdminAccountId:管理アカウントのAWSアカウントID
    • OrganizationID:管理アカウントのAWS Organizationsページで確認できる「組織 ID」

CloudFormationスタックを作成後、再度以下のコマンドを実行すると成功します。

aws ssm start-automation-execution \
    --document-name "check-os" \
    --parameters AutomationAssumeRole=arn:aws:iam::111111111111:role/AWS-SystemsManager-AutomationAdministrationRole,CheckHostname=check,CheckTimezone=check,CheckLocale=check,CheckUpdate=check,CheckNTP=check \
    --target-parameter-name InstanceId \
    --targets Key=tag:ssm-automation,Values=true \
    --target-locations '[{
        "Accounts": ["r-o2pk"],
        "IncludeChildOrganizationUnits": true,
        "Regions": ["ap-northeast-1"],
        "ExecutionRoleName": "AWS-SystemsManager-AutomationExecutionRole"
    }]'

実行後、以下のようにExecution IDが作成されます。

  • 管理アカウント
    • 親Execution ID:1つ
      • 管理アカウントと各メンバーアカウントの実行結果をまとめたもの
    • 子Execution ID:2つ以上
      • 管理アカウント内の全EC2インスタンスの実行結果をまとめたもの
      • 管理アカウント内の各EC2インスタンスごとの実行結果詳細
  • メンバーアカウント
    • 子Execution ID:2つ以上
      • 1つは、メンバーアカウント内の全EC2インスタンスの実行結果をまとめたもの
      • 残りは、各EC2インスタンスごとの実行結果詳細

これにより、管理アカウントのEC2インスタンスもAutomationの対象に含めることが可能になります。

参考

https://docs.aws.amazon.com/cli/latest/reference/ssm/start-automation-execution.html

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/automation-documents.html

https://dev.classmethod.jp/articles/ssm-automation-cross-account-access/

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy