Tag Archives: VHD

PowerShell.org’s Azure Journey, Part 4: Incoming Advice and Fun Facts


Had an opportunity to speak with some folks on the Azure team yesterday – Mark Russinovich was kind enough to make a contact for me.

First of all, fun fact: Azure only charges you for used pages in VHDs. That is, if you create a 100GB VHD and load 1GB of data on it, you’re paying for 1GB of data. Very clever. So it’s charging you as if it was a dynamically expanding VHD, but of course it’s a fixed VHD with all of the related performance improvements. Nice.

Second, they basically confirmed something I’d suspected. Azure’s “website model” tends to appeal more to smaller businesses or personal Web sites; most “serious” players (my word) are using the IaaS model, meaning they’re hosting VMs in the cloud, not just hosting a Web site. Having a full VM under your control obviously has advantages in terms of management, along with the ability to run things like in-memory caching software, load additional Web extensions, and so on. IaaS is absolutely the right model for PowerShell.org for many of those reasons.

That said, they also confirmed that the Web site model and the IaaS model cost about the same, at least as you get started. So it’s really – for a smaller Web site – a matter of what you want to do. Again, there are specifics about the IaaS model that work well for us, so that’s what we’re looking to do.

Azure also costs about the same, in an apples-to-apples comparison, as Amazon Web Services. That’s probably somewhat deliberate on Microsoft’s part, but Azure has advantages. For one, their virtualization layer has been approved by the various Microsoft product teams, so if you’re running SharePoint or SQL Server in an Azure VM, the team will support you. Not the case with AWS. Also, I frankly found Azure’s presentation of the costs easier to grok.

Fifth (I love numbered lists, sorry), I confirmed that the IaaS option charges you for (a) the VM’s you’re running, by the minute; (b) the storage used by all VM VHDs’ used pages, and (c) outbound bandwidth. This can potentially make IaaS more expensive than the “website” model because Azure won’t spin down an IaaS VM, so you run 24×7 unless you’re manually deallocating. With a website, Azure only spins up worker processes when they’re needed, so your site isn’t “running” 24×7, so you might pay less if it’s not being “hit” 24×7. Again, though, the website model offers us less control and flexibility.

Just thought you’d enjoy some of those details!

Test Hyper-V VHD Folders with PowerShell


I’ve recently added a USB 3.0 Express Card adapter to my laptop to provide USB 3.0 functionality. The added performance is very useful in my Hyper-V setup. However, I am running into a glitch that I have yet to figure out where the external drives (the Express card has 2 ports) are still attached and recognized, but the file system is wonky. I can see most of the folders, and files but not really. This is a problem when I fire up a Hyper-V virtual machine with a VHD in one of these folders. Even though I can run a DIR command and see the file, it really isn’t there. So I threw together a little function to add to my Hyper-V workflow module to validate folders with VHD and VHDX files.

My virtual machines are spread out among a number of drives. I don’t necessarily need to validate every VHD file, but I would like to know that the paths exist. Now, I could simply pass my list of paths to Test-Path.

PS C:\> "G:\VHDs","F:\VHD","D:\VHD","C:\Users\Public\Documents\Hyper-V\Virtual Hard Disks" | Test-Path
True
True
True
True

But my little glitch can lead to a false positive. So I need to take an extra step and validate one of the VHD/VHDX files using the Test-VHD cmdlet. Here’s my function.

#requires -version 3.0

Function Test-VHDPath {

[cmdletbinding()]
Param (
[ValidateNotNullorEmpty()]
[ValidateScript({Test-Path $_})]
#paths to my virtual hard disks for Hyper-V virtual machines
[string[]]$paths = @("G:\VHDs","F:\VHD","D:\VHD","C:\Users\Public\Documents\Hyper-V\Virtual Hard Disks")
)

Write-Verbose "Starting $($MyInvocation.mycommand)"

foreach ($path in $paths) {
  Try {
    #grab the first VHD\VHDX file and test it. No guarantee other files are OK
    #but at least I know the path has been verified.
    Write-Verbose "Validating $path"
    dir $path\*.vhd,*.vhdx | Select -first 1 | Test-VHD -ErrorAction Stop | out-null
  }
  Catch {
    Write-Error "Failed to validate VHD\VHDX files in $Path. $_.Exception.Message"
    Return
  }
}

#if no errors were found then return a simple True
Write-Verbose "No problems found with VHD paths"
Write $True

} #end function

The function gets the first VHD or VHDX file in each folder and tests it. If the test fails, Test-VHD throws an exception which I catch and then bail out. Otherwise, if there are no errors, the function writes $True to the pipeline. Otherwise, I’ll get an exception.

test-vhdpathBut now I have a tool to quickly verify paths before I start trying to launch virtual machines.

If you’d like to learn more about Test-VHD, take a look at my article on the Altaro Hyper-V blog. If you want to try out my function, you should be able to toggle to plain code and copy and paste.

 

Test Hyper-V VHD Folders with PowerShell


I’ve recently added a USB 3.0 Express Card adapter to my laptop to provide USB 3.0 functionality. The added performance is very useful in my Hyper-V setup. However, I am running into a glitch that I have yet to figure out where the external drives (the Express card has 2 ports) are still attached and recognized, but the file system is wonky. I can see most of the folders, and files but not really. This is a problem when I fire up a Hyper-V virtual machine with a VHD in one of these folders. Even though I can run a DIR command and see the file, it really isn’t there. So I threw together a little function to add to my Hyper-V workflow module to validate folders with VHD and VHDX files.

My virtual machines are spread out among a number of drives. I don’t necessarily need to validate every VHD file, but I would like to know that the paths exist. Now, I could simply pass my list of paths to Test-Path.

PS C:\> "G:\VHDs","F:\VHD","D:\VHD","C:\Users\Public\Documents\Hyper-V\Virtual Hard Disks" | Test-Path
True
True
True
True

But my little glitch can lead to a false positive. So I need to take an extra step and validate one of the VHD/VHDX files using the Test-VHD cmdlet. Here’s my function.

#requires -version 3.0

Function Test-VHDPath {

[cmdletbinding()]
Param (
[ValidateNotNullorEmpty()]
[ValidateScript({Test-Path $_})]
#paths to my virtual hard disks for Hyper-V virtual machines
[string[]]$paths = @("G:\VHDs","F:\VHD","D:\VHD","C:\Users\Public\Documents\Hyper-V\Virtual Hard Disks")
)

Write-Verbose "Starting $($MyInvocation.mycommand)"

foreach ($path in $paths) {
  Try {
    #grab the first VHD\VHDX file and test it. No guarantee other files are OK
    #but at least I know the path has been verified.
    Write-Verbose "Validating $path"
    dir $path\*.vhd,*.vhdx | Select -first 1 | Test-VHD -ErrorAction Stop | out-null
  }
  Catch {
    Write-Error "Failed to validate VHD\VHDX files in $Path. $_.Exception.Message"
    Return
  }
}

#if no errors were found then return a simple True
Write-Verbose "No problems found with VHD paths"
Write $True

} #end function

The function gets the first VHD or VHDX file in each folder and tests it. If the test fails, Test-VHD throws an exception which I catch and then bail out. Otherwise, if there are no errors, the function writes $True to the pipeline. Otherwise, I’ll get an exception.

test-vhdpathBut now I have a tool to quickly verify paths before I start trying to launch virtual machines.

If you’d like to learn more about Test-VHD, take a look at my article on the Altaro Hyper-V blog. If you want to try out my function, you should be able to toggle to plain code and copy and paste.

 

Hyper-V VHD Summary


When I made the move to Windows 8, one of my tasks was to migrate my test environment from VirtualBox to Hyper-V. Windows 8 includes a client Hyper-V feature that is easy to use and includes PowerShell support. Plus I needed to expand my Hyper-V skills anyway, especially from the console. After setting up a number of VMs, I realized I needed to get a handle on disks: what files were in use for which VM? The Get-VM cmdlet writes a VirtualMachine object to the pipeline. One of its properties is HardDrives.

PS S:\> $vm = get-vm "XP Lab"
PS S:\> $vm.GetType().Name
VirtualMachine
PS S:\> $vm | select *

VMName                      : XP Lab
VMId                        : da416538-7a72-4b57-b08d-7780d81745b6
Id                          : da416538-7a72-4b57-b08d-7780d81745b6
Name                        : XP Lab
State                       : Off
OperationalStatus           : {Ok}
PrimaryOperationalStatus    : Ok
SecondaryOperationalStatus  :
StatusDescriptions          : {Operating normally}
PrimaryStatusDescription    : Operating normally
SecondaryStatusDescription  :
Status                      : Operating normally
Heartbeat                   :
ReplicationState            : Disabled
ReplicationHealth           : NotApplicable
ReplicationMode             : None
CPUUsage                    : 0
MemoryAssigned              : 0
MemoryDemand                : 0
MemoryStatus                :
SmartPagingFileInUse        : False
Uptime                      : 00:00:00
IntegrationServicesVersion  :
ResourceMeteringEnabled     : False
ConfigurationLocation       : C:\ProgramData\Microsoft\Windows\Hyper-V
SnapshotFileLocation        : C:\ProgramData\Microsoft\Windows\Hyper-V
AutomaticStartAction        : StartIfRunning
AutomaticStopAction         : Save
AutomaticStartDelay         : 0
SmartPagingFilePath         : C:\ProgramData\Microsoft\Windows\Hyper-V
NumaAligned                 :
NumaNodesCount              : 1
NumaSocketCount             : 1
IsDeleted                   : False
ComputerName                : SERENITY
Notes                       :
Path                        : C:\ProgramData\Microsoft\Windows\Hyper-V
CreationTime                : 8/20/2012 5:43:38 PM
IsClustered                 : False
SizeOfSystemFiles           : 29366
ParentSnapshotId            :
ParentSnapshotName          :
MemoryStartup               : 536870912
DynamicMemoryEnabled        : False
MemoryMinimum               : 536870912
MemoryMaximum               : 1099511627776
ProcessorCount              : 1
RemoteFxAdapter             :
NetworkAdapters             : {Network Adapter}
FibreChannelHostBusAdapters : {}
ComPort1                    : Microsoft.HyperV.PowerShell.VMComPort
ComPort2                    : Microsoft.HyperV.PowerShell.VMComPort
FloppyDrive                 : Microsoft.HyperV.PowerShell.VMFloppyDiskDrive
DVDDrives                   : {DVD Drive on IDE controller number 1 at location 0}
HardDrives                  : {Hard Drive on IDE controller number 0 at location 0}
VMIntegrationService        : {Time Synchronization, Heartbeat, Key-Value Pair Exchange, Shutdown…}

The property is actually a nested object.

PS S:\> $vm.HardDrives

VMName ControllerType ControllerNumber ControllerLocation DiskNumber Path
—— ————– —————- —————— ———- —-
XP Lab IDE            0                0                             D:\VHD\XPLab.vhd

That tells me where the VHD is, but not about it. For that I can use Get-VHD.

PS S:\> $vm.HardDrives | Get-VHD

ComputerName            : SERENITY
Path                    : D:\VHD\XPLab.vhd
VhdFormat               : VHD
VhdType                 : Dynamic
FileSize                : 8709523456
Size                    : 10737418240
MinimumSize             : 10725765120
LogicalSectorSize       : 512
PhysicalSectorSize      : 512
BlockSize               : 2097152
ParentPath              :
FragmentationPercentage : 0
Alignment               : 0
Attached                : False
DiskNumber              :
IsDeleted               : False
Number                  :

There is some great information here like sizes, type and format. All I need now is a way to combine all of this information so that for every VM I can get a summary of pertinent VHD information. This is a great scenario for using Select-Object and creating a custom property.

$vm.HardDrives | Select VMName,Path,@{Name="Size";Expression={ (Get-VHD $_.path).Size}}

VMName                                    Path                                                             Size
——                                    —-                                                             —-
XP Lab                                    D:\VHD\XPLab.vhd                                          10737418240

In the hash table, the Expression script block is running Get-VHD and returning the Size property. Eventually I want this for all VMs which means I’ll need to expand the HardDrives property.

$vm | select -expandproperty HardDrives | Select VMName,Path,@{Name="Size";Expression={ (Get-VHD $_.path).Size}},
@{Name="FileSize";Expression={(Get-VHD $_.path).FileSize}}

VMName                         Path                                                     Size                       FileSize
——                         —-                                                     —-                       ——–
XP Lab                         D:\VHD\XPLab.vhd                                  10737418240                     8709523456

That is very promising. In fact, let’s cut to the chase.

#requires -version 3.0

Function Get-VHDSummary {

<#
.Synopsis
Get Hyper-V VHD Information
.Description
Get a summary report of all associated VHDs with their respective virtual
machines. This requires the Hyper-V PowerShell module.
.Example
PS C:\> Get-VHDSummary

VMName     : CHI-Client02
Path       : D:\VHD\Win7_C.vhd
Type       : Dynamic
Format     : VHD
SizeGB     : 25
FileSizeGB : 21

VMName     : CHI-Client02
Path       : C:\Users\Public\Documents\Hyper-V\Virtual Hard Disks\CHI-Client2-Swap.vhdx
Type       : Dynamic
Format     : VHDX
SizeGB     : 4
FileSizeGB : 1

VMName     : Win2012-01
Path       : D:\VHD\Win2012-01_340B1F8F-FFFB-4DCD-9B3A-7819D8BCE3C2.avhdx
Type       : Differencing
Format     : VHDX
SizeGB     : 40
FileSizeGB : 2

.Example
PS C:\> get-vhdsummary | sort Type | format-table -GroupBy Type -Property VMName,Path,*Size*
.Example
PS C:\> get-vhdsummary | Copy-item -destination Z:\VHDBackup
.Example
PS C:\> get-vhdsummary | measure-object -property FilesizeGB -sum
.Link
Get-VM
Get-VHD
#>

Param()

Get-VM  | Select-Object -expandproperty harddrives |
Select-Object -property VMName,Path,
@{Name="Type";Expression={(Get-VHD -Path $_.Path).VhdType}},
@{Name="Format";Expression={(Get-VHD -Path $_.Path).VhdFormat}},
@{Name="SizeGB";Expression={((Get-VHD -Path $_.Path).Size)/1GB -as [int]}},
@{Name="FileSizeGB";Expression={((Get-VHD -Path $_.Path).FileSize)/1GB -as [int]}}

} #end function

This function writes a summary object for each VM which means I can further sort, filter or whatever I need to do. Because this can take a bit of time to run, I’ll save the results to a variable.

PS S:\> $summary = get-vhdsummary

One unexpected bonus was that it helped me identify a VM with a “bad” VHD reference.

PS S:\> $summary | where {-Not $_.type}

VMName     : Windows Server 2012 VHD Boot
Path       : C:\Win8BetaServer.vhd
Type       :
Format     :
SizeGB     : 0
FileSizeGB : 0

This was a file I had renamed, which “broke” the virtual machine. Or I can see how much space my VHD files are taking.

PS S:\> $summary  | measure FileSizeGB -sum

Count    : 18
Average  :
Sum      : 102
Maximum  :
Minimum  :
Property : FileSizeGB

Or perhaps I need to back them all up.

PS S:\> $summary | copy -dest g:\vhd-backup -whatif
What if: Performing operation "Copy File" on Target "Item: D:\VHD\Win7_C.vhd Destination: G:\vhd-backup\Win7_C.vhd".

Awesome. But…..my function is based on code I developed to run from the command line which works great as a one line command. I’m not likely to have a great number of virtual machines so performance isn’t that big a deal. Plus I could always run it as a job. If I’m going to turn this into a function, perhaps it makes sense to break this up.

If you look at my function, I’m running Get-VHD multiple times for the same file. It probably makes more sense to only get it once. Here’s a revised block of code.

Function Get-VHDSummary2 {
Param()

#get all virtual machines
$vms=Get-VM

foreach ($vm in $vms) {
  Write-Host "Getting drive info from $($vm.name)" -foregroundcolor Cyan
  #get the hard drives foreach virtual machine
  $vm.HardDrives | foreach-object {
      #a VM might have multiple drives so for each one get the VHD
      $vhd=Get-VHD -path $_.path

      <#
       $_ is the hard drive object so select a few properties and
       include properties from the VHD
      #>

      $_ | Select-Object -property VMName,Path,
        @{Name="Type";Expression={$vhd.VhdType}},
        @{Name="Format";Expression={$vhd.VhdFormat}},
        @{Name="SizeGB";Expression={($vhd.Size)/1GB -as [int]}},
        @{Name="FileSizeGB";Expression={($vhd.FileSize)/1GB -as [int]}}
 } #foreach
} #foreach vm

} #close function

I’ll get the same result, and actually faster. My first version took 7.8 seconds and this takes 2.3 seconds. Because this is a function, I only have to type it once so I can add comments and even a progress message with Write-Host. This isn’t too say I can’t do this all from the console; it is just a bit more tedious.

The purpose of my post is to not only demonstrate some of the Hyper-V cmdlets, but also that what you type at the console doesn’t always make the best PowerShell script. Sometimes you need to re-think things for performance and maintainability.

Download Get-VHDSummary.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati