EC2 Image BuilderでWindows ServerのAMIを作成してみた

中山(順)@東京です。

re:Invent 2019 開催中ですね。

今回は、EC2 Image BuilderでWindows ServerのAMIを作成してみました。

EC2 Image Builderとは

AMI (Amazon Machine Image) の作成を自動化するための機能です。

EC2 Image BuilderではAMIを作成するための一連の処理を "Pipeline" として定義します。 Pipelineを定義する中で「どのイメージをベースにするか」「ビルドフェーズで何をするか」「テストフェーズで何をするか」を定義します。 この3つはEC2 Image Builderの中で "Recipe" というリソースとして定義されます。 そのほか、「イメージをいつ作成するのか(マニュアル / スケジュール)」「イメージのビルドをどのVPCで行うのか」「ログをどこに出力するのか」などを設定します。

EC2 Image Builderは現時点で以下のOSをサポートしています。

  • Amazon Linux 2
  • Windows Server 2019 / 2016 / 2012 R2

Supported Operating Systems

また、ビルドフェーズおよびテストフェーズでは複数の処理単位を実行することができます。 この処理単位は "Component" として定義されます。 Componentの定義方法は以下のドキュメントを参照してください。

Using Documents

また、ドキュメントの中で利用できるアクションはこちらを参照してください。

Supported Action Modules

やってみた

今回は以下のブログ記事で紹介されている処理の一部をビルドフェーズで実行してみたいと思います。

PowerShellスクリプトサンプル(Windows Server環境構築 初期設定の一括実行) – 2017年夏ver.

Documentの作成

3分間クッキング方式で大変恐縮ですが、以下のようなドキュメントを作成しました。

name: my-basic-setting
description: 'Ore no ami'
schemaVersion: 1.0
phases:
  -
    name: build
    steps:
      - 
        name: DisableWindowsFirewall
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False'
      - 
        name: CreateToolDirectory
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'mkdir C:\tools'
      - 
        name: SetEnvironmentVarialbe
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - '$path = [Environment]::GetEnvironmentVariable(''PATH'', ''Machine'')'
            - '$path += ";" + "C:\tools\nkf" + ";" + "C:\tools\gzip"'
            - '[Environment]::SetEnvironmentVariable(''PATH'', $path, ''Machine'')'
      - 
        name: InstallFirefox
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'Invoke-WebRequest -Uri "https://ftp.mozilla.org/pub/firefox/releases/70.0.1/win64/ja/Firefox%20Setup%2070.0.1.exe" -OutFile C:\tools\firefox.exe'
            - 'C:\tools\firefox.exe -ms'
      - 
        name: InstallChocolatey
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'iex ((New-Object System.Net.WebClient).DownloadString(''https://chocolatey.org/install.ps1''))'
      -
        name: RebootAfterConfigApplied
        action: Reboot
        inputs:
          delaySeconds: 60
      - 
        name: InstallGit
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'choco install -y git'
      - 
        name: InstallPython3
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'choco install -y python3'
      - 
        name: InstallCurl
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'choco install -y curl'
      -
        name: RebootAfterConfigApplied02
        action: Reboot
        inputs:
          delaySeconds: 60
name: test-my-basic-setting
description: 'Ore no test'
schemaVersion: 1.0
phases:
  -
    name: test
    steps:
      - 
        name: GetWindowsFirewall
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'Get-NetFirewallProfile'
      - 
        name: DescribeToolDirectory
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'ls C:\tools'
      - 
        name: GetEnvironmentVarialbe
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'Get-ChildItem env:Path'
      - 
        name: GetFirefox
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'Get-ChildItem -Path(''HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'') | % { Get-ItemProperty $_.PsPath | Select-Object DisplayName, DisplayVersion, Publisher, PSPath}'
      - 
        name: TestChocolatey
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'choco -v'
      - 
        name: TestGit
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'git --version'
      - 
        name: TestPython3
        action: ExecutePowerShell
        timeoutSeconds: 120
        onFailure: Abort
        maxAttempts: 3
        inputs:
          commands:
            - 'python --version'

Componentの作成

上記のDocumentを利用してComponentを作成します。

まずはBuildのためのComponentを作成します。

次にTest用のComponentを作成します。

この2つのComponentを使ってImageを作成します。

PipelineおよびRecipeの作成

Pipelineを作成します。

OSはWindows Server 2019を利用してみます。

BuildのためのComponentには先ほど作成したものを利用します。

次に、Recipeの名前やBuild / Testの為に作成されるEC2インスタンスに設定するIAM Role / Instance Profileを設定します。

動作確認のためにS3にログを出力します。

それ以外の設定は基本的にデフォルトとします。

設定を確認し、Pipelineの作成を完了します。

Imageの作成とログの確認

作成したPipelineを利用してImageをBuildします。

イメージが作成できました。

ログを確認します。 標準出力の結果は以下の通りで、問題なさそうです。(環境変数の部分は表示が切れていて確認できませんでしたが・・・)

2019-12-03 12:03:33 Info Document TOE_2019-12-03_12-03-31_UTC-0_ec891787-15c4-11ea-a476-0a728ba491f4/test-test_1152921504606846977.ps1 
2019-12-03 12:03:33 Info Phase test 
2019-12-03 12:03:33 Info Step GetWindowsFirewall 
2019-12-03 12:03:35 Info Command execution completed successfully 
2019-12-03 12:03:35 Info Stdout: Name                            : Domain
Enabled                         : False
DefaultInboundAction            : NotConfigured
DefaultOutboundAction           : NotConfigured
AllowInboundRules               : NotConfigured
AllowLocalFirewallRules         : NotConfigured
AllowLocalIPsecRules            : NotConfigured
AllowUserApps                   : NotConfigured
AllowUserPorts                  : NotConfigured
AllowUnicastResponseToMulticast : NotConfigured
NotifyOnListen                  : False
EnableStealthModeForIPsec       : NotConfigured
LogFileName                     : %systemroot%\system32\LogFiles\Firewall\pfirewall.log
LogMaxSizeKilobytes             : 4096
LogAllowed                      : False
LogBlocked                      : False
LogIgnored                      : NotConfigured
DisabledInterfaceAliases        : {NotConfigured}

Name                            : Private
Enabled                         : False
DefaultInboundAction            : NotConfigured
DefaultOutboundAction           : NotConfigured
AllowInboundRules               : NotConfigured
AllowLocalFirewallRules         : NotConfigured
AllowLocalIPsecRules            : NotConfigured
AllowUserApps                   : NotConfigured
AllowUserPorts                  : NotConfigured
AllowUnicastResponseToMulticast : NotConfigured
NotifyOnListen                  : False
EnableStealthModeForIPsec       : NotConfigured
LogFileName                     : %systemroot%\system32\LogFiles\Firewall\pfirewall.log
LogMaxSizeKilobytes             : 4096
LogAllowed                      : False
LogBlocked                      : False
LogIgnored                      : NotConfigured
DisabledInterfaceAliases        : {NotConfigured}

Name                            : Public
Enabled                         : False
DefaultInboundAction            : NotConfigured
DefaultOutboundAction           : NotConfigured
AllowInboundRules               : NotConfigured
AllowLocalFirewallRules         : NotConfigured
AllowLocalIPsecRules            : NotConfigured
AllowUserApps                   : NotConfigured
AllowUserPorts                  : NotConfigured
AllowUnicastResponseToMulticast : NotConfigured
NotifyOnListen                  : False
EnableStealthModeForIPsec       : NotConfigured
LogFileName                     : %systemroot%\system32\LogFiles\Firewall\pfirewall.log
LogMaxSizeKilobytes             : 4096
LogAllowed                      : False
LogBlocked                      : False
LogIgnored                      : NotConfigured
DisabledInterfaceAliases        : {NotConfigured} 
2019-12-03 12:03:35 Info Stderr:  
2019-12-03 12:03:35 Info ExitCode 0 
2019-12-03 12:03:35 Info Step DescribeToolDirectory 
2019-12-03 12:03:35 Info Command execution completed successfully 
2019-12-03 12:03:35 Info Stdout: Directory: C:\tools


Mode                LastWriteTime         Length Name                                                                  
----                -------------         ------ ----                                                                  
-a----        12/3/2019  11:41 AM       52023592 firefox.exe 
2019-12-03 12:03:35 Info Stderr:  
2019-12-03 12:03:35 Info ExitCode 0 
2019-12-03 12:03:35 Info Step GetEnvironmentVarialbe 
2019-12-03 12:03:36 Info Command execution completed successfully 
2019-12-03 12:03:36 Info Stdout: Name                           Value                                                                                   
----                           -----                                                                                   
Path                           C:\Python38\Scripts\;C:\Python38\;C:\Windows\system32;C:\Windows;C:\Windows\System32\... 
2019-12-03 12:03:36 Info Stderr:  
2019-12-03 12:03:36 Info ExitCode 0 
2019-12-03 12:03:36 Info Step GetFirefox 
2019-12-03 12:03:36 Info Command execution completed successfully 
2019-12-03 12:03:36 Info Stdout: DisplayName                                 DisplayVersion Publisher                     PSPath                        
-----------                                 -------------- ---------                     ------                        
                                                                                         Microsoft.PowerShell.Core\R...
Git version 2.24.0.2                        2.24.0.2       The Git Development Community Microsoft.PowerShell.Core\R...
Mozilla Firefox 70.0.1 (x64 ja)             70.0.1         Mozilla                       Microsoft.PowerShell.Core\R...
Mozilla Maintenance Service                 70.0.1         Mozilla                       Microsoft.PowerShell.Core\R...
                                                                                         Microsoft.PowerShell.Core\R...
Python 3.8.0 Core Interpreter (64-bit)      3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
Python 3.8.0 Tcl/Tk Support (64-bit)        3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
aws-cfn-bootstrap                           1.4.31         Amazon Web Services           Microsoft.PowerShell.Core\R...
Python 3.8.0 Utility Scripts (64-bit)       3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
Python 3.8.0 Add to Path (64-bit)           3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
Python 3.8.0 Standard Library (64-bit)      3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
Python 3.8.0 Development Libraries (64-bit) 3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
Python 3.8.0 Documentation (64-bit)         3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
Python 3.8.0 Executables (64-bit)           3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
Amazon SSM Agent                            2.3.722.0      Amazon Web Services           Microsoft.PowerShell.Core\R...
AWS PV Drivers                              8.3.2          Amazon Web Services           Microsoft.PowerShell.Core\R...
Python 3.8.0 pip Bootstrap (64-bit)         3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R...
Python 3.8.0 Test Suite (64-bit)            3.8.150.0      Python Software Foundation    Microsoft.PowerShell.Core\R... 
2019-12-03 12:03:36 Info Stderr:  
2019-12-03 12:03:36 Info ExitCode 0 
2019-12-03 12:03:36 Info Step TestChocolatey 
2019-12-03 12:03:37 Info Command execution completed successfully 
2019-12-03 12:03:37 Info Stdout: 0.10.15 
2019-12-03 12:03:37 Info Stderr:  
2019-12-03 12:03:37 Info ExitCode 0 
2019-12-03 12:03:37 Info Step TestGit 
2019-12-03 12:03:38 Info Command execution completed successfully 
2019-12-03 12:03:38 Info Stdout: git version 2.24.0.windows.2 
2019-12-03 12:03:38 Info Stderr:  
2019-12-03 12:03:38 Info ExitCode 0 
2019-12-03 12:03:38 Info Step TestPython3 
2019-12-03 12:03:38 Info Command execution completed successfully 
2019-12-03 12:03:38 Info Stdout: Python 3.8.0 
2019-12-03 12:03:38 Info Stderr:  
2019-12-03 12:03:38 Info ExitCode 0 
2019-12-03 12:03:38 Info Document TOE_2019-12-03_12-03-31_UTC-0_ec891787-15c4-11ea-a476-0a728ba491f4/test-build_1152921504606846977.ps1 

まとめ

以上のようにWindowsでもImageの作成を自動化することができました。 欲を言えばAnsibleやGitHub等との連携がもっと簡単にできるとさらに使い勝手が良くなりそうと思いました。

Windowsでも非常に簡単に利用できますので活用していきましょう。

現場からは以上です。(東京にいるけど)