Alex Mueller on Software and Technology 
Wednesday, March 25, 2009

I am always looking for tools to allow me to do more by doing less. Administering Hyper-V with PowerShell can be tedious. James O’Neill’s PowerShell Management Library for Hyper-V is a great tool to improve automation of Hyper-V management using PowerShell.

As a tester, I am constantly creating new environments, installing our product, taking snapshots, ect. Doing this by hand is a waste of time in my opinion. Doing this with a script makes life easier and frees up my time.

Typically, I prefer to create is to create one VM and to have other VMs inherit from that base image. My second VM (the first child to inherit from the base), I will install daily builds of our product. I will then create a third child, which I use a sandbox. I can easily delete it and recreate as needed without having to install our product over again. I have not consistently found success with using snapshots, so I prefer to use differencing disks. The examples below use differencing disks.

This first code block shows how I am using the PowerShell management library to simply the creation of a base VM. There is nothing magic about it. It’s pretty strait-forward. I create a VM, give it a name, set the CPU count, memory size, network adapters, hard drive, and DVD drive.

$server = "my-dev-server"
$vmName = "TestParentVM"
$vmVirtualSwitch = "My Virtual Network"

# create a new virtual machine
$vm = New-VM $vmName -server $server

# set cpu count
Set-VMCPUCount $vm 2 -server $server

# set memory size
Set-VMMemory $vm 2 -memory 4GB -server $server

# add a legacy network adapter
Add-VMNIC -vm $vm -VirtualSwitch $vmVirtualSwitch -legacy

# add a default VMBus (non-legacy) network adapter
Add-VMNIC -vm $vm -VirtualSwitch $vmVirtualSwitch

# add the hard drive to the VM
Add-VMNewHardDisk –vm $vm -controllerID 0 -lun 0 -vhdpath "$(get-VHDdefaultPath)\$vmName.vhd" -size 20GB

# add the DVD with bootup ISO
Add-VMDRIVE –vm $vm 1 1 -server $server -DVD 

Write-Host "Operation complete."

The next code block will create a differencing disk pointing to the parent created in the previous example. I create the usual, VM, CPU count, ect. What makes this a differencing disk is how the VHD is created by specifying the parent. I do some cleanup in this routine by deleting any previously created VMs or VHDs, and I loop through the creation a failure preventative.

$server = "my-dev-server"
$vmName = "TestChildVM"
$vmParent = "TestParentVM"
$vmVirtualSwitch = "My Virtual Network"

#region functions - feel free to move to a separate file

Function DeleteVhd
{
    Param ($pathToVhd)
    # delete the disk that was added if it exists
    if (test-path -path $pathToVhd)
    {
        Remove-Item -Path $pathToVhd -Force
    }
}

#endregion

# delete the VHD if it already exists
DeleteVhd "$(get-VHDdefaultPath)\$vmName.vhd"

# delete the VM if it already exists
Remove-VM -vm $vmName -server $server

# create a new virtual machine
$vm = New-VM $vmName -server $server

# get the vm
$vm = Get-VM $vmName

# set cpu count
Set-VMCPUCount $vm 2 -server $server

# set memory size
Set-VMMemory $vm 2 -memory 4GB -server $server

# get VM Nics available
$vmNics = Get-VMNic -server $server -vmbus -legacy
$vmNicSwitchList = New-Object System.Collections.ArrayList

# get VM Nic Switch available for each VM Nic and add it to our list
foreach ($vmNic in $vmNics)
{
    $vmSwitchElementName = (Get-VMNicSwitch $vmNic).ElementName
    if ($vmNicSwitchList.Contains($vmSwitchElementName) -ne $true)
    {
        $vmNicSwitchList.Add($vmSwitchElementName)
    }
}

# Use the first available VM Nic Switch - this assumes 
# I do not create more than on per each environment.
if ($vmNicSwitchList.Count -gt 0)
{
    $vmVirtualSwitch = $vmNicSwitchList[0]
}

# add a legacy network adapter
Add-VMNIC -vm $vm -VirtualSwitch $vmVirtualSwitch -legacy

# add a default VMBus (non-legacy) network adapter
Add-VMNIC -vm $vm -VirtualSwitch $vmVirtualSwitch

# add the hard drive to the VM
$parent = "$(get-VHDdefaultPath)\$vmParent.vhd"
$vhdPath = "$(get-VHDdefaultPath)\$vmName.vhd"
Add-VMNewHardDisk –vm $vm -controllerID 0 -lun 0 -vhdpath $vhdPath -parent $parent

# get the VM to see if a disk is attached to it, if it is not, then it failed
$disks = Get-VMDisk -vm $vm
$count = 1

# if the disk was not successfully added, try and add it again
while ($disks -eq $null -and $count -le 5)
{
    Write-Host "RETRY $count - The disk was not properly added. Attempting to retry."
    
    # delete the disk that was added
    DeleteVhd "$(get-VHDdefaultPath)\$vmName.vhd"
    
    Add-VMNewHardDisk –vm $vm -controllerID 0 -lun 0 -vhdpath $vhdPath -parent $parent
    
    $disks = Get-VMDisk -vm $vm
    $count++
}

if ($disks -eq $null)
{
    Write-Host "Operation failed to create this virtual machine. Contact an admin."
}
else
{
    Write-Host "Operation completed successfully. Attempting to start the VM."
    Start-VM -vm $vm -wait
}

I have found that the VM creation is not consistently successful on the first pass, so I have added the while loop. Since adding that, I have not had any issues.

I hope these help. They should be easy to change if you prefer snapshots versus differencing disks. Thanks to James O’Neill for creating this library. It makes administering Hyper-V much easier. It is available via CodePlex, where the latest release, updates, and forum support can be found.

Wednesday, March 25, 2009 2:44:50 PM (Mountain Standard Time, UTC-07:00) | Comments [1] | PowerShell | Tools#
Monday, February 09, 2009

The short story is this. If you are accessing Windows 7 from your MSDN subscriptions, save yourself time and frustration by installing the Windows 7 Beta, not the Windows 7 Beta Checked Build. See available downloads here.

This past weekend, I decided to upgrade my work laptop from Vista Enterprise to Windows 7 Ultimate Beta. I was unable to upgrade from Vista Enterprise to 7 Ultimate because that action is not supported. I then tried to upgrade from Enterprise to 7 Enterprise, but I was unable to upgrade from a staged build to a non-staged build. I decided to repave entirely, choosing Windows 7 Ultimate Beta, and I tried to do all of this on a Friday afternoon, two hours before leaving.

I installed Windows 7 Beta Checked Build (x86) - DVD (English), last updated on January 14, 2009,  from my MSDN subscription. I did not install an internal build. Instead, I decided to go with what is publicly available via MSDN.

Why did I go with a Checked Build? Because I failed to read the details describing it. What it cost me in time and frustration, I gain in valuable learning experiences. So it was not a total loss. I received a number of buggy issues with the checked build, mainly the inability for Windows Update to properly download all of my necessary updates, including my video drivers and smart card reader, where the latter enables me to remotely access my work domain.

I gave up and decided to download and install the Windows 7 Beta – DVD (English). Everything works great. I was able to get online, download all of my updates, and connect to my work domain. No issues found so far, and no annoying assertion error popups.

My favorite Windows 7 feature so far (non-UI related) is that PowerShell 2.0 is installed by default. PowerShell 2.0 provides more functionality for remoting, among other enhancements.

Monday, February 09, 2009 2:16:32 PM (Mountain Standard Time, UTC-07:00) | Comments [0] | Operating Systems | PowerShell | Windows#
Wednesday, December 17, 2008

PowerShell documentation, you are too honest.

Get-Help Remove-Item -Full

As I was retrieving information on "Remove-Item," this is what I came across. There is more detail beyond what I am showing, but pay attention to what is bolded.

NAME
    Remove-Item

SYNOPSIS
    Deletes the specified items.

SYNTAX
    Remove-Item [-path] <string[]> [-recurse] [-force] [-include <string[]>] [-
    exclude <string[]>] [-filter <string>] [-credential <PSCredential>] [-whatI
    f] [-confirm] [<CommonParameters>]

    Remove-Item [-literalPath] <string[]> [-recurse] [-force] [-include <string
    []>] [-exclude <string[]>] [-filter <string>] [-credential <PSCredential>]
    [-whatIf] [-confirm] [<CommonParameters>]

DETAILED DESCRIPTION
    The Remove-Item cmdlet deletes one or more items. Because it is supported b
    y many providers, it can delete many different types of items, including fi
    les, directories, registry keys, variables, aliases, certificates, and func
    tions.

PARAMETERS
    -path <string[]>
        Specifies a path to the items being removed. Wildcards are permitted. T
        he parameter name ("-Path") is optional.

        Required?                    true
        Position?                    1
        Default value                N/A - The path must be specified
        Accept pipeline input?       true (ByValue, ByPropertyName)
        Accept wildcard characters?  true

    -recurse <SwitchParameter>
        Deletes the items in the specified locations and in all child items of
        the locations.

        The Recurse parameter in this cmdlet does not work properly.

        Required?                    false
        Position?                    named
        Default value                False
        Accept pipeline input?       false
        Accept wildcard characters?  false

 

"Hmmm... Am I feeling lucky? Ahhh, what the hell, let's do it."

Remove-Item "C:\" -Recurse
Wednesday, December 17, 2008 1:36:14 PM (Mountain Standard Time, UTC-07:00) | Comments [0] | PowerShell#
Monday, December 08, 2008

I consider myself to be a strong follower of the DRY principle. I practice it religiously. Lately, I have been applying DRY outside of my IDE, more specifically, with my build environments and daily developer tasks.

For example, every night I clean my build environment, sync up the latest code, and rebuild all products within my domain. The total time it takes to clean, sync, and build is over two hours, depending on hardware and connection speed. This has been reduced from over four hours. Now before I do all of this, I need to check my environment to ensure I have all the latest tools and versions of those tools, such as Visual Studio, SQL Server, windows updates, service packs, ect. This is just part of our build environment. Typically, we run this check until we receive a positive response, "all is well."

Rather than spend my time running these commands, I automate them, and run them around the clock using Task Scheduler, a BAT file, and PowerShell.

I create one scheduled task that runs once per hour, indefinitely. My scheduled task runs a BAT file that calls my PowerShell script. Within my PowerShell script, I create a Hashtable of commands to run and at which hour to run them. Each hour as the script is executed, I get the current hour and see if any tasks are available to run. For example, each night, sometime during the 2 AM hour, I start my full build. I realize that running once per hour limits the number of commands I can run, but for now, this imperfect solution meets my needs.

Nightly PowerShell Script - Copy Code
1 # Local variables 2 $productRoot = "c:\Projects\dev" 3 $currentHour = (Get-Date).Hour 4 $list = New-Object System.Collections.Hashtable 5 6 # Define tasks to run each hour 7 # Hour 0 = 12:00-12:59 AM 8 # Hour 1 = 1:00-1:59 AM 9 # Hour 20 = 8:00-8:59 PM 10 $list.Add(20,"environmentCheck dev"); 11 $list.Add(21,"environmentCheck test"); 12 $list.Add(22,"environmentCheck system"); 13 $list.Add(23,"projects clean sync"); 14 $list.Add(2,"projects build full"); 15 16 # if a task is defined for this hour, execute it 17 if($list.Contains($currentHour)) 18 { 19 # Get the command to execute for this hour 20 $buildCommand = $list[$currentHour] 21 22 # Set the project bat path 23 $pathToBat = $productRoot + "\bin\project.bat" 24 25 # Set the cmd.exe's arguments 26 $arguments = "/C cd /d " + $productRoot + " & " + $pathToBat + " & " + $buildCommand 27 28 # Start up the DOS prompt, execute the commands 29 $process = New-Object -TypeName System.Diagnostics.Process 30 $process.StartInfo.FileName = "cmd.exe" 31 $process.StartInfo.Arguments = $arguments 32 $process.StartInfo.UseShellExecute = $false 33 $process.Start() 34 $process.WaitForExit() 35 }

Every morning, I expect to have fresh builds. My build system will email me with the results. However, adding email capabilities to the above script with PowerShell is doable. The above commands execute on my dev server, but executing them on remote servers is also doable, even with PowerShell 1.0 and something like the following.

Execute Command Remotely - Copy Code
1 $toServer = "my-remote-server" 2 ([WMICLASS]"\\$toServer\ROOT\CIMV2:win32_process").Create($command)

If my environment is not current and my builds are not working, I can lose productivity during normal business hours. Using a simple scheduled task and PowerShell helps me administer my server and keeps me from repeating the same tedious steps on a daily basis.

How else does PowerShell help me adhere to DRY? It enables me to administer my Hyper-V machines, configure websites, GAC and un-GAC assemblies, and much more.

Monday, December 08, 2008 9:25:53 PM (Mountain Standard Time, UTC-07:00) | Comments [0] | PowerShell#
MuellerDesigns.net
Search
On This Page
The Split Personality of the Tester/Developer
Cross Site Scripting (XSS)
Creating files with FSUTIL
PowerShell Management Library for Hyper-V
Installing Windows 7
Installing Linux in Hyper-V
Internet Explorer 8 Release Candidate 1
PowerShell Documentation
Automate Daily Tasks with PowerShell
SketchPath XPath Editor
Software Testing - Revisited
Architecting Buildings and Software
NBCOlympics.com with Silverlight
Marker Interfaces and C# Attributes
Most Popular
JavaScript ReplaceAll Functionality
What is polymorphism?
What is composition?
Sorting with IComparable and IComparer
Applying the Observer Pattern in ASP.NET
MVP in ASP.NET
What is abstraction?
What is encapsulation?
What is a class?
What is inheritance?
Authentication in ASP.NET
Calendar Controls
XPathNavigator.CheckValidity new for 2.0
SQL Server 2005 Connection Issues
Auto-attach to process '[####] aspnet_wp.exe' on m...
What is an object?
FreeTextBox
VMWare and VPC
An Example of Reflection using C#
Changing File Ownership In Vista and Longhorn
Archive
Links
Categories
My Local Blog Map
Blogroll
About
Powered by:

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
MuellerDesigns.net

Sign In

Help Those In Need
The Hunger Site
Ronald McDonald House Charities (RMHC) of Western Washington & Alaska