Amazon Web Services ブログ

Systems Manager Automation Runbook でスクリプトを活用する

お客様は今まで、 AWS Systems Manager Automationドキュメントを使用して、AWS Lambda 関数の呼び出しや Amazon Machine Image (AMI) のコピーなど、AWS インフラストラクチャで実行する一連のアクションを定義してきました。これらのドキュメントは現在 Runbook と呼ばれており、簡単に使用でき、かつ強力です。 aws:executeScript アクションを使用すると、Python と PowerShell を Runbook に直接埋め込むことができます。

aws:executeScript アクションにより、次のことが可能になります。

  • ロジックを格納するためだけに Amazon Elastic Compute Cloud (Amazon EC2) インスタンスなどのリソース – これらは追加で、ネットワーク、AWS Identity and Access Management (IAM)、セキュリティなどの構成が必要になる場合がありますね- を立ち上げる必要がなくなります。
  • ループ、文字列、JSON、エラー処理などのプログラミング ロジックをフルで使用できます。
  • 開発者がすでに慣れている構文を使用して、SDK および PowerShell コマンドレットの呼び出しができます。

このブログポストでは、AWS Config自動修復アクション の一部としてこのアクションを使用する方法の例をいくつか紹介します。まず、aws:executeScript を使用してリソースの情報を取得し、次に Slack と統合し情報を Slack チャネルに送信します。

ソリューションの概要

このブログポストでは、AWS Config の encrypted-volumes ルールを使用します。このルールは、Amazon Elastic Block Store (Amazon EBS) ボリュームをモニターし、暗号化されていないボリュームを見つけます。 暗号化されていないボリュームが見つかると、ルールによって自動修復アクションがトリガーされ、Automation Runbook にてボリュームに関する情報を収集し、情報をSlack チャネルに送信します。これにより、Slack チャネルをモニターしている運用管理者または開発者チームは、問題のあるリソースに対して対応することができます。

: コードを再利用しやすいように、異なるステップとして作成しています。必要に応じて、両ステップを 1 つのステップに統合して構いません。

図 1 ソリューションの流れ

プロセスは次のとおりです。

  1. AWS Config は encrypted-volumes ルールを実行して、暗号化されていない EBS ボリュームを見つけます。
  2. 暗号化されていない EBS ボリュームごとに、AWS Config は Systems Manager Automation Runbook を実行する自動修復アクションを呼び出します。
  3. Runbook は aws:executeScript を使用して、EBS ボリュームに関する情報を取得します。
  4. Runbook は aws:executeScript を使用して次のことを行います。
    • Slack URL とチャネル情報を含む AWS Secrets Manager シークレットを取得します。
    • 情報を Slack チャネルに投稿します。

前提

このチュートリアルを完了するには、次のものが必要です。

Slack との統合

Slack との統合については、この手順に従って Slack Incoming Webhook を追加します。情報を送信するためのSlack チャネルを選び、https://hooks.slack.com/workflows/… のプレフィックスが付いた URL を取得します。Slack チャネルと URL は後に使用します。

AWS Config との統合

AWS Config を有効にします。 [記録するリソースタイプ] で、すべてのリソースを記録しているか、少なくともEC2:Instance と EC2:Volume が含まれていることを確認してください。これらの設定は、encrypted-volumes ルールが機能するために必要です。詳細については、AWS Config 開発者ガイドの AWS Config の使用開始 を参照してください。

AWS Secrets Manager でシークレットを作成する

作成した Slack URL は機密情報と見なされるため、AWS Secrets Manager に保存します。

  1. AWS Secrets Manager コンソールにサインインし、[新しいシークレットを保存] を選択します。
  2. シークレットの種類には、[その他のシークレット] を選択します。
  3. [プレーンテキスト] タブで、以下を貼り付けます。
    {
      "URL": "TheSlackUrl",
      "channel": "TheSlackChannel"
    }

    TheSlackUrlTheSlackChannel を “前提” の章で構成した値に置き換えてから、[次へ] をクリックします。

  4. [シークレットの名前] には、SlackInfo と入力します。
  5. 他のページは、すべてのデフォルトのままとします。
  6. 後でシークレットの ARN を使用するため、[シークレット] でシークレットの名前を選択し、[シークレットの ARN] の下にある値をコピーします。

CloudFormation テンプレートを使ってスタックを作成する

次に、EncryptedVolsToSlack.yaml から CloudFormation テンプレートをダウンロードし、コンピューター上に保存します。

  1. AWS CloudFormation コンソールで、[スタック]、[スタックの作成]、[新しいリソースを使用 (標準)] の順に選択します。
  2. [スタックの作成] ページで、[テンプレートファイルのアップロード] を選択し、保存した YAML ファイルを選択してから、[次へ] をクリックします。
  3. [スタックの詳細を指定] ページで:
    • [スタックの名前]には、UnencryptedVolToSlackStack と入力します。
    • [パラメータ] の [SlackSecretARN] で、SlackInfo シークレット ARN の値を入力します。
    • 既にIAM ロールを持っている場合はその名前を[ExistingRoleName] に入力します。そうでない場合は、このフィールドを空白のままにします。
  4. [次へ] をクリックします。
  5. 次のページで、[次へ] をクリックします。
  6. [レビュー] ページで、[承認します] チェックボックスをオンにしてから、[スタックの作成] をクリックします。

数分後、ページを更新し、スタックのステータスが CREATE_COMPLETE になっていることを確認してください。 AWS Config は、新しく作成されたencrypted-volumes ルールを実行します。これには数分かかる場合があります。

: CloudFormation テンプレートで使用される IAM ロールは、アカウント内のすべてのリソース (Resource: ‘*’) で ec2:DescribeVolumes および ec2:DescribeInstances アクションを許可します。あくまでも一例です。組織のセキュリティーポリシーに従って、適切に制限してください。

AWS Config ルールを確認する

  1. AWS Config コンソールで、[ルール] を選択します。
  2. UnencryptedVolToSlackStackrule を選択します。これは、CloudFormation スタックで構成されたルールです。
  3. [対象範囲内のリソース] の下にある [非準拠] を選択して、暗号化されていない EBS ボリュームのリストを表示します。

図2. 暗号化されていない EBS ボリューム

暗号化されていない EBS ボリュームがある場合には、自動修復の後 (つまり、スタックによって作成された Automation Runbook が実行された後) 、 [ステータス] の下に [アクションが正常に実行されました] という文字が表示されます。表示されていない場合は、ページを更新してください。

AWS Systems Manager Automation Runbook を確認する

次に、Systems Manager で作成されたリソースを見てみましょう。

  1. AWS Systems Manager コンソールで、[ドキュメント] を選択します。
  2. [自己所有] タブを選択し、UnencryptedVolToSlackStack* のプレフィックスが付いた Automation Runbook を選択します。これは CloudFormation テンプレートによって作成されたものです。
  3. [コンテンツ] タブで、aws:executeScript アクションを呼び出す 2 つのステップを含む、Automation ドキュメントのコンテンツを確認できます。これらを詳細に調べてみましょう。

EBS ボリュームに関する情報を取得します:

- name: extractInfo
  action: 'aws:executeScript'
  outputs:
       - Name: ebsInfoMsg
         Selector: $.Payload.message
         Type: String
   inputs:
         Runtime: python3.6
         Handler: script_handler
         Script: |-
                import json
                import boto3
                        
                def script_handler(events, context):
                  ec2 = boto3.client('ec2')
                          
                  response = ec2.describe_volumes(
                      Filters=[
                          {
                              'Name': 'volume-id',
                              'Values': [
                                events['ebsVolumeId']
                              ],
                          }
                      ],
                  )
                  
                  state = response['Volumes'][0]['State']
                  iops = 'N/A'
                  if 'Iops' in response['Volumes'][0]:
                      iops = response['Volumes'][0]['Iops']
                  volumeType = response['Volumes'][0]['VolumeType']
                  attachments = response['Volumes'][0]['Attachments']
                  ec2Attached = "No EC2 attached"
                  if attachments:
                      ec2Attached = attachments[0]['InstanceId']
                  
                  theMsg = 'Account {} - Unencrypted Volume {} found! ' \
                          '[type:{} state:{} Iops:{} EC2-attached:{}' \
                          .format(events['accountId'], events['ebsVolumeId'],volumeType,state,iops,ec2Attached)
                  return {'message': theMsg }
         InputPayload:
             ebsVolumeId: '{{ResourceId}}'
             accountId: '{{global:ACCOUNT_ID}}'
      description: Gather information for the resource

以下の3つの部分に注目してください。

  1. Inputsセクションでは、実行する Python スクリプトを指定しています。このスクリプトは、ec2.describe_volumes 呼び出しを実行して、EBS ボリュームに関する情報を取得します。
  2. InputPayload セクションは、Python スクリプトで使用する変数を宣言しています。この変数は、AWS Lambda 関数ハンドラーのイベント引数を介して Python スクリプト内でアクセスできます。
    • ebsVolumeId は、AWS Config ルールによって Automation ドキュメントに渡される引数 ResourceId で初期化されます。
    • accountId は、実行時に使用できるグローバル Automation システム変数 である ACCOUNT_ID で初期化されます。
  3. Outputs セクションは変数 ebsInfoMsg を宣言し、$.Payload.message で初期化します。 Payload 変数には、Python スクリプトが返す JSON オブジェクト (つまり、return {‘message’: theMsg} ) が設定されます。したがって、ebsInfoMsg には theMsg の値が設定されます。

メッセージを Slack チャネルに投稿します:

- name: publishToSlack
  action: 'aws:executeScript'
  inputs:
    Runtime: python3.6
    Handler: script_handler
    Script: |-
        import json
        import urllib
        from botocore.exceptions import ClientError
        import boto3

        def script_handler(events, context):
            slack_secret_arn = events['SlackSecretARN']
            slack_info = json.loads(get_slack_secret(slack_secret_arn))

            slack_message = {
                'channel': slack_info["channel"],
                'Content': events['theMsgToSend']
            }
            data = json.dumps(slack_message).encode('utf-8')
            req = urllib.request.Request(slack_info["URL"], data)

            try:
                response = urllib.request.urlopen(req)
                the_page = response.read()
            except HTTPError as e:
                print('Request failed: {} {}'.format(e.code, e.reason))
            except URLError as e:
                print('Server connection failed: {}'.format(e.reason))

            return {"msg": 'Message posted to Slack Channel {}'.format(slack_message['channel'])}

        def get_slack_secret(secret_arn):
            ………
    InputPayload:
      theMsgToSend: '{{extractInfo.ebsInfoMsg}}'
      SlackSecretARN: !Ref SlackSecretARN
  description: Send message to Slack channel

この 2 つ目のアクションには、2 つの重要なセクションがあります。

  1. inputs セクションは、実行する Python スクリプトを指定しています。今回は、Slack チャネルとメッセージを含む JSON ペイロードをポストすることにより、Slack チャネルに通知を送信します。
  2. inputPayload セクションは、Python スクリプトに渡す 2 つの変数を初期化しています。変数は、AWS Lambda 関数ハンドラーのイベント引数を介して Python スクリプト内でアクセスできます。
    • theMsgToSend は、前のステップ extractInfo で宣言された ebsInfoMsg 変数の値を保持します。
    • CloudFormation テンプレートで宣言されたパラメーター SlackSecretARN は、通知の送信先となる Slack チャネルと URL を取得するために使用されます。

Automation 実行結果を確認する

Automation の実行中にエラーが発生した場合、実行されたアクションの結果をチェックできます。

  1. AWS Systems Manager コンソールで[自動化] を選択すると、実行されたすべての Automation Runbook のリストを確認できます。
  2. Runbook の名前は UnencryptedVolToSlackStack* で始まります。詳細を表示するには、その実行の 1 つを選択します。

図3. 実行の詳細

実行の詳細ページで、2 つのステップが正常に実行されたかどうかを確認できます。 1 つのステップでエラーが発生した場合は、失敗した処理のステップ ID を選択して確認します。

クリーンナップ

このチュートリアルで作成した AWS リソースを削除するには、CloudFormation コンソールで [スタック] を選択します。作成したスタック (たとえば、UnencryptedVolToSlackStack) を選択し、[削除] を選択してから、確認画面で[スタックの削除] を選択します。

テンプレートを複数の AWS リージョンにデプロイした場合、スタックによって作成された IAM ロールは、それを作成したスタックによってのみ削除されることに注意してください。このため、IAM ロールを作成したスタックが削除されていることを最後に確認し、不完全にリソースが残らないように気をつけてください。

結論

このブログポストでは、Systems Manager Automation Runbook で aws:executeScript を使用して Python コードのブロックを実行する 2 つの方法を紹介しました。EC2 インスタンスなどの追加のリソースをプロビジョニングすることなしに、使い慣れた AWS SDK for Python コマンドを使用して準拠していないリソースの情報を収集し、標準の Python ロジックを使用してサードパーティのツールと統合しました。

今回は、スクリプトを AWS Config で実行しましたが、AWS Service CatalogAmazon EventBridge などの他の AWS サービスを介して Automation Runbook を実行することもできます(訳注:他に AWS Systems Manager OpsCenterAWS Systems Manager Incident Manager を介しても実行可能です)。また、スタンドアロンの Automation Runbook を実行することもできます。 EC2 および EBS リソースへのアクセスを許可するポリシーを含む IAM ロールを使用して、CloudFormation テンプレートにてインフラストラクチャを簡単にセットアップできます。このロールは、組織のニーズに合わせて調整可能です。

Systems Manager と AWS Config は、すべての主要な AWS リージョンで利用できます。詳細については、AWS Systems Manager ユーザーガイドの スクリプトを実行するランブックを作成する と、AWS Config 開発者ガイドの AWS Config ルールによる非準拠の AWS リソースの修復 を参照してください。

著者について

Melina Schweizer

Melina Schweizer は、AWS のシニアスペシャリストソリューションアーキテクトビルダーです。彼女は、お客様のビジネス成果を促進するためのシンプルで効果的なソリューションの作成に取り組んでいます。余暇には、ピアノを弾いたり、ガーデニングをしたり、家族と一緒に休暇を楽しんだりしています。

 

 

翻訳は SA 石橋が担当しました。原文はこちら