AWS Systems Managerインベントリはどのアプリケーション情報を収集しているのか調べてみた

しばたです。
先日弊社吉井によりAWS Systems Managerインベントリの機能を使い「管理している EC2 インスタンスにインストールされているソフトウェアとそのバージョンを一覧表示する」記事が公開されました。

EC2インスタンスにインストールされているソフトウェアとバージョン一覧を表示したい

Systems ManagerインベントリではSSM Agentの機能で対象となるEC2インスタンスにインストールされているアプリケーション情報を収集しています。
ここで単純に「インストールされているアプリケーション」と記載しましたが、実際には各OSにおけるアプリケーションのインストール方法およびその検出方法は千差万別です。

本記事ではSystems ManagerインベントリおよびSSM Agentがどの様な「アプリケーション」を検出対象としているか調べてみました。

ドキュメントの記載

まずはドキュメントを当たってみます。
ユーザーガイドを調べてみると、

インベントリを設定して、次のメタデータのタイプを収集できます。
* アプリケーション : アプリケーション名、発行元、バージョンなど。

[
  ・・・(中略)・・・

  {
    "typeName" : "AWS:Application",
    "version": "1.1",
    "attributes":[
      { "name": "Name",               "dataType": "STRING"},
      { "name": "ApplicationType",    "dataType": "STRING"},
      { "name": "Publisher",          "dataType": "STRING"},
      { "name": "Version",            "dataType": "STRING"},
      { "name": "InstalledTime",      "dataType": "STRING"},
      { "name": "Architecture",       "dataType": "STRING"},
      { "name": "URL",                "dataType": "STRING"},
      { "name": "Summary",            "dataType": "STRING"},
      { "name": "PackageId",          "dataType": "STRING"},
      { "name": "Release",            "dataType": "STRING"},
      { "name": "Epoch",              "dataType": "STRING"}
    ]
  },

  ・・・(後略)・・・
}

とあり、「アプリケーション」で収集される情報はわかるものの、”何を” 収集対象にしているか記載されているものを見つけることはできませんでした。
(もし記載されているドキュメントがある様でしたらコメントをお願いします...)

SSM Agentのソースを読む

皆さんもご存じかもしれませんがSSM AgentはGitHubでソースが公開されています。

「ドキュメントが無ければソースを読めば良いじゃない」ということでソースを読んでみました。
本記事では現在の最新バージョンであるVer.2.3.539.0を対象としています。

aws:softwareInventory

SSM Agentは各機能をプラグイン形式で保持しており、Systems Managerインベントリに関わる機能はaws:softwareInventoryプラグインが担っています。

そしてこのソースの配下にあるgatherers/applicationがアプリケーション情報を収集する機能となっています。

Windowsにおけるアプリケーション情報

Windowsでのアプリケーション情報収集の実装はdataProvider_windows.goに記述されています。

ソース中のコメントに

// dataProvider_windows.go 114行目付近より抜粋

/*
        Note:

        We get list of installed apps by using powershell to query registry from 2 locations:

        Path-1 => HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
        Path-2 => HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*

        Path-2 is used to get a list of 32 bit apps running on a 64 bit OS (when 64bit agent is running on 64bit OS)
        For all other scenarios we use Path-1 to get the list of installed apps.
        Reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724072(v=vs.85).aspx

        ・・・(後略)・・・
*/

とある様に、レジストリ、

  • HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\
  • HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\

配下にある情報を読んでアプリケーション情報を取得しています。
このレジストリキーはMSIインストーラーなどによってインストールされた場合に追加される領域で「プログラムと機能(プログラムの追加と削除)」で表示される情報の大元となっています。
Windowsの詳しい方あれば"ごく普通"だと思っていただけるでしょう。

Linuxにおけるアプリケーション情報

Linuxでのアプリケーション情報収集の実装はdataProvider_unix.goに記述されています。

こちらの実装ではアプリケーション情報をrpmまたはdpkg(dpkg-query)から取得しています。
最初にdpkg-queryでアプリケーション情報の取得を試み、dpkgがインストールされていないなどのエラーとなった場合rpmを使う実装となっています。

コマンド的にはそれぞれ、

  • dpkg-query -W
  • rpm -qa

を内部で発行しています。

ソースとしては以下のあたりの記述となります。

// dataProvider_windows.go 75行目付近より抜粋

// collectPlatformDependentApplicationData collects all application data from the system using rpm or dpkg query.
func collectPlatformDependentApplicationData(context context.T) (appData []model.ApplicationData) {

    var err error
    log := context.Log()

    args := []string{dpkgArgsToGetAllApplications, dpkgQueryFormat}
    cmd := dpkgCmd

    // try dpkg first, if any error occurs, use rpm
    if appData, err = getApplicationData(context, cmd, args); err != nil {
        log.Info("Getting applications information using dpkg failed, trying rpm now")
        cmd = rpmCmd
        args = []string{rpmCmdArgToGetAllApplications, rpmQueryFormat, rpmQueryFormatArgs}
        if appData, err = getApplicationData(context, cmd, args); err != nil {
            log.Errorf("Unable to detect package manager - hence no inventory data for %v", GathererName)
        }
    }

    return
}

確認してみた

かなり雑な方法ですがWindows Server 2019、Amazon Linux 2のEC2インスタンスをそれぞれ1台ずつ用意し、PowerShell Coreのアプリケーションを各インスタンスに以下の様にインストールしてみます。

  • Stableバージョン(Ver.6.2.0)はMSIインストーラーおよびYum(rpm)からインストール
  • Previewバージョン(Ver.6.2.0-rc1)はzip/tarファイルをダウンロード、解凍展開してインストール

インストーラーを使わないPreviewバージョンの方はSystems Managerインベントリに乗らないはずです。   Systems Managerの各種設定は最初に紹介した記事の通り行います。

Windows Server 2019でのセットアップ

ざっくりこんな感じです。本筋ではないので細かい説明は端折ります。

# StableバージョンはMSIインストーラーでインストール(自作スクリプト)
Set-ExecutionPolicy Bypass -Scope Process -Force; [Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/stknohg/PSCoreUpdate/master/FirstTimeInstaller/Install-LatestPowerShell.ps1'))

# PreviewバージョンはZipでインストール (※Preview版もMSIでインストールできます)
cd ~
Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-rc.1/PowerShell-6.2.0-rc.1-win-x64.zip -OutFile .\PowerShell-6.2.0-rc.1-win-x64.zip
Expand-Archive -LiteralPath .\PowerShell-6.2.0-rc.1-win-x64.zip -DestinationPath 'C:\Program Files\PowerShell\6-preview'

Amazon Linux 2でのセットアップ

ざっくりこんな感じです。こちらも本筋ではないので細かい説明は端折ります。

# StableバージョンはYum(rpm)でインストール
curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo
sudo yum install -y powershell

# PreviewバージョンはZipでインストール (※Preview版もYumからインストールできます)
cd ~
curl -LO https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-rc.1/powershell-6.2.0-rc.1-linux-x64.tar.gz
sodu mkdir /opt/microsoft/powershell/6-preview
cd  /opt/microsoft/powershell/6-preview
sudo tar -xzvf ~/powershell-6.2.0-rc.1-linux-x64.tar.gz
sudo chmod 755 ./pwsh
sudo ln -s /opt/microsoft/powershell/6-preview/pwsh /usr/bin/pwsh-preview

結果の確認

詳細ビューからざっくりpowershsllの名を持つアプリケーションを検索してみます。

# クエリが雑なのは許してほしい...
select name, packageid, version, resourceid
  from aws_application
 where lower(name) like 'powershell%'
 order by packageid

結果はこの通りStableバージョンのPowerShellのみ収集されており、Preview版は収集されませんでした。

余談

ちなみに、Previewバージョンをインストーラーからインストールすると下図の様にちゃんと収集対象となります。

(Previewバージョンをインストーラーからインストールした場合)

最後に

ざっとこんな感じです。
結果としてSystems Managerインベントリは、

  • Windowsでは「プログラムと機能」に表示される情報を収集する
  • Linuxではdpkg-queryまたはrpmコマンドからアプリケーション情報を収集する

ことがわかりました。
具体的に取得できる情報を知ることでSystems Managerインベントリをより有効に活用できると思います。