「AWS無料相談会」をオンラインで開催中

AWS Systems Manager Run Commandの実行結果をSlackに通知する方法

どうも、7月中旬から1~3ヶ月のレンジでホーチミンでリモートワークをする予定のため、今からとても楽しみな古川です。

今回はタイトルの通り、AWS Systems Manager Run Commandの実行結果をSlackに通知する方法を共有したいと思います。

処理フロー

AWS Systems Manager Run Command -> Amazon SNS -> AWS Lambda -> Slack

前提

  1. AWS Systems Manager Run Commandを実行するメンテナンスウィンドウを作成済み
  2. AWS Systems Manager Run Commandのドキュメントは AWS-RunPatchBaseline の想定とする
  3. Slackへの通知はIncoming Webhookで作成した Webhook URL を対象に行い、既に設定が完了しているものとする
  4. AWS LambdaはRubyで作成する

作業の大まかな流れ

  1. Amazon SNSのトピック作成
  2. Slack通知の処理を担当するAWS Lambdaのセットアップ
  3. AWS Systems Manager Run CommandにAmazon SNSを関連づける

実作業

1. Amazon SNSのトピック作成

1-1. IAMロールの作成

URLの「タスク 1: Amazon SNS 通知の IAM ロールを作成する」、「タスク 2: iam:PassRole ポリシーを Amazon SNS ロールにアタッチする」に従いIAMロールを作成。

後述の「4. AWS Systems Manager Run CommandにAmazon SNSを関連づける」内で使用する想定。

上記手順により、以下のような設定のIAMロールが作成される。


※ インラインポリシーが作成されているか要確認。

1-2. トピックの作成

Amazon SNS管理コンソール上でトピックの作成をクリック。

名前を入力して保存。

2.AWS Lambdaのセットアップ

2-1. AWS Lambdaを作成

ランタイムをRuby2.5とし、Lambdaを作成。

作成したLambdaの「コードエントリタイプ」を「コードをインラインで編集」にして以下のコードを main.rb というファイル名で作成。ハンドラを「 main.lambda_handler 」とする。

コード
require 'uri'
require 'net/http'
require 'json'

def color_by(status)
  color = ""
  case status
  when "Failed"
    color = "danger"
  when "Success"
    color = "good"
  when "InProgress"
    color = "#16B0FF" #skyblue
  else
    color = "warning"
  end
  color
end

def make_notification_text(subject, msg)
  content = {
    "Subject"           => subject,
    "CommandID"         => msg["commandId"],
    "Status"            => msg["status"],
    "DocumentName"      => msg["documentName"],
    "InstanceID"        => msg["instanceId"],
    "RequestedDatetime" => msg["requestedDateTime"],
    "EventTime"         => msg["eventTime"]
  }

  text = ""
  content.each do |k, v|
    text << "*#{k}:*
#{v}

"
  end
  text
end

def lambda_handler(event:, context:)
  puts event
  event["Records"].each do |record|
    msg = JSON.parse(record["Sns"]["Message"])

    # HTTPSで通信するための設定
    uri = URI.parse(ENV['SLACK_HOOK_URL'])
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    req = Net::HTTP::Post.new(uri.path, initheader = { 'Content-Type' => 'application/json' })

    # attachmentsをキーとすることでSlackの通知メッセージにcolorをセット
    # 参考: https://api.slack.com/docs/message-attachments
    req.body = { "attachments": [{ "text" => make_notification_text(record["Sns"]["Subject"], msg), "color" => color_by(msg["status"]) }] }.to_json
    res = http.request(req)
    puts "Response #{res.code} #{res.message}: #{res.body}"
  end
end

2-2. 環境変数の設定

Lambdaの環境変数設定にて環境変数をセットする。

SLACK_HOOK_URL というキーで Incoming Webhook で作成してある Webhook URL をセットする。

2-3. Amazon SNSのトリガーを追加

AWS LambdaにAmazon SNSのトリガーを追加。
設定するARNは「1. Amazon SNSのトピック作成」の手順で作成したAmazon SNSのトピックのものとする。

2-4. 実行ロールの設定

AWSLambdaBasicExecutionRole のポリシーがアタッチされたロールを「実行ロール」にて選択。

3. AWS Systems Manager Run CommandにAmazon SNSを関連づける

既に作成されているAWS Systems Managerのメンテナンスウィンドウより操作。

↓のように対象のメンテナンスウィンドウの「タスク」より①、②と順番にクリックし編集画面へ移動。

編集画面下部に↓の編集項目があるのでそれぞれ値を入力し保存する。

① - チェックをし有効化。
② - 前述の「1. Amazon SNSのトピック作成」の「1-1. IAMロールの作成」で作成したIAMロールを選択。
③ - 前述の「1. Amazon SNSのトピック作成」の「1-2. トピックの作成」で作成したトピックのARNを入力。

以上の設定が完了次第、AWS Systems Manager Run Commandの実行結果が、以下のような形式でSlackへと通知が行くようになります。
ステータスごとに色が変わるので一見してわかりやすくて良いですね。