PowerShellで仮想ディスクを構築/マウント/アンマウントする方法

# Pythonの環境を他の人と共有したい
[PythonUSB](https://gammasoft.jp/blog/python-and-jupyterlab-in-usb-stick/)の環境を仮想ディスクで共有したいと思って、検討してみた。

diskpart でもある程度できるけど、batchから何度もpowershellを呼ぶと効率が良くないと思ってすべてpowershellで書き直してみた。

## config
まずはコンフィグ。仮想ディスクのvolumeはディスクラベルで判別するので、ユニークな名前を。
GetFullPath を使っているのは、パスの正規化のため。
マウントは正規化されてなくてもOKなのに、アンマウントで見つからないと言われた。
なお、```$C_VDISK_NAME```で、```vhdx```にすると拡張子でファイルの種類変えるみたい。

```powershell:00_config.ps1
# カレントパス
$C_CUR_PATH = Split-Path $MyInvocation.MyCommand.Path

# 仮想ディスクのラベル
$C_VDISK_LABEL = "Python"

# 仮想ディスクのファイル名
$C_VDISK_NAME = "Python.vhd"
$C_VDISK_FILE = [IO.Path]::GetFullPath( (Join-Path $C_CUR_PATH $C_VDISK_NAME) )

# 仮想ディスクのマウント先
$C_MOUNT_NAME = "..\Python"
$C_MOUNT_PATH = [IO.Path]::GetFullPath( (Join-Path $C_CUR_PATH $C_MOUNT_NAME) )
```

## 仮想ディスクの構築
管理者権限に移行時、パスが変わるので、スクリプトのパスに初めに戻るようにしている。
仮想ディスクを作って、フォーマットまでします。

なお、このスクリプトでは、bitlockerの暗号化回避できないかと思って、FAT32使ってるけど、意味はなかった。普通にvhdxにしてexFatとかでいいと思います。

```powershell:01_CreateDisk.ps1

Set-Location -LiteralPath $PSScriptRoot

. .\00_config.ps1

if ( Test-Path $C_VDISK_FILE ) {
  echo "既にファイルが存在します"
  exit
}

if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators")) {
  Start-Process powershell.exe "-File `"$PSCommandPath`"" -Verb RunAs -Wait
  exit
}

New-VHD -Path $C_VDISK_FILE -SizeBytes 32GB | Mount-VHD -Passthru | Initialize-Disk -Passthru | New-Partition -UseMaximumSize | Format-Volume -NewFileSystemLabel $C_VDISK_LABEL -FileSystem FAT32 -Confirm:$false -Force

Dismount-VHD $C_VDISK_FILE

```

## 仮想ディスクのマウント
既にマウント済みか、ディスクラベルでチェックし、なければ管理者に移行してマウント。
管理者権限移行時、ウィンドウがじゃなであれば、``` -WindowStyle Hidden```を付ければいいけど、
エラーがわからなくなるので戻しました。
エラーメッセージも消したい場合は、```$ErrorActionPreference = "Stop"```にする必要あり。
これははじめ？？？となった。

```powershell:02_MountDisk.ps1
Set-Location -LiteralPath $PSScriptRoot

. .\00_config.ps1

if ( !(Test-Path $C_VDISK_FILE) ) {
  echo "ファイルが存在しません. " + $C_VDISK_FILE
  exit
}

foreach ( $e in Get-Volume ) {
  if( $e.FileSystemLabel -eq $C_VDISK_LABEL) {
    echo "マウント済みです"
    exit
  }
}


if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators")) {
  # Start-Process powershell.exe "-File `"$PSCommandPath`"" -Verb RunAs -Wait -WindowStyle Hidden
  Start-Process powershell.exe "-File `"$PSCommandPath`"" -Verb RunAs -Wait
  exit
}

$ErrorActionPreference = "Continue"
try {
  Mount-VHD $C_VDISK_FILE
} catch {

}

try {
  Get-Volume -FileSystemLabel $C_VDISK_LABEL | Get-Partition | Add-PartitionAccessPath -AccessPath $C_MOUNT_PATH
} catch {

}
```

## 仮想ディスクのアンマウント
マウント時の反対のことをしているので、特に特記事項はないけど、あえて言えば、
マウントされていないことをチェックするのにちょっと小細工。人によってはわかりにくいのかな・・・

```powershell:03_UnmountDisk.ps1
Set-Location -LiteralPath $PSScriptRoot

. .\00_config.ps1

if ( !(Test-Path $C_VDISK_FILE) ) {
  echo "ファイルが存在しません. " + $C_VDISK_FILE
  exit
}

:MOUNTED do {
  foreach ( $e in Get-Volume ) {
    if( $e.FileSystemLabel -eq $C_VDISK_LABEL) {
      break MOUNTED
    }
  }
  echo "マウントされてません"
  exit
} while( $false )


if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators")) {
  # Start-Process powershell.exe "-File `"$PSCommandPath`"" -Verb RunAs -Wait -WindowStyle Hidden
  Start-Process powershell.exe "-File `"$PSCommandPath`"" -Verb RunAs -Wait
  exit
}

$ErrorActionPreference = "Continue"
try {
  Get-Volume -FileSystemLabel $C_VDISK_LABEL | Get-Partition | Remove-PartitionAccessPath -AccessPath $C_MOUNT_PATH
} catch {

}

try {
  Dismount-VHD $C_VDISK_FILE
} catch {

}
```