EC2Launchで追加ディスクの初期設定を自動化する

しばたです。

Windows Server 2012 R2までのEC2インスタンスでは、ルートデバイス以外に追加のボリュームを指定してインスタンスを作成した場合にEC2Configの機能により自動でフォーマットされドライブレターの割り当てまで行ってくれます。
具体的にはEc2InitializeDrivesプラグインがこの役目を果たしており、このプラグインは既定で有効なため自動で処理が動作します。

(Ec2InitializeDrivesプラグインは既定で有効)

簡単な例を挙げると、ルートデバイス以外に2つのEBSボリュームを追加したWindows Server 2012 R2インスタンス作成すると、初回起動の時点で以下の様に各ディスクはNTFSフォーマットされドライブレターも割り当てられた状態になっています。

これがWindows Server 2016だと、初回起動時点ではルートデバイス(Cドライブ)以外のディスクはオフラインのままであり、ディスクのフォーマットは自分で行ってやる必要があります。

(ディスク構成は同様でOSをWindows Server 2016に変えた場合)

原因

原因としてはWindows Server 2016ではEC2Configに変わってEC2Launchが導入されており、このEC2LaunchにはEc2InitializeDrivesプラグインが無いことが直接的な原因です。

InitializeDisks.ps1

ではEC2Launchに同様の機能が無いのかというとそうではなく、InitializeDisks.ps1というスクリプトファイルがEc2InitializeDrivesプラグインと同等の機能を持っています。
ただこのスクリプトは既定で自動実行されないためEC2Configと異なる挙動を示します。

スクリプトの場所

EC2LaunchはC:\ProgramData\Amazon\EC2-Windows\Launchにあり、そのサブフォルダに設定ファイルやスクリプトファイルが保存されています。
InitializeDisks.ps1は、

  • C:\ProgramData\Amazon\EC2-Windows\Launch\Script\InitializeDisks.ps1

に存在します。

スクリプトの仕様

このスクリプトは、すべてのディスクを走査し、オフラインのディスクがあればオンラインにしてフォーマットおよびドライブレターの割り当てを行います。
引数は以下の2つあるのですが通常は引数無しで実行して構いません。

  • (Switch) Schedule : このパラメーターを指定した場合、ディスクのフォーマットを次回起動時にスケジュールします
  • (Switch) EnableTrim : このパラメーターを指定した場合、ディスクに対してTRIMコマンドを有効にします
    • 内部的には fsutil behavior set DisableDeleteNotify NTFS 0 コマンドを発行します

スクリプトの実行結果はログファイルに保存され、ファイルは、

  • C:\ProgramData\Amazon\EC2-Windows\Launch\Log\DiskInitialization.log

になります。

InitializeDisks.ps1 を実行してみる

それでは実際にInitializeDisks.ps1を実行してみます。
本記事では1台のWindows Server 2016に対してSSM Run Commandからこのスクリプトを実行してみます。
(SSM Run Commandを実行できるところまでは設定済みの想定です)

1. コマンド指定

AWS Systems Managerのコマンド実行画面からAWS-RunPowerShellScriptコマンドを選択します。

2. 実行スクリプト

実行するスクリプトは以下。
InitializeDisks.ps1を実行しログファイルの内容を出力するものにしています。
設定変更後の内容を確認できる様にGet-DiskGet-PSDriveを最後に呼び出しています。

SSM Run CommandでPowerShellスクリプトを実行した場合、コンソール幅が80の想定になっている様で、その調整のためにいくつかの箇所で| Out-Stringを使用しています。
この部分は他の環境で実行する場合は不要です。

 
# InitializeDisks.ps1 スクリプトを実行
& (Join-Path $env:ProgramData 'Amazon\EC2-Windows\Launch\Scripts\InitializeDisks.ps1')
if (-not $?) {
    # エラー発生時
    Get-Content (Join-Path $env:ProgramData 'Amazon\EC2-Windows\Launch\Log\DiskInitialization.log') | Out-String
    exit $LASTEXITCODE
}
Get-Content (Join-Path $env:ProgramData 'Amazon\EC2-Windows\Launch\Log\DiskInitialization.log') | Out-String

# 更新後の設定を確認
Write-Host 'Get currnet disk and drives...'
Get-Disk | Format-Table -AutoSize | Out-String -Width 80
Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize | Out-String -Width 80

このスクリプトをCommnandパラメーターに貼り付けてやります。

3. ターゲット指定など

ターゲットに該当のWindows Server 2016を指定し、その他パラメーターは環境に応じて設定してください。

4. コマンド実行

コマンドを実行してエラーなく完了すればOKです。

5. 結果の確認

コマンドを実行した結果のログは以下の様な感じになります。
InitializeDisks.ps1の処理の経過と最後にGet-DiskGet-PSDriveの結果が表示され、きちんとD、Eドライブが作成されていることがわかります。

コマンド出力に表示されるのは 2500 文字までです。コマンドを実行するときに S3 バケットまたは CloudWatch ロググループを指定した場合、完全なコマンド出力は、Amazon S3 または CloudWatch ログで確認することができます。

2019/03/01 05:41:48Z: Initializing disks started
2019/03/01 05:41:48Z: Disable TRIM
2019/03/01 05:41:48Z: Found Disk Name:\\.\PHYSICALDRIVE0; Index:0; SizeBytes:42944186880;
2019/03/01 05:41:51Z: Partition already exists: PartitionNumber 1; DriverLetter C
2019/03/01 05:41:51Z: Found Disk Name:\\.\PHYSICALDRIVE1; Index:1; SizeBytes:10733990400;
2019/03/01 05:41:51Z: Initializing disk 1 begins
2019/03/01 05:41:51Z: Initializing the disk ...
2019/03/01 05:41:52Z: Looking for drive letter ...
2019/03/01 05:41:52Z: Creating partition with drive letter D ...
2019/03/01 05:41:52Z: Formatting the volume ...
2019/03/01 05:41:55Z: Disk 1 is successfully initialized, partitioned and formatted
2019/03/01 05:41:55Z: Found Disk Name:\\.\PHYSICALDRIVE2; Index:2; SizeBytes:21467980800;
2019/03/01 05:41:55Z: Initializing disk 2 begins
2019/03/01 05:41:55Z: Initializing the disk ...
2019/03/01 05:41:55Z: Looking for drive letter ...
2019/03/01 05:41:56Z: Creating partition with drive letter E ...
2019/03/01 05:41:56Z: Formatting the volume ...
2019/03/01 05:41:59Z: Disk 2 is successfully initialized, partitioned and formatted
2019/03/01 05:41:59Z: Reading drive letter mapping config from file
2019/03/01 05:41:59Z: Finished reading drive letter mapping config:
Volume Name: Sample Volume; Drive Letter: D
2019/03/01 05:41:59Z: Volume name "Sample Volume" doesn't exist.. skipping it
2019/03/01 05:41:59Z: Initializing disks done successfully
2019/03/01 05:41:59Z: Enable TRIM

Get currnet disk and drives...
Number Friendly Name Serial Number HealthStatus OperationalStatus Total 
Size
------ ------------- ------------- ------------ ----------------- -------
0 AWS PVDISK vol0e9cd4e2ae0832d0c Healthy Online 40 GB
1 AWS PVDISK vol0e73a5a2169e214ae Healthy Online 10 GB
2 AWS PVDISK vol0cffac1deafb0f1cf Healthy Online 20 GB

Name Used (GB) Free (GB) Provider Root CurrentLocation
---- --------- --------- -------- ---- ---------------
C 14.56 25.44 FileSystem C:\ Windows\system32
D 0.04 9.96 FileSystem D:\ 
E 0.06 19.94 FileSystem E:\ 

最後にサーバーにログインしてみると、ちゃんとドライブレターが割り当てられていることが確認できます。

ちょっと補足

本記事ではSSM Run Commandで試しましたがそれ以外の環境、例えばユーザーデータから実行することももちろん可能です。

<powershell>
# Initialize disks
& (Join-Path $env:ProgramData 'Amazon\EC2-Windows\Launch\Scripts\InitializeDisks.ps1')
</powershell>

ユーザーデータだと結果を直接参照することはできないので上記の様に単純にスクリプトを実行するだけの記述をしておけば良いでしょう。

参考

InitializeDisks.ps1に関するほぼ唯一のドキュメント。
DriveLetterMappingConfig.jsonを設定することでドライブレターの割り当てを明示することができることについても触れられています。
(EC2Configの [Drive Letter Mapping] 設定画面に相当)