EC2Launch v2環境でユーザーデータを扱う方法

2021.09.26

しばたです。

先日から利用可能になったWindows Server 2022 AMIではデフォルトでEC2Launch v2が組み込まれています。

このEC2Launch v2では以前のバージョンに対しユーザーデータの扱い方が変わっているため本記事で解説します。

Windows Server EC2のユーザーデータ

EC2ではインスタンスの起動時に「ユーザーデータ」に記述した独自のスクリプトを実行できます。
このユーザーデータはLinuxインスタンスにおいてはcloud-init、WindowsインスタンスにおいてはEC2Config(Windows Server 2016以前)およびEC2Launch(Windows Server 2016以降)によって実行されます。

ここでEC2ConfigEC2Launch (v1)においては以下の様な簡素なタグを使った構文でユーザーデータを記載できました。

バッチファイルを実行する場合 (コマンドプロンプト)

<script>
echo Current date and time >> %SystemRoot%\Temp\test.log
echo %DATE% %TIME% >> %SystemRoot%\Temp\test.log
</script>

ユーザーデータを起動のたびに実行する場合は<persist>タグを付けます。

<script>
echo Current date and time >> %SystemRoot%\Temp\test.log
echo %DATE% %TIME% >> %SystemRoot%\Temp\test.log
</script>
<persist>true</persist>

PowerShellスクリプトを実行する場合 (Windows PowerShell)

<powershell>
$file = $env:SystemRoot + "\Temp\" + (Get-Date).ToString("MM-dd-yy-hh-mm")
New-Item $file -ItemType file
</powershell>

こちらもユーザーデータを起動のたびに実行する場合は<persist>タグを付けます。

<powershell>
$file = $env:SystemRoot + "\Temp\" + (Get-Date).ToString("MM-dd-yy-hh-mm")
New-Item $file -ItemType file
</powershell>
<persist>true</persist>

EC2Launch v2環境でのユーザーデータ

ここまでが従来の構文ですが、EC2Launch v2環境では上記記法に加え以下の様なYAML形式の構文が追加されました。

version: 1.0
tasks:
- task: executeScript
  inputs:
  - frequency: always
    type: powershell
    runAs: localSystem
    content: |-
      $file = $env:SystemRoot + "\Temp\" + (Get-Date).ToString("MM-dd-yy-hh-mm")
      New-Item $file -ItemType file

この構文はEC2Launch v2の設定ファイルagent-config.ymlを設定する際に使用するものと同じになります。

agent-config.ymlでは各種設定を実行するタイミングをstageに記述し、このstage

  1. Boot
  2. Network
  3. PreReady
  4. PostReady
  5. UserData

の5つの段階があるのですが5. UserDataだけは設定ファイルでなくユーザーデータに直接記載する形となります。

これは「5. UserDataだけは別の設定ファイルがありagent-config.ymlの後に実行される」ようなものと考えるのが理解しやすいと思います。
(私はこの記事を書くまでこのステージの扱いをずっと誤認してました...)

この様にEC2Launch v2におけるユーザーデータはagent-config.ymlの拡張の様な形となっているため、単純にスクリプトを実行する以外にEC2Launch v2自体の機能も呼び出すことが可能となっています。

各タスクのドキュメントにあるAllowedStages(実行可能ステージ)にUserDataが記載されているものであればどれでも実行可能です。
たとえば追加EBSボリュームの初期フォーマットを行うinitializeVolumeタスクはAllowedStages[PostReady, UserData]であるためユーザーデータに

version: 1.0
tasks:
  - task: initializeVolume
    inputs:
      initialize: all

と記載すればEC2起動時に追加ボリュームのフォーマットを同時にやってくれます。

この場合のEC2Launchのログを確認すると以下の様な感じです。
PostReadyステージの後にユーザーデータが実行されている事がわかりますね。

・・・前略・・・

2021-09-26 01:04:25 Info: Stage: postReadyLocalData execution completed
2021-09-26 01:04:25 Info: Getting user-data
2021-09-26 01:04:25 Info: 
version: 1.0
tasks:
  - task: initializeVolume
    inputs:
      initialize: all
2021-09-26 01:04:25 Info: Try parsing user-data in yaml format
2021-09-26 01:04:25 Info: Initializing user-data state
2021-09-26 01:04:25 Info: User-data state initialized successfully
2021-09-26 01:04:25 Info: Executing initialize volume.
2021-09-26 01:04:26 Info: Running ebsnvme-id.exe: Disk Number: 0
Volume ID: vol-0c229eb058629c42b
Device Name: sda1

Disk Number: 1
Volume ID: vol-0d68c67a1a66c54a8
Device Name: xvdb


2021-09-26 01:04:28 Info: Successfully initialized device xvdb with drive letter D:
2021-09-26 01:04:28 Info: Stage: postReadyUserData execution completed

・・・後略・・・

ユーザーデータに記述可能なタスクは

  • enableJumboFrames : ジャンボフレームの有効化
  • enableOpenSsh : OpenSSHサーバーの自動構築 (参考)
  • executeProgram : 指定プログラムの実行
  • executeScript : 指定スクリプトの実行
  • initializeVolume : 追加EBSボリュームのフォーマット、ドライブレターマッピング
  • optimizeEna : ENA最適化の設定
  • setHostName : ホスト名設定
  • setWallpaper : デスクトップ壁紙の設定
  • startSsm : SSM Agentサービスの起動
  • sysprep : Sysprepの実行
  • writeFile : 指定ファイルの書き込み

となりますので必要に応じて使い分けてください。

補足1. タスクの実行頻度に関して

補足として各タスクの実行頻度はFrequencyに記載されています。

スクリプト実行のexecuteScriptタスクであれば自分でOnceAlwaysを選べるので問題ないのですが変更できないタスクも存在します。

たとえば先述のinitializeVolumeの場合Alwaysのみでありユーザーで変更できません。
このタスクは常にサーバー起動のたびに実行されることになります。
このため一度だけの実行で十分であればユーザーデータ自体を編集して削除してやるといった対処が必要になりますのでご注意ください。

(ユーザーデータを何度も実行したくない場合は直接編集しなければならないケースもある)

補足2. 既存形式の実行

EC2Launch v2では下位互換のため既存形式のユーザーデータもサポートされています。
記事の最初に出した例もそのまま実行することが可能です。

この場合の実行ログは以下の様になり、最初にYAML形式での解析を試み失敗した場合従来のフォーマットでの解析を試みているのが見て取れます。

・・・前略・・・

2021-09-26 01:53:24 Info: Stage: postReadyLocalData execution completed
2021-09-26 01:53:24 Info: Getting user-data
2021-09-26 01:53:24 Info: 
<powershell>
$file = $env:SystemRoot + "\Temp\" + (Get-Date).ToString("MM-dd-yy-hh-mm")
New-Item $file -ItemType file
</powershell>
2021-09-26 01:53:24 Info: Try parsing user-data in yaml format
2021-09-26 01:53:24 Info: Parsing failed, fall back to legacy format
2021-09-26 01:53:24 Info: Converting user-data to yaml format
2021-09-26 01:53:24 Info: Frequency is set to: once
2021-09-26 01:53:24 Info: Run as user is set to: admin
2021-09-26 01:53:24 Info: Script is set to run as a detached process
2021-09-26 01:53:24 Info: PowerShell content detected
2021-09-26 01:53:24 Info: Userdata conversion completed
2021-09-26 01:53:24 Info: Initializing user-data state

・・・中略・・・

2021-09-26 01:53:25 Info: Stage: postReadyUserData execution completed

・・・後略・・・

最後に

以上となります。

恥ずかしながら去年EC2Launch v2の調査をした時点からずっとこのユーザーデータの仕様を誤認してました...
分かってしまえばシンプルな話ですしユーザーデータの表現力も大幅に増しておりとても便利になっています。

Windows Server 2022以外の環境でもEC2Launch v1からv2に移行することは可能です。
ユーザーデータで複雑なことを求められる場合はEC2Launchのバージョン自体を上げてしまうのも一つの方法としてアリでしょう。