PoshCode Logo PowerShell Code Repository

Here’s several functions for working with VHD’s in Windows 7. I’ve been working with PowerShell for about a year and this is my first go at a module. I’m a sysadmin and not a developer so some of my solutions are in that mode of thinking. There’s probably .NET ways to accomplish what I did and I’m certainly open to learning if there’s a better way. I’ve found these functions useful and hopefully someone else out there will too. Enjoy.

  1. <#
  2.  
  3. Name: VHDFunctions.psm1
  4. Author: Rich Kusak (rkusak@cbcag.edu)
  5. Created: 2009-10-23
  6. LastEdit: 2009-11-02 15:54
  7.  
  8. Included Functions:
  9.         Dismount-VHD
  10.         Initialize-VHD
  11.         Mount-VHD
  12.         New-VHD
  13.         Set-VHDBootConfiguration
  14.         Test-VHD
  15.        
  16. #>
  17.  
  18. <#
  19. .SYNOPSIS
  20.         Dismount a VHD file from the system.
  21.  
  22. .DESCRIPTION
  23.         This function wraps the consistancy of PowerShell around the Diskpart utility.
  24.         A Diskpart script is created to automate the dismount (detach) of a VHD file from the system.
  25.         Optionally, the VHD file can be deleted following detachment.
  26.        
  27. .PARAMETER Path
  28.         Specifies the full path to the VHD file.
  29.  
  30. .PARAMETER Remove
  31.         Removes (deletes) the VHD file after dismounting it.
  32.  
  33. .PARAMETER NoConfirm
  34.         Supresses the delete confirmation prompt.
  35.  
  36. .PARAMETER DiskpartScript
  37.         Specifies the path location of the Diskpart script file.
  38.         Default location is $env:SystemDrive
  39.         This file is deleted at the conclusion of the script.
  40.  
  41. .PARAMETER Rescan
  42.         Instructs Diskpart to rescan the system for available storage resources.
  43.        
  44. .EXAMPLE
  45.         Dismount-VHD -Path C:\test.vhd
  46.         Dismounts the specified VHD file.
  47.        
  48. .EXAMPLE
  49.         Dismount-VHD -Path C:\test.vhd -Remove
  50.         Dismounts the specified VHD file and then deletes it.
  51.  
  52. .NOTES
  53.         Name: Dismount-VHD.ps1
  54.         Author: Rich Kusak
  55.         Created: 2009-10-22
  56.         LastEdit: 2009-10-26 11:35
  57.        
  58.         #Requires -Version 2.0
  59. #>
  60. function Dismount-VHD {
  61.         [CmdletBinding()]
  62.         param (
  63.                 [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
  64.                         [string]$Path,
  65.                 [switch]$Remove,
  66.                 [switch]$NoConfirm,
  67.                 [string]$DiskpartScript = "$env:SystemDrive\DiskpartScript.txt",
  68.                 [switch]$Rescan
  69.         )
  70.        
  71.         begin {
  72.                 function InvokeDiskpart {
  73.                         Diskpart.exe /s $DiskpartScript
  74.                 }
  75.                
  76.                 function RemoveVHD {
  77.                         switch ($NoConfirm) {
  78.                                 $false {
  79.                                         ## Prompt for confirmation to delete the VHD file ##
  80.                                         "" ; Write-Warning "Are you sure you want to delete the file ""$Path""?"
  81.                                         $Prompt = Read-Host "Type ""YES"" to continue or anything else to break"
  82.                                         if ($Prompt -ceq 'YES') {
  83.                                                 Remove-Item -Path $Path -Force
  84.                                                 "" ; Write-Host "VHD ""$Path"" deleted!" ; ""
  85.                                         } else {
  86.                                                 "" ; Write-Host "Script terminated without deleting the VHD file." ; ""
  87.                                         }
  88.                                 }
  89.                                 $true {
  90.                                         ## Confirmation prompt suppressed ##
  91.                                         Remove-Item -Path $Path -Force
  92.                                         "" ; Write-Host "VHD ""$Path"" deleted!" ; ""
  93.                                 }
  94.                         }
  95.                 }
  96.                 ## Validate Operating System Version ##
  97.                 if (Get-WmiObject win32_OperatingSystem -Filter "Version < '6.1'") {throw "The script operation requires at least Windows 7."}
  98.         }
  99.         process{
  100.         ## DiskPart Script Content ## Here-String statement purposefully not indented ##
  101. @"
  102. $(if ($Rescan) {'Rescan'})
  103. Select VDisk File="$Path"`nDetach VDisk
  104. Exit
  105. "@ | Out-File -FilePath $DiskpartScript -Encoding ASCII -Force
  106.                 InvokeDiskpart
  107.         }
  108.         end {
  109.                 if ($Remove) {RemoveVHD}
  110.                 Remove-Item -Path $DiskpartScript -Force ; ""
  111.         }
  112. }
  113. Export-ModuleMember -Function Dismount-VHD
  114.  
  115.  
  116. <#
  117. .SYNOPSIS
  118.         Initialize a VHD by preparing it for use.
  119.  
  120. .DESCRIPTION
  121.         This function wraps the consistancy of PowerShell around the Diskpart utility.
  122.         A Diskpart script is created to automate initializing a VHD.
  123.         The script creates a partition, assigns a drive letter, and formats a mounted VHD.
  124.        
  125. .PARAMETER Path
  126.         Specifies the full path to the VHD file.
  127.  
  128. .PARAMETER Drive
  129.         A drive letter to assign to the mounted VHD.
  130.         If not specified the system will auto assign the next available drive letter.
  131.  
  132. .PARAMETER Label
  133.         A volume label to assign to the mounted VHD.
  134.  
  135. .PARAMETER DiskpartScript
  136.         Specifies the path location of the Diskpart script file.
  137.         Default location is $env:SystemDrive
  138.         This file is deleted at the conclusion of the script.
  139.  
  140. .PARAMETER Rescan
  141.         Instructs Diskpart to rescan the system for available storage resources.
  142.  
  143. .EXAMPLE
  144.         Initialize-VHD C:\test.vhd X: TestVHD
  145.         Initializes the VHD at path C:\test.vhd assign it to drive letter X: and give it the volume label "TestVHD".
  146.  
  147. .NOTES
  148.         Name: Initialize-VHD
  149.         Author: Rich Kusak
  150.         Created: 2009-10-22
  151.         LastEdit: 2009-10-26 15:11
  152.        
  153.         #Requires -Version 2.0
  154. #>
  155. function Initialize-VHD {
  156.         [CmdletBinding()]
  157.         param (
  158.                 [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
  159.                         [string]$Path,
  160.                 [Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$false)]
  161.                         [string]$Drive,
  162.                 [Parameter(Position=2,Mandatory=$false,ValueFromPipeline=$false)]
  163.                         [string]$Label,
  164.                 [Parameter(Position=3,Mandatory=$false,ValueFromPipeline=$false)]
  165.                         [string]$DiskpartScript = "$env:SystemDrive\DiskpartScript.txt",
  166.                 [switch]$NoConfirm,
  167.                 [switch]$Rescan
  168.         )
  169.        
  170.         begin {
  171.                 function InvokeDiskpart {
  172.                         Diskpart.exe /s $DiskpartScript
  173.                 }
  174.        
  175.                 ## Validate Operating System Version ##
  176.                 if (Get-WmiObject win32_OperatingSystem -Filter "Version < '6.1'") {throw "The script operation requires at least Windows 7."}
  177.                 ## Validate -Drive parameter ##
  178.                 if ($Drive) {
  179.                         $Reserved = @('A:','B:','C:')
  180.                         $Reserved += (Get-WmiObject win32_LogicalDisk -Property DeviceID | ForEach-Object {$_.DeviceID})
  181.                         switch ($Drive) {
  182.                                 {($_ -notmatch "^[a-z]$") -and ($_ -notmatch "^[a-z]:$")} {throw "The drive letter ""$_"" is invalid."}
  183.                                 {$_ -notmatch ":"} {$Drive += ":"}
  184.                                 {$Reserved -contains $Drive} {throw "The drive letter ""$_"" is reserved."}
  185.                         }
  186.                 }
  187.                 if (!$NoConfirm) {
  188.                         "" ; Write-Warning "The VHD ""$Path"" is about to initialized. Any existing data will be destroyed!`nAre you sure you want to continue?" ; ""
  189.                         $Prompt = Read-Host "Type ""YES"" to continue or anything else to break"
  190.                         if ($Prompt -cne 'YES') {Write-Host "Function terminated by user."; "" ; break}
  191.                 }
  192.         }
  193.         process {
  194.                 ## Diskpart Script Content ## Here-String statement purposefully not indented ##
  195. @"
  196. $(if ($Rescan) {'Rescan'})
  197. Select VDisk File="$Path"
  198. Clean
  199. Create Partition Primary
  200. Format Quick FS=NTFS $(if ($Label) {"Label=""$Label"""})
  201. $(if ($Drive) {"Assign Letter=$Drive"} else {'Assign'})
  202. Detail VDisk
  203. Exit
  204. "@ | Out-File -FilePath $DiskpartScript -Encoding ASCII -Force
  205.         }
  206.         end {
  207.                 InvokeDiskpart
  208.                 Remove-Item -Path $DiskpartScript -Force ; ""
  209.                 Write-Host "The VHD ""$Path"" has been successfully initialized." ; ""
  210.         }
  211. }
  212. Export-ModuleMember -Function Initialize-VHD
  213.  
  214.  
  215. <#
  216. .SYNOPSIS
  217.         Mount a VHD to the system.
  218.  
  219. .DESCRIPTION
  220.         This function wraps the consistancy of PowerShell around the Diskpart utility.
  221.         A Diskpart script is created to automate mounting (attach) a VHD file to the system.
  222.        
  223. .PARAMETER Path
  224.         Specifies the full path to the VHD file.
  225.        
  226. .PARAMETER DiskpartScript
  227.         Specifies the path location of the Diskpart script file.
  228.         Default location is $env:SystemDrive
  229.         This file is deleted at the conclusion of the script.
  230.  
  231. .PARAMETER Rescan
  232.         Instructs Diskpart to rescan the system for available storage resources.
  233.  
  234. .NOTES
  235.         Name: Mount-VHD.ps1
  236.         Author: Rich Kusak
  237.         Created: 2009-10-22
  238.         LastEdit: 2009-10-26 09:25
  239.        
  240.         #Requires -Version 2.0
  241. #>
  242. function Mount-VHD {
  243.         [CmdletBinding()]
  244.         param (
  245.                 [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
  246.                         [string]$Path,
  247.                 [string]$DiskpartScript = "$env:SystemDrive\DiskpartScript.txt",
  248.                 [switch]$Rescan
  249.         )
  250.        
  251.         begin {
  252.                 function InvokeDiskpart {
  253.                         Diskpart.exe /s $DiskpartScript
  254.                 }
  255.                 ## Validate Operating System Version ##
  256.                 if (Get-WmiObject win32_OperatingSystem -Filter "Version < '6.1'") {throw "The script operation requires at least Windows 7."}
  257.         }
  258.         process{
  259.                 ## Diskpart Script Content ## Here-String statement purposefully not indented ##
  260. @"
  261. $(if ($Rescan) {'Rescan'})
  262. Select VDisk File="$Path"`nAttach VDisk
  263. Exit
  264. "@ | Out-File -FilePath $DiskpartScript -Encoding ASCII -Force
  265.                 InvokeDiskpart
  266.         }
  267.         end {
  268.                 Remove-Item -Path $DiskpartScript -Force ; ""
  269.                 Write-Host "The VHD ""$Path"" has been successfully mounted." ; ""
  270.         }
  271. }
  272. Export-ModuleMember -Function Mount-VHD
  273.  
  274.  
  275. <#
  276. .SYNOPSIS
  277.         Create a new VHD file.
  278.  
  279. .DESCRIPTION
  280.         This function wraps the consistancy of PowerShell around the Diskpart utility.
  281.         A Diskpart script is created to automate the creation of the VHD.
  282.         Optionally, the VHD can be mounted immediately following the creation process.
  283.        
  284. .PARAMETER Path
  285.         Specifies the full path to the VHD file.
  286.        
  287. .PARAMETER Maximum
  288.         The maximum space allocated for the VHD to use.
  289.  
  290. .PARAMETER Fixed
  291.         Creates a fixed disk VHD file. By default a dynamically expanding VHD file is created.
  292.        
  293. .PARAMETER Mount
  294.         Mount (attach) the VHD to the system making it available to Windows.
  295.  
  296. .PARAMETER Prepare
  297.         Prepares the VHD for use by partitioning, Mount (attach) the VHD to the system making it available to Windows.
  298.  
  299. .PARAMETER NoConfirm
  300.         Supresses the maximum validation warning confirmation prompt.
  301.        
  302. .PARAMETER DiskpartScript
  303.         Specifies the path location of the Diskpart script file.
  304.         Default location is $env:SystemDrive
  305.         This file is deleted at the conclusion of the script.
  306.  
  307. .PARAMETER Rescan
  308.         Instructs Diskpart to rescan the system for available storage resources.
  309.  
  310. .NOTES
  311.         Name: New-VHD
  312.         Author: Rich Kusak
  313.         Created: 2009-05-27
  314.         LastEdit: 2009-10-26 10:06
  315.        
  316.         #Requires -Version 2.0
  317. #>
  318. function New-VHD {
  319.         [CmdletBinding()]
  320.         param (
  321.                 [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
  322.                         [string]$Path,
  323.                 [Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$false)]
  324.                         [int]$Maximum,
  325.                 [switch]$Fixed,
  326.                 [switch]$Mount,
  327.                 [switch]$NoConfirm,
  328.                 [string]$DiskpartScript = "$env:SystemDrive\DiskpartScript.txt",
  329.                 [switch]$Rescan
  330.         )
  331.        
  332.         begin {
  333.                 function InvokeDiskpart {
  334.                         Diskpart.exe /s $DiskpartScript
  335.                 }
  336.                 function TestMaximum {
  337.                         ## Validates the -Maximum parameter against the specified disk's available space ##
  338.                         $Drive = Split-Path $Path -Qualifier
  339.                         $LogicalDisk = Get-WmiObject win32_LogicalDisk -Filter "DeviceID = '$Drive'"
  340.                         $FreeSpace = [math]::Truncate(($LogicalDisk.FreeSpace)/1MB)
  341.                         $Percent = [math]::Round(($Maximum/$FreeSpace)*100,0)
  342.                         switch ($Maximum) {
  343.                                 {$_ -gt 2088960} {throw "The -Maximum parameter value ""$Maximum"" exceeds the allowable VHD size."}
  344.                                 {$_ -ge $FreeSpace -and $Fixed} {throw "The -Maximum parameter value ""$Maximum"" exceeds available disk space."}
  345.                                 {$_ -ge $FreeSpace} {
  346.                                         "" ; Write-Warning "The VHD maximum size exceeds available disk space."
  347.                                         if (!$NoConfirm) {
  348.                                                 Write-Host "Are you sure you want to continue?"
  349.                                                 $Prompt = Read-Host "Type ""YES"" to continue or anything else to break"
  350.                                                 if ($Prompt -cne 'YES') {
  351.                                                         "" ; Write-Host "Script terminiated by user." ; ""
  352.                                                         return
  353.                                                 }
  354.                                         }
  355.                                 }
  356.                                 {$Percent -ge 80} {
  357.                                         "" ; Write-Warning "The VHD maximum size is $Percent% of the available disk space."
  358.                                         if (!$NoConfirm) {
  359.                                                 Write-Host "Are you sure you want to continue?"
  360.                                                 $Prompt = Read-Host "Type ""YES"" to continue or anything else to break"
  361.                                                 if ($Prompt -cne 'YES') {
  362.                                                         "" ; Write-Host "Script terminiated by user." ; ""
  363.                                                         return
  364.                                                 }
  365.                                         }
  366.                                 }
  367.                         }
  368.                         ## Validate Operating System Version ##
  369.                         if (Get-WmiObject win32_OperatingSystem -Filter "Version < '6.1'") {throw "The script operation requires at least Windows 7."}
  370.                 }
  371.         }
  372.         process {
  373.                 TestMaximum
  374.                 ## Diskpart Script Content ## Here-String statement purposefully not indented ##
  375. @"
  376. $(if ($Rescan) {'Rescan'})
  377. Create VDisk File="$Path" Maximum=$Maximum $(if ($Fixed) {'Type=Fixed'} else {'Type=Expandable'})
  378. $(if ($Mount) {"Select VDisk File=""$Path""`nAttach VDisk"})
  379. Exit
  380. "@ | Out-File -FilePath $DiskpartScript -Encoding ASCII -Force
  381.                 InvokeDiskpart
  382.         }
  383.         end {
  384.                 Remove-Item -Path $DiskpartScript -Force ; ""
  385.                 Write-Host "A new VHD has been created at ""$Path""." ; ""
  386.                 if ($Mount) {Write-Host "The VHD has been successfully mounted." ; ""}
  387.         }
  388. }
  389. Export-ModuleMember -Function New-VHD
  390.  
  391.  
  392. <#
  393. .SYNOPSIS
  394.         Set the system boot configuration to boot from a VHD.
  395.  
  396. .DESCRIPTION
  397.         This function wraps the consistancy of PowerShell around the BCDEdit tool.
  398.         The Boot Configuration DataStore Editor (BCDEdit) is used to set the necessary
  399.         boot configuration entry to optionally boot to a VHD during startup.
  400.        
  401. .PARAMETER Path
  402.         Specifies the full path to the VHD file.
  403.  
  404. .PARAMETER Description
  405.         Description for the boot configuration entry.
  406.        
  407. .EXAMPLE
  408.         Set-VHDBootConfiguration       
  409.  
  410. .NOTES
  411.         Name: Set-VHDBootConfiguration
  412.         Author: Rich Kusak
  413.         Created: 2009-10-22
  414.         LastEdit: 2009-10-26 10:14
  415.        
  416.         #Requires -Version 2.0
  417. #>
  418. function Set-VHDBootConfiguration {
  419.         [CmdletBinding()]
  420.         param (
  421.                 [Parameter(Position=0,Mandatory=$true)]
  422.                         [string]$Path,
  423.                 [Parameter(Position=1,Mandatory=$true)]
  424.                         [string]$Description
  425.         )
  426.        
  427.         begin {
  428.                 if (!(Test-Path -Path $Path -Include "*.vhd")) {throw "The path ""$Path"" is invalid or does not contain a VHD file."}
  429.                 $Drive = Split-Path -Path $Path -Qualifier
  430.                 $UnQualifiedPath = Split-Path -Path $Path -NoQualifier
  431.         }
  432.         process {
  433.                 $Copy = bcdedit /copy '{current}' /d $Description
  434.                 $CLSID = $Copy | ForEach-Object {$_.Remove(0,37).Replace(".","")}
  435.                 bcdedit /set $CLSID device vhd=[$Drive]""$UnQualifiedPath""
  436.                 bcdedit /set $CLSID osdevice vhd=[$Drive]""$UnQualifiedPath""
  437.                 bcdedit /set $CLSID detecthal on
  438.                 New-Object PSObject | Add-Member -MemberType NoteProperty -Name 'Identifier' -Value $CLSID -PassThru |
  439.         }
  440.         end {
  441.                 Write-Host "The VHD ""$Path"" has been prepared for boot operation." ; ""
  442.         }
  443. }
  444. Export-ModuleMember -Function Set-VHDBootConfiguration
  445.  
  446.  
  447. <#
  448. .SYNOPSIS
  449.         Test a VHD.
  450.  
  451. .DESCRIPTION
  452.         This script wraps the consistancy of PowerShell around the Diskpart utility.
  453.         A Diskpart script is created to automate initializing a VHD.
  454.         The script creates a partition, assigns a drive letter, and formats a mounted VHD.
  455.        
  456. .PARAMETER Path
  457.         Specifies the full path to the VHD file.
  458.  
  459. .PARAMETER DiskpartScript
  460.         Specifies the path location of the Diskpart script file.
  461.         Default location is $env:SystemDrive
  462.         This file is deleted at the conclusion of the script.
  463.  
  464. .PARAMETER Rescan
  465.         Instructs Diskpart to rescan the system for available storage resources.
  466.  
  467. .NOTES
  468.         Name: Test-VHD.ps1
  469.         Author: Rich Kusak
  470.         Created: 2009-10-23
  471.         LastEdit: 2009-10-26 10:17
  472.        
  473.         #Requires -Version 2.0
  474. #>
  475. function Test-VHD {
  476.         [CmdletBinding()]
  477.         param (
  478.                 [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
  479.                         [string]$Path,
  480.                 [Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$false)]
  481.                         [string]$DiskpartScript = "$env:SystemDrive\DiskpartScript.txt",
  482.                 [switch]$Rescan
  483.         )
  484.  
  485.         begin {
  486.                 function InvokeDiskpart {
  487.                         Diskpart.exe /s $DiskpartScript
  488.                 }
  489.                 if (!(Test-Path -Path $Path -Include "*.vhd")) {throw "The path ""$Path"" is not valid or does not contain a VHD file."}
  490.         }
  491.         process {
  492. @"
  493. Select VDisk File="$Path"
  494. Detail VDisk
  495. "@ | Out-File -FilePath $DiskpartScript -Encoding ASCII -Force
  496.                 InvokeDiskpart
  497.         }
  498.         end {
  499.                 Remove-Item -Path $DiskpartScript -Force ; ""
  500.         }
  501. }
  502. Export-ModuleMember -Function Test-VHD

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me