EBS ボリューム ID とデバイス名とドライブレターの関連を Systems Manager で確認したい
先日 Windows Server でボリューム ID とドライブレターを関連付ける記事を書きました。 nasrinjp1.hatenablog.com
これ単純に PowerShell 実行してるだけなので、Systems Manager で簡単にできるんじゃないの?って思ったのでやってみました。 簡単は簡単なんですが、ちょっとだけてこずりました。
とりあえず PowerShell スクリプトを実行するドキュメントを作成
Systems Manager の既存の RunCommand ドキュメントから適当に aws:runPowerShellScript を実行してるものを探して、そのコンテンツを参考にスクリプトを埋め込みます。 前回記事で書いたスクリプトを実行するように書いた JSON が↓です。 JSON の中に PowerShell を書く場合は、下記に注意しましょう。
{ "schemaVersion": "1.2", "description": "Mapping Disks to Volumes on Your Windows Instance.", "runtimeConfig": { "aws:runPowerShellScript": { "properties": [ { "id": "0.aws:runPowerShellScript", "timeoutSeconds": 7200, "runCommand": [ "function Get-EC2InstanceMetadata", "{", " param([string]$Path)", " (Invoke-WebRequest -Uri \"http://169.254.169.254/latest/$Path\").Content ", "}", "", "function Convert-SCSITargetIdToDeviceName", "{", " param([int]$SCSITargetId)", " If ($SCSITargetId -eq 0) {", " return \"/dev/sda1\"", " }", " $deviceName = \"xvd\"", " If ($SCSITargetId -gt 25) {", " $deviceName += [char](0x60 + [int]($SCSITargetId / 26))", " }", " $deviceName += [char](0x61 + $SCSITargetId % 26)", " return $deviceName", "}", "", "Try {", " $InstanceId = Get-EC2InstanceMetadata \"meta-data/instance-id\"", " $AZ = Get-EC2InstanceMetadata \"meta-data/placement/availability-zone\"", " $Region = $AZ.Remove($AZ.Length - 1)", " $BlockDeviceMappings = (Get-EC2Instance -Region $Region -Instance $InstanceId).Instances.BlockDeviceMappings", " $VirtualDeviceMap = @{}", " (Get-EC2InstanceMetadata \"meta-data/block-device-mapping\").Split(\"`n\") | ForEach-Object {", " $VirtualDevice = $_", " $BlockDeviceName = Get-EC2InstanceMetadata \"meta-data\/block-device-mapping\/$VirtualDevice\"", " $VirtualDeviceMap[$BlockDeviceName] = $VirtualDevice", " $VirtualDeviceMap[$VirtualDevice] = $BlockDeviceName", " }", "}", "Catch {", " Write-Host \"Could not access the AWS API, therefore, VolumeId is not available. ", " Verify that you provided your access keys.\" -ForegroundColor Yellow", "}", "", "Get-WmiObject -Class Win32_DiskDrive | ForEach-Object {", " $DiskDrive = $_", " $Volumes = Get-WmiObject -Query \"ASSOCIATORS OF {Win32_DiskDrive.DeviceID='$($DiskDrive.DeviceID)'} WHERE AssocClass=Win32_DiskDriveToDiskPartition\" | ForEach-Object {", " $DiskPartition = $_", " Get-WmiObject -Query \"ASSOCIATORS OF {Win32_DiskPartition.DeviceID='$($DiskPartition.DeviceID)'} WHERE AssocClass=Win32_LogicalDiskToPartition\"", " }", " If ($DiskDrive.PNPDeviceID -like \"*PROD_PVDISK*\") {", " $BlockDeviceName = Convert-SCSITargetIdToDeviceName($DiskDrive.SCSITargetId)", " $BlockDevice = $BlockDeviceMappings | Where-Object { $_.DeviceName -eq $BlockDeviceName }", " $VirtualDevice = If ($VirtualDeviceMap.ContainsKey($BlockDeviceName)) { $VirtualDeviceMap[$BlockDeviceName] } Else { $null }", " } ElseIf ($DiskDrive.PNPDeviceID -like \"*PROD_AMAZON_EC2_NVME*\") {", " $BlockDeviceName = Get-EC2InstanceMetadata \"meta-data/block-device-mapping/ephemeral$($DiskDrive.SCSIPort - 2)\"", " $BlockDevice = $null", " $VirtualDevice = If ($VirtualDeviceMap.ContainsKey($BlockDeviceName)) { $VirtualDeviceMap[$BlockDeviceName] } Else { $null }", " } Else {", " $BlockDeviceName = $null", " $BlockDevice = $null", " $VirtualDevice = $null", " }", " New-Object PSObject -Property @{", " Disk = $DiskDrive.Index;", " Partitions = $DiskDrive.Partitions;", " DriveLetter = If ($Volumes -eq $null) { \"N/A\" } Else { $Volumes.DeviceID };", " EbsVolumeId = If ($BlockDevice -eq $null) { \"N/A\" } Else { $BlockDevice.Ebs.VolumeId };", " Device = If ($BlockDeviceName -eq $null) { \"N/A\" } Else { $BlockDeviceName };", " VirtualDevice = If ($VirtualDevice -eq $null) { \"N/A\" } Else { $VirtualDevice };", " VolumeName = If ($Volumes -eq $null) { \"N/A\" } Else { $Volumes.VolumeName };", " }", "} | Sort-Object Disk | Format-Table -AutoSize -Property Disk, Partitions, DriveLetter, EbsVolumeId, Device, VirtualDevice, VolumeName" ] } ] } } }
ざっとドキュメントの作り方を書きます。「Create Document」を押します。
名前は適当につけてください。ドキュメントのタイプは、コマンドのドキュメントを選びましょう。今回はコンテンツは JSON を選んで、↑で書いた JSON をコピペします。これだけです、かんたんですね。
実行してみよう
RunCommand でさきほど作成したドキュメントを実行しましょう。簡単に流れを書きます。
先ほど作成したコマンドのドキュメントを選びます。
実行対象のインスタンスを選択します。
出力オプションを適当に指定します。S3 に出力しておくと日本語のログも文字化けすることなく見れるので、最低限 S3 には出力するようにしましょう。これで実行します。
エラー出た
あれ? なんか Could not access the AWS API, therefore, VolumeId is not available. ってエラー出てるな… これ Catch で指定したエラー文なので、本当のエラーを確認するために Catch の中を下記に書き換えます。
Write-Host $_.Exception.Message
ちなみに、schemaVersion を 1.2 で作ってしまったので、同じドキュメントを更新してバージョン 2 を作ることができません。
とりあえず、さっき作ったドキュメントを削除して再作成しましょう。 そして RunCommand で再実行します。
エラー内容出ました。Windows Server でスクリプト実行した時はすんなり結果出たのに不思議だ。。。
Internet Explorer エンジンを使用できないか、Internet Explorer の初回起動構成が完了していないため、応答のコンテンツを解析できません。UseBasicParsing パラメーターを指定して再試行してください。
なんか Windows Server を起動して一度もログインしてないか IE 開いてないからこのエラーが出てるように見えますね。
UseBasicParsing オプションとは?
このオプションをつけると IE エンジンを使わずにパースするみたいです。逆にこのオプションをつけなければ IE エンジンを使おうとします。 今回の場合は、AMI から Windows Server のインスタンス起動した直後の状態で IE は開いてもないし設定もしていないのでこうなったみたいです。 前回、OS にログインしてやった時は無意識に IE を開いていたんだろうか…?
TechNet フォーラムにかいてありました。
https://social.technet.microsoft.com/Forums/ja-JP/12e5ada3-5fc6-46c5-a504-e8d51719cad1/invokewebrequest203512999226178123981230012475124611251712522124861?forum=powershellja
PowerShell 6.0.0 以降だとこのオプションがついた状態での実行となるようです。 docs.microsoft.com
UseBasicParsing オプションをつけてみる
PowerShell の 4 行目の Invoke-WebRequest に UseBasicParsing オプションを追加します。 大事なことなので 2 回書きますが、schemaVersion を 1.2 で作ってしまったので同じドキュメントを更新してバージョン 2 を作ることができません。 さっき作ったドキュメントを削除して再作成します。 次からは schemaVersion は 2.2 で作ろうと心の底から思いました。
ここまでの変更点を反映したコンテンツの JSON は↓になります。
{ "schemaVersion": "1.2", "description": "Mapping Disks to Volumes on Your Windows Instance.", "runtimeConfig": { "aws:runPowerShellScript": { "properties": [ { "id": "0.aws:runPowerShellScript", "timeoutSeconds": 7200, "runCommand": [ "function Get-EC2InstanceMetadata", "{", " param([string]$Path)", " (Invoke-WebRequest -Uri \"http://169.254.169.254/latest/$Path\" -UseBasicParsing).Content ", "}", "", "function Convert-SCSITargetIdToDeviceName", "{", " param([int]$SCSITargetId)", " If ($SCSITargetId -eq 0) {", " return \"/dev/sda1\"", " }", " $deviceName = \"xvd\"", " If ($SCSITargetId -gt 25) {", " $deviceName += [char](0x60 + [int]($SCSITargetId / 26))", " }", " $deviceName += [char](0x61 + $SCSITargetId % 26)", " return $deviceName", "}", "", "Try {", " $InstanceId = Get-EC2InstanceMetadata \"meta-data/instance-id\"", " $AZ = Get-EC2InstanceMetadata \"meta-data/placement/availability-zone\"", " $Region = $AZ.Remove($AZ.Length - 1)", " $BlockDeviceMappings = (Get-EC2Instance -Region $Region -Instance $InstanceId).Instances.BlockDeviceMappings", " $VirtualDeviceMap = @{}", " (Get-EC2InstanceMetadata \"meta-data/block-device-mapping\").Split(\"`n\") | ForEach-Object {", " $VirtualDevice = $_", " $BlockDeviceName = Get-EC2InstanceMetadata \"meta-data\/block-device-mapping\/$VirtualDevice\"", " $VirtualDeviceMap[$BlockDeviceName] = $VirtualDevice", " $VirtualDeviceMap[$VirtualDevice] = $BlockDeviceName", " }", "}", "Catch {", " Write-Host $_.Exception.Message", "}", "", "Get-WmiObject -Class Win32_DiskDrive | ForEach-Object {", " $DiskDrive = $_", " $Volumes = Get-WmiObject -Query \"ASSOCIATORS OF {Win32_DiskDrive.DeviceID='$($DiskDrive.DeviceID)'} WHERE AssocClass=Win32_DiskDriveToDiskPartition\" | ForEach-Object {", " $DiskPartition = $_", " Get-WmiObject -Query \"ASSOCIATORS OF {Win32_DiskPartition.DeviceID='$($DiskPartition.DeviceID)'} WHERE AssocClass=Win32_LogicalDiskToPartition\"", " }", " If ($DiskDrive.PNPDeviceID -like \"*PROD_PVDISK*\") {", " $BlockDeviceName = Convert-SCSITargetIdToDeviceName($DiskDrive.SCSITargetId)", " $BlockDevice = $BlockDeviceMappings | Where-Object { $_.DeviceName -eq $BlockDeviceName }", " $VirtualDevice = If ($VirtualDeviceMap.ContainsKey($BlockDeviceName)) { $VirtualDeviceMap[$BlockDeviceName] } Else { $null }", " } ElseIf ($DiskDrive.PNPDeviceID -like \"*PROD_AMAZON_EC2_NVME*\") {", " $BlockDeviceName = Get-EC2InstanceMetadata \"meta-data/block-device-mapping/ephemeral$($DiskDrive.SCSIPort - 2)\"", " $BlockDevice = $null", " $VirtualDevice = If ($VirtualDeviceMap.ContainsKey($BlockDeviceName)) { $VirtualDeviceMap[$BlockDeviceName] } Else { $null }", " } Else {", " $BlockDeviceName = $null", " $BlockDevice = $null", " $VirtualDevice = $null", " }", " New-Object PSObject -Property @{", " Disk = $DiskDrive.Index;", " Partitions = $DiskDrive.Partitions;", " DriveLetter = If ($Volumes -eq $null) { \"N/A\" } Else { $Volumes.DeviceID };", " EbsVolumeId = If ($BlockDevice -eq $null) { \"N/A\" } Else { $BlockDevice.Ebs.VolumeId };", " Device = If ($BlockDeviceName -eq $null) { \"N/A\" } Else { $BlockDeviceName };", " VirtualDevice = If ($VirtualDevice -eq $null) { \"N/A\" } Else { $VirtualDevice };", " VolumeName = If ($Volumes -eq $null) { \"N/A\" } Else { $Volumes.VolumeName };", " }", "} | Sort-Object Disk | Format-Table -AutoSize -Property Disk, Partitions, DriveLetter, EbsVolumeId, Device, VirtualDevice, VolumeName" ] } ] } } }
さあ今度こそ!
無事成功しました!
Disk Partitions DriveLetter EbsVolumeId Device VirtualDevice VolumeName ---- ---------- ----------- ----------- ------ ------------- ----- 0 1 C: vol-0cec1838471c7db24 /dev/sda1 root 1 0 G: vol-06bce859498e0fa05 xvdb ebs2 G drive 2 0 H: vol-0ed34a2e652141916 xvdc ebs3 H drive 3 0 I: vol-06a9e298faeec098b xvdd ebs4 I drive
これで OS にログインしなくても確認できますね
OS にログインしてしまうと権限によってはなんでもできてしまうので、オペミス等で変な操作してしまって業務に影響及ぼしてしまうとかありますが、Systems Manager を使えばそれもなくなりますね。 ログも簡単に残せるので OS 上でのコマンド作業は積極的に Systems Manager を使うようにしていきましょう。
あと、RunCommand ドキュメントの schemaVersion は 2.2 で書きましょう!