[PowerShell] EC2Launchを最新バージョンにするスクリプトを作ってみた

しばたです。

Windows Server 2016以降のEC2インスタンスに導入されているEC2Launchですが、これまでのEC2Configではバージョンアップを自動化するAWS-UpdateEC2Configドキュメントが提供されているのですが、EC2Launchでは同様のドキュメントは提供されておらずAMI全体のアップデートを行うAWS-UpdateWindowsAmiドキュメントに統合されており単体でバージョンアップすることができません。

そこで今回EC2Launchのバージョンアップを自動化する簡単なスクリプトを作ってみました。

EC2Launchのバージョンアップ手順

EC2LaunchはPowerShellモジュールとして提供されているのですが、標準のモジュールパスに登録されていないためUpdate-Moduleでは更新できず手動で最新パッケージをダウンロードしてインストールする必要があります。
詳細は以下のドキュメントに記載されています。

手順としてはコンテンツのEC2-Windows-Launch.zipinstall.ps1を一緒にダウンロードして実行するだけなのですが、これくらいなら公式な更新ドキュメントを提供してほしいという気持ちもひとしおです...

ちなみEC2Launchの更新履歴は以下にあります。

バージョンアップスクリプト

作ったスクリプトを以下に記載します。

このスクリプトを適当な名前で保存して実行すれば前項で説明した手順を自動化できます。
スクリプトの中身について細かく説明はしませんが、機能ごとに関数を分けてるのでやっていることの雰囲気は掴んで頂けるのではないかと思います。

#
# 最新バージョンの EC2Launch をインストールするスクリプト
# Ref : https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2launch-download.html
#
# ※ EC2Launch 設定ファイルのバックアップには対応していません。
#

<#
.SYNOPSIS
    EC2Launchの最新バージョンを取得します
#>
function Get-LatestEC2LaunchVersion() {
    # https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2launch-version-details.html
    # より最新バージョンを取得
    #
    # ※いつHTMLの構造が変わるかわからないので最悪
    # return [Version]"1.3.2001040"
    # の様に固定値を設定しても良いと思われる
    $res = Invoke-WebRequest -Uri 'https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2launch-version-details.html' -UseBasicParsing
    $versions = ([Xml]$res.Content).html.body.GetElementsByTagName("table") | 
        Where-Object {$_.id -eq 'w269aac17c17b9c19b9'} |
        ForEach-Object { $_.tr } |
        Select-Object -Skip 1 |
        ForEach-Object { [Version]($_.td[0].p) }
    $latestVersion = $versions |
        Sort-Object -Descending |
        Select-Object -First 1
    return $latestVersion
}

<#
.SYNOPSIS
    現在インストールされているEC2Launchのバージョンを取得します。
    EC2Launchがインストールされていない場合は $null を返します。
#>
function Get-EC2LaunchVersion() {
    $psdPath = 'C:\ProgramData\Amazon\EC2-Windows\Launch\Module\Ec2Launch.psd1'
    if (-not (Test-Path -Path $psdPath -PathType Leaf)) {
        Write-Warning "Ec2Launch isn't installed."
        return $null
    }
    return [Version](Import-PowerShellDataFile $psdPath).ModuleVersion
}

<#
.SYNOPSIS
    EC2Launchの最新パッケージをダウンロードします。
#>
function Save-LatestEC2LaunchPackage([string]$SavePath = $env:TEMP) {
    $packages = @(
        @{FileName = 'EC2-Windows-Launch.zip'; Uri = 'https://s3.amazonaws.com/ec2-downloads-windows/EC2Launch/latest/EC2-Windows-Launch.zip'},
        @{FileName = 'install.ps1'; Uri = 'https://s3.amazonaws.com/ec2-downloads-windows/EC2Launch/latest/install.ps1'}
    )
    foreach ($package in $packages) {
        Write-Host "Download $($package.FileName)..."
        $outFile = Join-Path $SavePath ($package.FileName)
        Invoke-WebRequest -Uri $package.Uri -OutFile $outFile
    }
}

<#
.SYNOPSIS
    ダウンロードしたEC2Launchの最新パッケージを削除します。
#>
function Clear-LatestEC2LaunchPackage([string]$InstallerPath = $env:TEMP) {
    $items = @(
        (Join-Path $InstallerPath 'EC2-Windows-Launch.zip'),
        (Join-Path $InstallerPath 'install.ps1')
    )
    foreach ($item in $items) {
        if (Test-Path -LiteralPath $item) {
            Write-Host "Remove $item ..."
            Remove-Item -LiteralPath $item -Force
        }
    }
}

<#
.SYNOPSIS
    EC2Launchのインストーラーを実行します。
#>
function Invoke-EC2LaunchInstaller([string]$InstallerPath = $env:TEMP) {
    $installer = Join-Path $InstallerPath 'install.ps1'
    try {
        Push-Location $InstallerPath
        Write-Host "Execute $installer ..."
        & $installer
    }
    finally {
        Pop-Location
    }
}

<#
.SYNOPSIS
    EC2LaunchがサポートされているOS(2016 Server以降)か判定します。
#>
function Test-SupportedVersion() {
    $ProductType_WorkStation = 1
    try {
        $versionInfo = Get-CimInstance -ClassName Win32_OperatingSystem -Property Version, ProductType, Caption
        Write-Host "OS : $($versionInfo.Caption)"
        if (([Version]$versionInfo.Version).Major -lt 10) {
            return $false
        }
        if ($versionInfo.ProductType -eq $ProductType_WorkStation ) {
            return $false
        }
        return $true
    }
    catch {
        return $false
    }
}

<#
.SYNOPSIS
    Main関数です。
#>
function Main() {
    if (-not (Test-SupportedVersion)) {
        Write-Host "This is unsupported OS."
        return
    }

    # Check EC2Launch version
    $latestVersion = LatestEC2LaunchVersion
    Write-Host "Latest EC2Launch version is $latestVersion ..."
    $currentVersion = Get-EC2LaunchVersion
    Write-Host "Current EC2Launch version is $currentVersion ..."
    if ($currentVersion -eq $latestVersion) {
        Write-Host "EC2Launch is the latest version."
        return
    }

    # Update EC2Launch
    try {
        Save-LatestEC2LaunchPackage
        Invoke-EC2LaunchInstaller
    }
    finally {
        Clear-LatestEC2LaunchPackage
    }
    $currentVersion = Get-EC2LaunchVersion
    Write-Host "Current EC2Launch version is $currentVersion ."
}
Main

バージョンアップスクリプトを実行してみる

本記事ではこのスクリプトをSSM Run Commandで実行してみるところまで紹介します。

開発環境にあるWindows Server 2016インスタンスに対して実行する想定でRun Commandを実行できるところまでは設定済みとします。

1. コマンド指定

本記事ではAWS-RunPowerShellScriptコマンドでスクリプトを実行します。

2. スクリプト貼り付け

選んだコマンドのCommands欄に上記スクリプトをそのまま貼り付けます。
Working Directoryは指定しなくても大丈夫です。

3. ターゲット指定、コマンド実行

ターゲットとなるEC2インスタンスを指定します。
ログの出力オプションなどは自由に設定してください。

設定後、「実行」を押してコマンドを実行します。

4. 結果確認

実行が完了したら出力を確認してみます。

このスクリプトでは下図の様なログを出す様にしており、今回はVer.1.3.2000930からVer.1.3.2001040に更新できていることがわかります。

最後に

ざっとこんな感じです。
利用機会はそれほど多くないかもしれませんがこのスクリプトが誰かの役に立てば幸いです。