I often use Virtual Hard Disk (VHD or VHDX) files for different purposes then disks for virtual machines (VMs).
A few examples:
- Container for temporary data
- It is possible to use it in home environment for file backups or for differential backups using Windows Server Backup.
- I like to use VHD as data disk (D:) on my old Microsoft Surface version 1. I won Surface v1 and I still use it at home. The only problem of my Surface that it has small disk. It is not a good idea to create more partitions because more partitions means more wasted space (every partition must have enough free space). To keep application data and system on different partitions I decided to create VHD and mount it using Scheduled Task when system starts.
The only problem of VHD and PowerShell that it is not possible to use *-VHD cmdlets (New-VHD, Mount-VHD) on a devices without Hyper-V role (for example on my Microsoft Surface). The only possibility is to use PowerShell Storage module (Mount-DiskImage) or Dism module (Mount-WindowsImage) to mount VHD and diskpart to create it.
I decided it to write a new module with following cmdlets (advanced functions with module manifest):
- New-RvVirtualDisk
- Mount-RvVirtualDisk
- Dismount-RvVirtualDisk
Cmdlets (advanced functions) have a lot of possibilities how to use them:
- It is possible to run it locally or to define ComputerName to run it on remote device (server) or to set Session parameter and use already established session to remote device (server).
- New-RvVirtualDisk will not only create VHD or VHDX but it can also format it and assign defined drive letter or Mount Point.
- Mount-RvVirtualDisk will not only mount defined VHD or VHDX but it can also assign access path (drive letter or Mount Point) to existing partition on mounted disk.
- New-RvVirtualDisk and Mount-RvVirtualDisk return CimInstance (from Get-Volume) that contains DriveLetter property so it possible for example script another operations that automatically copy data to mounted disk.
Examples
Use established session; Create new disk, format it and keep it mounted
$sessionItem = New-PSSession -ComputerName cont2test0.ad1.contoso.com
New-RvVirtualDisk -Path 'C:\Temp\NewDisk.vhdx' -Mount:$true `
-Session $sessionItem
Disconnect-PSSession -Session $sessionItem | Remove-PSSession
Invoke commands on multiple servers; Create new disk, format it, specify drive letter (or Mount Point) and file system label and do not keep the disk mounted
New-RvVirtualDisk -Path 'C:\Temp\NewDisk2.vhdx' `
-SizeBytes 100GB `
-Dynamic:$true `
-Format:$true `
-AccessPath Z: `
-VolumeLabel 'My new storage' `
-Mount:$false `
-ComputerName cont2test0.ad1.contoso.com, cont2test1.ad1.contoso.com
Invoke commands on multiple servers; Mount disk and if partition does not have access path (drive letter or Mount Point) then assign available drive letter
Mount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-AddAccessPath:$true `
-ComputerName cont2test0.ad1.contoso.com, cont2test1.ad1.contoso.com
Mount and add or change drive letter
Mount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-AddAccessPath:$true `
-AccessPath Z:
Dismount piped VHDX files
Get-DiskImage -ImagePath 'C:\Temp\NewDisk.vhdx' |
Dismount-RvVirtualDisk
New-RvVirtualDisk
Function New-RvVirtualDisk
{
<#
.SYNOPSIS
Create new VHD or VHDX file (virtual disk).
.DESCRIPTION
Developer
Developer: Rudolf Vesely, http://rudolfvesely.com/
Copyright (c) Rudolf Vesely. All rights reserved
License: Free for private use only
"RV" are initials of the developer's name Rudolf Vesely and distingue names of Rudolf Vesely's cmdlets from the other cmdlets.
Description
Create new VHD or VHDX file (virtual disk).
Cmdlet is functional on devices where it is not possible to use *-VHD cmdlets because the device does not have installed Hyper-V role.
Requirements
Developed and tested using PowerShell 4.0.
.PARAMETER Path
Path to the VHD or VHDX file.
.PARAMETER SizeBytes
Size in Bytes.
.PARAMETER Dynamic
Possibilities
$true (default)
Dynamically expanding virtual disk
$false
Virtual disk with fixed size.
.PARAMETER Format
Initialize disk (GPT), create partition, volume and format (NTFS).
.PARAMETER AccessPath
If specified and volume is formatted then partition will get specified access path.
Possibilities
Drive letter (only): X
Drive letter: X:
Mount Point: "D:\My disk"
If not specified and volume is formatted then available drive letter will be assigned to the new partition.
.PARAMETER VolumeLabel
Defined file system label for the new volume.
.PARAMETER Mount
If $false then the new disk will not be mounted or will be dismounted after formatting.
.PARAMETER Force
Remove already existing file and create new.
.EXAMPLE
EXAMPLE: Local: Create new disk, format it and keep it mounted'
New-RvVirtualDisk -Path 'C:\Temp\NewDisk.vhdx' `
-SizeBytes 100GB `
-Dynamic:$true `
-Format:$true `
-Mount:$true
.EXAMPLE
'Local: Create new disk, format it, specify drive letter (or Mount Point) and volume label and keep it mounted'
New-RvVirtualDisk -Path 'C:\Temp\NewDisk2.vhdx' `
-SizeBytes 100GB `
-Dynamic:$true `
-Format:$true `
-AccessPath Z: `
-VolumeLabel 'My new storage' `
-Mount:$true `
-ComputerName cont2test0.ad1.contoso.com
.EXAMPLE
'Local: Create new disk, overwrite existing file, format it but do not keep it mounted'
New-RvVirtualDisk -Path 'C:\Temp\NewDisk.vhdx' `
-SizeBytes 100GB `
-Dynamic:$true `
-Format:$true `
-Mount:$false `
-Force:$true
.EXAMPLE
'ComputerName: Create new disk, format it and keep it mounted'
New-RvVirtualDisk -Path 'C:\Temp\NewDisk.vhdx' -Mount:$true `
-ComputerName cont2test0.ad1.contoso.com, cont2test0.ad1.contoso.com
.EXAMPLE
'Session: Create new disk, format it and keep it mounted'
$sessionItem = New-PSSession -ComputerName cont2test0.ad1.contoso.com
New-RvVirtualDisk -Path 'C:\Temp\NewDisk.vhdx' -Mount:$true `
-Session $sessionItem
Disconnect-PSSession -Session $sessionItem | Remove-PSSession
.INPUTS
.OUTPUTS
Output from Get-Volume:
Microsoft.Management.Infrastructure.CimInstance
.LINK
https://techstronghold.com/
#>
[CmdletBinding(
DefaultParametersetName = 'Path',
SupportsShouldProcess = $true,
PositionalBinding = $false,
HelpURI = 'https://techstronghold.com/',
ConfirmImpact = 'Medium'
)]
Param
(
[Parameter(
Mandatory = $false,
Position = 0,
ParameterSetName = 'Path',
ValueFromPipelineByPropertyName = $true
)]
[ValidateLength(1, 255)]
[Alias('FullName')]
[string[]]$Path,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[int64]$SizeBytes = 127GB,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[switch]$Dynamic = $true,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[switch]$Format = $true,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[string]$AccessPath,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[string]$VolumeLabel = 'Data',
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[switch]$Mount = $true,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[switch]$Force,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[AllowNull()]
[ValidateLength(1, 255)]
[string[]]$ComputerName,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[AllowNull()]
[System.Management.Automation.Runspaces.PSSession[]]$Session
)
Begin
{
# Configurations
$ErrorActionPreference = 'Stop'
if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' }
Set-PSDebug -Strict
Set-StrictMode -Version Latest
#region Functions
Function New-RvVirtualDiskProcess
{
[CmdletBinding(
DefaultParametersetName = 'Disk',
SupportsShouldProcess = $true,
PositionalBinding = $true,
HelpURI = 'https://techstronghold.com/',
ConfirmImpact = 'Medium'
)]
Param
(
[Parameter(
Mandatory = $true,
Position = 0,
ParameterSetName = 'Disk'
)]
[string[]]$Path,
[Parameter(
Mandatory = $true,
Position = 1
# ParameterSetName = ''
)]
[int64]$SizeBytes,
[Parameter(
Mandatory = $true,
Position = 2
# ParameterSetName = ''
)]
[bool]$Dynamic,
[Parameter(
Mandatory = $true,
Position = 3
# ParameterSetName = ''
)]
[bool]$Format,
[Parameter(
Mandatory = $true,
Position = 4
# ParameterSetName = ''
)]
[AllowNull()]
[AllowEmptyString()]
[string]$AccessPath,
[Parameter(
Mandatory = $true,
Position = 5
# ParameterSetName = ''
)]
[AllowNull()]
[AllowEmptyString()]
[string]$VolumeLabel,
[Parameter(
Mandatory = $true,
Position = 6
# ParameterSetName = ''
)]
[bool]$Mount,
[Parameter(
Mandatory = $true,
Position = 7
# ParameterSetName = ''
)]
[bool]$Force
)
Begin
{
# Configurations
$ErrorActionPreference = 'Stop'
if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' }
Set-PSDebug -Strict
Set-StrictMode -Version Latest
}
Process
{
foreach ($pathItem in $Path)
{
<#
Create
#>
# Check if already exists
if (Test-Path -Path $pathItem -PathType Leaf)
{
if ($Force)
{
Write-Warning -Message ('Item will be removed: {0}' -f $pathItem)
Remove-Item -Path $pathItem
}
else
{
Write-Error -Message ('Item already exists: {0}' -f $pathItem)
}
}
# Create parent directory
elseif (!(Test-Path -Path (Split-Path -Path $pathItem -Parent) -PathType Container))
{
New-Item -Path (Split-Path -Path $pathItem -Parent) -ItemType directory | Out-Null
}
# diskpart: Command
# create vdisk file= {[type=<fixed|expandable>] | [parent=] | [source=]} [maximum=] [sd=] [noerr]
$diskpartCommand = 'create vdisk file="{0}"' -f $pathItem
if ($Dynamic)
{
$diskpartCommand += ' type=expandable maximum={0}' -f [int64]($SizeBytes / 1MB)
}
else
{
$diskpartCommand += ' type=fixed'
}
# diskpart: Run
Write-Debug -Message ('diskpart command: {0}' -f $diskpartCommand)
$diskpartOutput = $diskpartCommand | diskpart
if ($diskpartOutput -match 'DiskPart successfully')
{
Write-Debug -Message 'OK'
}
else
{
Write-Error -Message ('Error during trial to create file: {0}' -f $pathItem)
}
<#
Mount and format
#>
if ($Format -or $Mount)
{
$vhdItem = Mount-DiskImage -ImagePath $pathItem -PassThru
if ($Format)
{
$vhdItem | Get-DiskImage |
Get-Disk |
Initialize-Disk -PartitionStyle GPT -PassThru |
New-Partition -UseMaximumSize -OutVariable partitionItem |
Format-Volume -FileSystem NTFS -NewFileSystemLabel $VolumeLabel -Confirm:$false |
Out-Null
if ($AccessPath)
{
# Drive letter only (only single character)
if ($AccessPath -match '^[a-zA-Z]$')
{
$accessPathItem = '{0}:' -f $AccessPath.ToUpper()
}
# Mount Point
elseif ($AccessPath -match '^[a-zA-Z]:\\\w')
{
if (!(Test-Path -Path $AccessPath -PathType Container))
{
New-Item -Path (Split-Path -Path $pathItem -Parent) `
-ItemType directory | Out-Null
}
$accessPathItem = $AccessPath
}
# Other, for example X:
else
{
$accessPathItem = $AccessPath
}
Write-Verbose -Message ('Add access path: {0}' -f $accessPathItem)
$partitionItem | Add-PartitionAccessPath -AccessPath $accessPathItem
}
else
{
Write-Verbose -Message 'Add available drive letter'
$partitionItem | Add-PartitionAccessPath -AssignDriveLetter
}
# Return
$partitionItem | Get-Volume
}
else
{
# Return
$vhdItem | Get-DiskImage |
Get-Disk
}
if (!$Mount)
{
$vhdItem | Dismount-DiskImage
}
}
}
}
End
{
}
}
#endregion
}
Process
{
# Remote device: Session
if ($Session)
{
foreach ($sessionItem in $Session)
{
Invoke-Command `
-Session $sessionItem `
-ArgumentList $Path, $SizeBytes, $Dynamic, $Format, $AccessPath, $VolumeLabel, $Mount, $Force `
-ScriptBlock ${Function:New-RvVirtualDiskProcess}
}
}
else
{
foreach ($computerNameItem in $(if ($ComputerName) { $ComputerName } else { '.' } ))
{
# Local device
if ($computerNameItem -eq '.' -or $computerNameItem -eq $env:COMPUTERNAME)
{
New-RvVirtualDiskProcess `
-Path $Path `
-SizeBytes $SizeBytes `
-Dynamic:$Dynamic `
-Format:$Format `
-AccessPath $AccessPath `
-VolumeLabel $VolumeLabel `
-Mount:$Mount `
-Force:$Force
}
# Remote device: ComputerName
else # if ($ComputerName)
{
Invoke-Command `
-ComputerName $computerNameItem `
-ArgumentList $Path, $SizeBytes, $Dynamic, $Format, $AccessPath, $VolumeLabel, $Mount, $Force `
-ScriptBlock ${Function:New-RvVirtualDiskProcess}
}
}
}
}
End
{
}
}
Mount-RvVirtualDisk
Function Mount-RvVirtualDisk
{
<#
.SYNOPSIS
Mount VHD or VHDX file (virtual disk).
.DESCRIPTION
Developer
Developer: Rudolf Vesely, http://rudolfvesely.com/
Copyright (c) Rudolf Vesely. All rights reserved
License: Free for private use only
"RV" are initials of the developer's name Rudolf Vesely and distingue names of Rudolf Vesely's cmdlets from the other cmdlets.
Description
Mount VHD or VHDX file (virtual disk).
Cmdlet is functional on devices where it is not possible to use *-VHD cmdlets because the device does not have installed Hyper-V role.
Requirements
Developed and tested using PowerShell 4.0.
.PARAMETER Path
Path to the VHD or VHDX file.
.PARAMETER DiskImage
Output from Get-DiskImage (Microsoft.Management.Infrastructure.CimInstance).
.PARAMETER AddAccessPath
If first partition on the mounted disk does not have access path (drive letter or Mount Point) then add it.
if -AccessPath parameter is not specified and the first partition does not have existing access path then add available drive letter.
.PARAMETER AccessPath
If specified then first partition will get specified access path.
Possibilities
Drive letter (only): X
Drive letter: X:
Mount Point: "D:\My disk"
.EXAMPLE
'Local: Mount and do not try to make the partition accessible'
Mount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-AddAccessPath:$false
.EXAMPLE
'Local: Mount and if partition does not have access path (drive letter or Mount Point) then assign available drive letter'
Mount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-AddAccessPath:$true
.EXAMPLE
'Local: Mount and if partition does not have defined access path then assign defined drive letter or Mount Point'
Mount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-AddAccessPath:$true `
-AccessPath Z:
.EXAMPLE
'ComputerName: Mount and do not try to make the partition accessible'
Mount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-AddAccessPath:$false `
-ComputerName cont2test0.ad1.contoso.com, cont2test0.ad1.contoso.com
.EXAMPLE
'Session: Mount and do not try to make the partition accessible'
$sessionItem = New-PSSession -ComputerName cont2test0.ad1.contoso.com
Mount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-AddAccessPath:$false `
-Session $sessionItem
Disconnect-PSSession -Session $sessionItem | Remove-PSSession
.INPUTS
Output from Get-DiskImage:
Microsoft.Management.Infrastructure.CimInstance
.OUTPUTS
Output from Get-Volume:
Microsoft.Management.Infrastructure.CimInstance
.LINK
https://techstronghold.com/
#>
[CmdletBinding(
DefaultParametersetName = 'DiskImage',
SupportsShouldProcess = $true,
PositionalBinding = $false,
HelpURI = 'https://techstronghold.com/',
ConfirmImpact = 'Medium'
)]
Param
(
[Parameter(
Mandatory = $true,
Position = 0,
ParameterSetName = 'Path',
ValueFromPipelineByPropertyName = $true
)]
[ValidateLength(1, 255)]
[Alias('FullName')]
[string[]]$Path,
[Parameter(
Mandatory = $true,
Position = 0,
ParameterSetName = 'DiskImage',
ValueFromPipeline = $true
)]
[Microsoft.Management.Infrastructure.CimInstance[]]$DiskImage,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[switch]$AddAccessPath = $true,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[string]$AccessPath,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[AllowNull()]
[ValidateLength(1, 255)]
[string[]]$ComputerName,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[AllowNull()]
[System.Management.Automation.Runspaces.PSSession[]]$Session
)
Begin
{
# Configurations
$ErrorActionPreference = 'Stop'
if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' }
Set-PSDebug -Strict
Set-StrictMode -Version Latest
#region Functions
Function Mount-RvVirtualDiskProcess
{
[CmdletBinding(
DefaultParametersetName = 'Disk',
SupportsShouldProcess = $true,
PositionalBinding = $true,
HelpURI = 'https://techstronghold.com/',
ConfirmImpact = 'Medium'
)]
Param
(
[Parameter(
Mandatory = $true,
Position = 0,
ParameterSetName = 'Disk'
)]
[AllowNull()]
[string[]]$Path,
[Parameter(
Mandatory = $true,
Position = 1
# ParameterSetName = ''
)]
[AllowNull()]
[Microsoft.Management.Infrastructure.CimInstance[]]$DiskImage,
[Parameter(
Mandatory = $true,
Position = 2
# ParameterSetName = ''
)]
[bool]$AddAccessPath,
[Parameter(
Mandatory = $true,
Position = 3
# ParameterSetName = ''
)]
[AllowNull()]
[AllowEmptyString()]
[string]$AccessPath
)
Begin
{
# Configurations
$ErrorActionPreference = 'Stop'
if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' }
Set-PSDebug -Strict
Set-StrictMode -Version Latest
}
Process
{
<#
Mount
#>
$diskImageItems = @()
foreach ($pathItem in $Path)
{
$diskImageItems += Mount-DiskImage -ImagePath $pathItem -PassThru | Get-DiskImage
}
foreach ($diskImageItem in $DiskImage)
{
$diskImageItems += Mount-DiskImage -ImagePath $diskImageItem.ImagePath -PassThru | Get-DiskImage
}
<#
Access paths
#>
foreach ($diskImageItem in $diskImageItems)
{
# Correct defind access path
$mountPoint = $false
if ($AccessPath)
{
# Drive letter only (only single character)
if ($AccessPath -match '^[a-zA-Z]$')
{
$accessPathItem = '{0}:' -f $AccessPath.ToUpper()
}
# Mount Point
elseif ($AccessPath -match '^[a-zA-Z]:\\\w')
{
$accessPathItem = $AccessPath
$mountPoint = $true
}
# Other, for example X:
else
{
$accessPathItem = $AccessPath
}
# Remove last backslash for further comparison
$accessPathItem -replace '\\$', ''
}
else
{
$accessPathItem = $null
}
# Add access path
if ($AddAccessPath)
{
$partitionItem = $diskImageItem |
Get-Disk |
Get-Partition |
Where-Object -Property Type -EQ Basic |
Select-Object -First 1
# Get access paths without last backslash and do not include partition GUID
$partitionItemAccessPathItems = $partitionItem |
Select-Object -ExpandProperty AccessPaths |
Where-Object -FilterScript { $_ -notmatch '^\\\\\?' } |
ForEach-Object -Process { $_ -replace '\\$', '' }
# Add specific access path
if ($accessPathItem)
{
if ($partitionItemAccessPathItems -contains $accessPathItem)
{
Write-Verbose -Message ('Partition has defined access path; Current access paths: {0}' -f ($partitionItemAccessPathItems -join ', '))
}
else
{
if ($mountPoint -and !(Test-Path -Path $accessPathItem -PathType Container))
{
New-Item -Path $accessPathItem -ItemType directory | Out-Null
}
Write-Verbose -Message ('Partition does not have defined access path; Add: {0}' -f $accessPathItem)
$partitionItem | Add-PartitionAccessPath -AccessPath $accessPathItem
}
}
# Add any access path
else
{
if ($partitionItemAccessPathItems)
{
Write-Verbose -Message ('Partition has following access paths: {0}' -f ($partitionItemAccessPathItems -join ', '))
}
else
{
Write-Verbose -Message 'No access path; Assign available drive letter'
$partitionItem | Add-PartitionAccessPath -AssignDriveLetter
}
}
# Return
$partitionItem | Get-Volume
}
else
{
# Return
$diskImageItem | Get-Disk | Get-Partition | Get-Volume
}
}
}
End
{
# Refresh Windows PowerShell Drives
Get-PSDrive | Out-Null
}
}
#endregion
}
Process
{
# Remote device: Session
if ($Session)
{
foreach ($sessionItem in $Session)
{
Invoke-Command `
-Session $sessionItem `
-ArgumentList $Path, $DiskImage, $AddAccessPath, $AccessPath `
-ScriptBlock ${Function:Mount-RvVirtualDiskProcess}
}
}
else
{
foreach ($computerNameItem in $(if ($ComputerName) { $ComputerName } else { '.' } ))
{
# Local device
if ($computerNameItem -eq '.' -or $computerNameItem -eq $env:COMPUTERNAME)
{
Mount-RvVirtualDiskProcess `
-Path $Path `
-DiskImage $DiskImage `
-AddAccessPath:$AddAccessPath `
-AccessPath $AccessPath
}
# Remote device: ComputerName
else # if ($ComputerName)
{
Invoke-Command `
-ComputerName $computerNameItem `
-ArgumentList $Path, $DiskImage, $AddAccessPath, $AccessPath `
-ScriptBlock ${Function:Mount-RvVirtualDiskProcess}
}
}
}
}
End
{
}
}
Dismount-RvVirtualDisk
Function Dismount-RvVirtualDisk
{
<#
.SYNOPSIS
Dismount VHD or VHDX file (virtual disk).
.DESCRIPTION
Developer
Developer: Rudolf Vesely, http://rudolfvesely.com/
Copyright (c) Rudolf Vesely. All rights reserved
License: Free for private use only
"RV" are initials of the developer's name Rudolf Vesely and distingue names of Rudolf Vesely's cmdlets from the other cmdlets.
Description
Dismount VHD or VHDX file (virtual disk).
Cmdlet is functional on devices where it is not possible to use *-VHD cmdlets because the device does not have installed Hyper-V role.
Requirements
Developed and tested using PowerShell 4.0.
.PARAMETER Path
Path to the VHD or VHDX file.
.PARAMETER DiskImage
Output from Get-DiskImage (Microsoft.Management.Infrastructure.CimInstance).
.EXAMPLE
'Local: Dismount defined VHDX file'
Dismount-RvVirtualDisk -Path 'C:\Temp\NewDisk.vhdx'
.EXAMPLE
'Local: Dismount defined VHDX file; Pipeline'
Get-ChildItem -Path 'C:\Temp' -Filter *.vhdx |
Dismount-RvVirtualDisk
.EXAMPLE
'Local: Dismount defined VHDX file; Pipeline'
Get-DiskImage -ImagePath 'C:\Temp\NewDisk.vhdx' |
Dismount-RvVirtualDisk
.EXAMPLE
'ComputerName: Dismount defined VHDX file'
Dismount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-ComputerName cont2test0.ad1.contoso.com, cont2test0.ad1.contoso.com
.EXAMPLE
'Session: Dismount defined VHDX file'
$sessionItem = New-PSSession -ComputerName cont2test0.ad1.contoso.com
Dismount-RvVirtualDisk `
-Path 'C:\Temp\NewDisk.vhdx' `
-Session $sessionItem
Disconnect-PSSession -Session $sessionItem | Remove-PSSession
.INPUTS
Output from Get-DiskImage:
Microsoft.Management.Infrastructure.CimInstance
.OUTPUTS
.LINK
https://techstronghold.com/
#>
[CmdletBinding(
DefaultParametersetName = 'DiskImage',
SupportsShouldProcess = $true,
PositionalBinding = $false,
HelpURI = 'https://techstronghold.com/',
ConfirmImpact = 'Medium'
)]
Param
(
[Parameter(
Mandatory = $true,
Position = 0,
ParameterSetName = 'Path',
ValueFromPipelineByPropertyName = $true
)]
[ValidateLength(1, 255)]
[Alias('FullName')]
[string[]]$Path,
[Parameter(
Mandatory = $true,
Position = 0,
ParameterSetName = 'DiskImage',
ValueFromPipeline = $true
)]
[Microsoft.Management.Infrastructure.CimInstance[]]$DiskImage,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[AllowNull()]
[ValidateLength(1, 255)]
[string[]]$ComputerName,
[Parameter(
Mandatory = $false
# Position = ,
# ParameterSetName = ''
)]
[AllowNull()]
[System.Management.Automation.Runspaces.PSSession[]]$Session
)
Begin
{
# Configurations
$ErrorActionPreference = 'Stop'
if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' }
Set-PSDebug -Strict
Set-StrictMode -Version Latest
}
Process
{
if ($PSCmdlet.ParameterSetName -eq 'Path')
{
$pathItems = $Path
}
else # if ($PSCmdlet.ParameterSetName -eq 'DiskImage')
{
$pathItems = $DiskImage.ImagePath
}
# Remote device: Session
if ($Session)
{
foreach ($sessionItem in $Session)
{
Invoke-Command `
-Session $sessionItem `
-ScriptBlock { Dismount-DiskImage -ImagePath $Using:pathItems }
}
}
else
{
foreach ($computerNameItem in $(if ($ComputerName) { $ComputerName } else { '.' } ))
{
# Local device
if ($computerNameItem -eq '.' -or $computerNameItem -eq $env:COMPUTERNAME)
{
Dismount-RvVirtualDiskProcess `
-Path $pathItems
}
# Remote device: ComputerName
else # if ($ComputerName)
{
Invoke-Command `
-ComputerName $computerNameItem `
-ScriptBlock { Dismount-DiskImage -ImagePath $Using:pathItems }
}
}
}
}
End
{
}
}

One response to “PowerShell advanced function (cmdlet) to create, format, mount and dismount VHD and VHDX files on local and remote computers”
Function New-RvVirtualDisk :
$diskpartCommand += ‘ type=fixed’
should be :
$diskpartCommand += ‘ type=fixed’ maximum={0}’ -f [int64]($SizeBytes / 1MB)
?