Windows 用 CloudWatch エージェントのバージョンを一括で取得したい

Windows 用 CloudWatch エージェント について、Systems Manager インベントリで収集されたバージョン番号は正しくないものなのでご注意ください。

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

コンバンハ、千葉(幸)です。

とある事情から、環境に存在する Windows EC2 インスタンスにインストールされた CloudWatch エージェントのバージョンを確認したい機会がありました。

具体的には、v1.247354以前のバージョンを使用しているインスタンスがないかを確認したいです。環境に存在するインスタンスが数十台あるので、個別に1台ずつログオンして確認するようなことはしたくありません。

Systems Manager の機能を用いていい感じにできないか検討しました。

まとめ

  • Systems Manager インベントリの機能を有効化しているとインスタンスにインストールされたアプリケーションのバージョンを確認できる
  • インベントリで確認できる Windows の CloudWatch エージェントのバージョンは正しいものではない
  • 作り込みせずにバージョンを取得したいのであれば Systems Manager Run Command が便利

話の前提

今回はインスタンスが Systems Manager の管理ノードになっていることを前提としています。言い換えれば、Systems Manager コンソールの「フリートマネージャー」→「マネージドノード」に表示されている状態であることを前提としています。

管理ノードになるためには、Systems Manager エージェント、インスタンスプロファイル(IAMロール)、ネットワーク経路など、いくつか条件を満たしている必要があります。詳細は以下をご参照ください。

aws ssm list-inventory-entries で取得してみたが何やらおかしい

今回わたしが試行した環境では各マネージドノードで Systems Manager インベントリが有効になっていました。

aws ssm list-inventory-entriesコマンドを使用すればインベントリに収集された情報を取得できます。

以下のように指定してあげれば CloudWatch エージェントのバージョンを取得できそうです。

aws ssm list-inventory-entries\
    --instance-id xxxx\
    --type-name AWS:Application\
    --filters Key=Name,Values="Amazon CloudWatch Agent",Type=Equal

以下のようにaws ec2 describe-instancesと組み合わせてループ処理を実行し、各インスタンスにおけるバージョンが取得できました。めでたしめでたし……と考えていました。

% echo "InstanceId Version" > /tmp/awscli.tmp
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId]" --output text | while read line;\
do
   echo ${line} > /tmp/awscli-instance.tmp;\
   aws ssm list-inventory-entries --instance-id ${line} --type-name AWS:Application --filters Key=Name,Values="Amazon CloudWatch Agent",Type=Equal --query "Entries[].Version" --output text >> /tmp/awscli-instance.tmp;\
   cat /tmp/awscli-instance.tmp | tr "\n" " " | sed 's/$/\n/g' >> /tmp/awscli.tmp;\
done
column -t /tmp/awscli.tmp;\
rm /tmp/awscli.tmp /tmp/awscli-instance.tmp
InstanceId           Version
i-0c6f3xxxxxxxxxxxx
i-09c4cxxxxxxxxxxxx  1.3.50739
i-0036exxxxxxxxxxxx
i-052e6xxxxxxxxxxxx  1.3.50739
i-0e297xxxxxxxxxxxx  1.3.50739
i-01ab9xxxxxxxxxxxx  1.3.50739
...

取得できたバージョンをよくよく見ると何かが違います。今回は「v1.247354以前のものかどうか」を確認したかったですが、1.3の後にピリオドが入っているあたりバージョンの構成が違いそうです。

念のため CloudWatch エージェントのリリースノートを見てみますが、1.3.から始まるバージョンは存在しません。

正しいバージョンを取得したいときはコマンド実行が必要

以下エントリに答えがありました。

AWS ドキュメントによると Windows サーバー上の CloudWatch エージェントのバージョン番号を確認するには次のコマンドを実行する必要あり、とされています。

& $Env:ProgramFiles\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1 -m ec2 -a status

注記

このコマンドを使用することは、CloudWatch エージェントのバージョンを検索する正しい方法です。コントロールパネルの [Programs and Features (プログラムと機能)] を使用すると、誤ったバージョン番号が表示されます。

「誤ったバージョン番号が表示されます。」そんな……。

Run Command で複数台に同時にコマンドを実行する

先述のエントリでは Windows サーバー上の CloudWatch エージェントのバージョン番号を確認するコマンドを載せてくれています。

function Get-CloudWatchAgentVersion () {
    $agentCtlPath = "$env:ProgramFiles\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1"
    if (-not (Test-Path -LiteralPath $agentCtlPath)) {
        return "Not installed"
    }
    return (& $agentCtlPath -m ec2 -a status ) | ConvertFrom-Json | Select-Object -ExpandProperty version
}
Get-CloudWatchAgentVersion

これを Systems Manager Run Command で実行してあげれば、ある程度ラクにバージョン番号が取得できます。早速やってみます。

「AWS Systems Manager」→「 Run Command」→「コマンドの実行」と進みます。

コマンドドキュメントにAWS-RunPowerShellScriptを選択し、バージョンはデフォルトを指定します。

AWS_Systems_Manager_-_Run_Command_Windows_cloudwatch_agent

パラメータCommandsに先ほどのコマンドの内容を入力します。その他のパラメータはデフォルトのままにします。

AWS_Systems_Manager_-_Run_Command_CloudWatch_agent

ターゲットを指定します。例えばタグによって適切にグルーピングできているようであればタグ指定するのも良いでしょう。

今回は個別にインスタンスを指定していきます。プラットフォームがここで確認できるので「Windowsのインスタンスのみ対象にする」ということも楽です。

AWS_Systems_Manager_-_Run_Command_Windows_CloudWatch_Agent-1197677

その他以下を必要に応じて指定し、コマンドを実行します。

  • コメント(メモ)
  • タイムアウト秒数
  • レート制御
    • 同時実行数
    • エラーのしきい値
  • ログ出力
    • S3 バケット
    • CloudWatch Logs
  • CloudWatch アラーム(対象のインスタンスに関するアラームが上がったら中止)
  • SNS 通知(コマンド実行ステータスの通知)

↑ わたしは特に上記を指定/変更せずコマンド実行しました。

20 台を対象に実行し、ほんの数秒で完了しました。

AWS_Systems_Manager_-_Run_Command_windows_CloudWatch_agent-1197940

ターゲットごとに出力を確認すると、このようにバージョン情報が取得できています。

この出力を1台ずつ確認していくのが手間といえば手間なのですが、許容できる範囲かなということで今回はこの方式で賄いました。

ここで取得した情報を一箇所に集約して一覧表示、みたいなこともできる予感もするので、もっとスマートなやり方が思いつく方は教えていただけると嬉しいです。


2022/12/30追記

こんな感じで AWS CLI で一括取得できました。

$ COMMAND_ID=xxxxxxxx-xxxx-xxxx-xxxxx-xxxxxxxxxxxx
$ aws ssm list-command-invocations --command-id $COMMAND_ID --details\
    --query "sort_by(CommandInvocations, &InstanceId)[].[InstanceId, InstanceName, Status, CommandPlugins[0].Output]" \
    --output text | awk 'BEGIN {print "InstanceId, InstanceName, Status, Output"} {
    print $1", "$2", "$3", "$4
    }' | column -t -s ',' | sed '/^ *$/d'
InstanceId            InstanceName   Status    Output
i-0xxxxxxxxxxxxxxxx   HOSTNAME-A     Success   1.247349.0b251399
i-0xxxxxxxxxxxxxxxx   HOSTNAME-B     Success   1.247348.0b251302
i-0xxxxxxxxxxxxxxxx   HOSTNAME-C     Success   1.247347.4b250525
i-0xxxxxxxxxxxxxxxx   HOSTNAME-D     Success   1.247347.6b250880
i-0xxxxxxxxxxxxxxxx   HOSTNAME-E     Success   1.247349.0b251399
i-0xxxxxxxxxxxxxxxx   HOSTNAME-F     Success   1.231221.0
...


終わりに

Windows 用 CloudWatch エージェントのバージョンを一括で取得したい、という内容でした。

Systems Manager インベントリで取得できるバージョンが正しいものではないというのが完全に想定外でしたが、ある程度楽に取得できる手法が見つかってよかったです。

今回は単発の実施だったため採用しませんでしたが、定期的に取得したいという場合には以下エントリのようにカスタムインベントリの採用をご検討ください。

CloudWatch エージェントでは少し罠がありましたが、Systems Manager インベントリを使用することで「各インスタンスにインストールされたアプリケーションのバージョンを一括取得できる」ということを覚えておくと役に立つかもしれません。

参考になれば幸いです。

以上、 チバユキ (@batchicchi) がお送りしました。

余談:aws ssm describe-instance-information

今回は結果的に空振りに終わったのですが、以下のように「インスタンス一覧を取得してそれぞれにループ処理でインベントリ情報取得」を試行した、というのは先述の通りです。

echo "InstanceId Version" > /tmp/awscli.tmp
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId]" --output text | while read line;\
do
   echo ${line} > /tmp/awscli-instance.tmp;\
   aws ssm list-inventory-entries --instance-id ${line} --type-name AWS:Application --filters Key=Name,Values="Amazon CloudWatch Agent",Type=Equal --query "Entries[].Version" --output text >> /tmp/awscli-instance.tmp;\
   cat /tmp/awscli-instance.tmp | tr "\n" " " | sed 's/$/\n/g' >> /tmp/awscli.tmp;\
done
column -t /tmp/awscli.tmp;\
rm /tmp/awscli.tmp /tmp/awscli-instance.tmp

ここでは「インスタンス一覧の取得」にaws ec2 describe-instancesを使用したのですが、他にもaws ssm describe-instance-informationコマンドも候補になります。

こちらのコマンドではプラットフォーム情報も取得できたりと嬉しい部分があります。先ほど引用したエントリでは以下のようなオシャレなコマンドの使い方をしています。

aws ssm describe-instance-information \
    --query "sort_by(InstanceInformationList, &ComputerName)[].[InstanceId, PlatformType]" \
    --output text | awk 'BEGIN {print "InstanceId, Platform, Version"} {
        "aws ssm list-inventory-entries --instance-id "$1" --type-name \"AWS:Application\" --filters \"Key=Name,Values=Amazon CloudWatch Agent, amazon-cloudwatch-agent\" --query \"Entries[0].Version\" --output text" | getline version
        print $1", "$2", "version
    }' | column -t -s ','

ただ、aws ssm describe-instance-informationで取得できる対象は以下のみのようでした。

  • マネージドノードであり、かつ Ping ステータスがオンラインであるもの

リファレンスの書きっぷり的にてっきり Ping ステータスがConnectionLostのものも取得できると考えていたのですが、わたしが試行した範囲では実現できませんでした。(AWS CLI 2.9.1で試行。)

そのため、「マネージドノードだが停止中」というインスタンスはこのコマンドでは一覧に含まれてきません。今回はそこそこ停止中のインスタンスがあったのでaws ec2 describe-instancesを採用した、という背景がありました。(結果的に空振りでしたが。。)

参考