Archive

Posts Tagged ‘HyperV’

Powershell: Importing Hyper-V VM’s

February 2, 2012 5 comments

In my previous post Creating Hyper-V Symolic Links using Powershell I created a small but useful function called Create-SymbolicLinks which was used to execute one or more .bat files that created symbolic links to base or middle tier VHD’s as part of the initial classroom VM setup.  Once this was completed the next step was to import the VM’s and of course what better way to automate this then to use Powershell.

The first task at hand is to download and import the Hyper-V module from Codeplex.  There are 2 versions of this module available to download, with the latest version being R2 SP1.  Once downloaded I then place it into the directory where I will be running the script\function so that it can be copied to the appropriate Modules directory on the server.   Both the module path and name are defined in the Param statement as follows, along with the path to the VM’s.

 Param (
  $ModulePath = ($env:psmodulePath -split ";")[1],
  $ModuleName = "HyperV",
  $path = "C:\Program Files\Microsoft Learning\6419\Drives\"
  )

The code to copy the HyperV module and import it is:

 #Copy the HyperV module if it doesn't already exist
 if(!((Dir $ModulePath) -match $ModuleName)) {
  Copy-Item .\$ModuleName $ModulePath -Recurse
  }
 #Import the HyperV module if not already imported
 if(!(Get-Module | ?{$_.Name -like $ModuleName})) {
   Import-Module $ModuleName
  }

Now the real work begins.  I need to determine what VM’s have already imported into Hyper-V to make sure we don’t 1) do more work then is necessary and 2) don’t try overwriting any previously imported VMs.  Doing this involves using Get-VM and extracting just he name property ( or in this case the ElementName property) and putting those results into an array called $ActiveVMs.
In order to get a list of the VM’s I need to import I run a Get-ChildItem $Path and extract just the Name property and put the results into an array called $VMsToImport.

 #Create array to contain active VM's
 $ActiveVMs = Get-VM | Foreach{$_.ElementName}
 #Create array to contain VM's to be imported
 $VMsToImport = (Get-ChildItem $path) | Foreach{ $_.Name }

Now comes the interesting part.  How to do a comparision of the two arrays and determine if any VM names overlap.  This seemed like a perfect opportunity to use a regular expression.  I remember reading an article on the Scripting Guys called “Speed Up Array Comparisions in Powershell with a Runtime Regex” where the author discussed the benefits of using the -Match operator with a regular expression  instead of the -Contains comparison operator.  Needless to say using a regular expression was way faster…10x faster and since Powershell is all about automation and efficiency , creating a regex seems like the way to go.

 [regex]$ActiveVMs_Regex = '(?i)^('+(($ActiveVMs |
        Foreach {[regex]::Escape($_)})-join "|" )+')$'

The only thing left now was to run the -Match comparison and import the VM’s  using Import-VM.   I also needed to use Start-Sleep 5 because during my initial tests ( and there were many of them ) some VM’s weren’t imported.  It was random and not consistent but after having the script pause before each import provided just the rate of success I was looking for.   Powershell you rock!!!

            
 #Import the VMs            
 $VMsToImport -notmatch $ActiveVMs_Regex |             
        Foreach{ Import-VM (Join-Path $path $_ )             
        Start-Sleep 5            
  }

Here is the complete function..

Function Import-VMs {            

 Param (
  $ModulePath = ($env:psmodulePath -split ";")[1],
  $ModuleName = "HyperV",
  $path = "C:\Program Files\Microsoft Learning\6419\Drives\"
     )            
 #Copy the HyperV module if it doesn't already exist
 if(!((Dir $ModulePath) -match $ModuleName)) {
  Copy-Item .\$ModuleName $ModulePath -Recurse
     }             

 #Import the HyperV module if not already imported
 if(!(Get-Module | Where{$_.Name -like $ModuleName})) {
   Import-Module $ModuleName
     }            

 #Create array to contain active VM's
 $ActiveVMs = Get-VM | Foreach{$_.ElementName}            

 #Create array to contain VM's to be imported
 $VMsToImport = ( Get-ChildItem $path ) | Foreach{$_.Name}
 [regex]$ActiveVMs_Regex = '(?i)^('+(($ActiveVMs |
        Foreach {[regex]::Escape($_)})-join "|" )+')$'            

 #Import the VMs            
 $VMsToImport -notmatch $ActiveVMs_Regex |             
        Foreach{ Import-VM ( Join-Path $path $_ )             
        Start-Sleep 5            
     }
}#End Function
Categories: Powershell Tags: ,

Creating Hyper-V Symbolic Links using Powershell

January 31, 2012 1 comment

As an MCT I’m required to download preconfigured VM’s and supporting VHD’s from Microsoft which are then imported into base images running Server 2008 R2 and HyperV.  Prior to importing the VM’s, symbolic links need to be created in each VM’s root Virutal Hard Disk folder.  Symbolic links are shortcuts that are associated with base or middle tier VHD’s.  Microsoft even includes a .bat file that automates their creation.  An example of one of these .bat files is below:

@ECHO OFF
@ECHO Creating Symlinks necessary to import VM’s
rem ************  to create Symlink for Base Image
rem ************substitute VHDName.vhd with the name of the base image needed
mklink “Path:\Source.vhd” “Path:\Base.vhd”
pause

So it occurred to me that if a .bat file could automate the creation of one or more symbolic links, then perhaps a Powershell script could automate the execution of one or more individual .bat files.  Creating these symbolic links is a repetitive act of having to browse to each root VM’s folder, execute the .bat file and then close the corresponding cmd prompt.  This is surely another instance where time could be better spent elsewhere and so I turn to my old friend Powershell for a script to automate this mundane task.

The first step is define the path to the root VM folders.

$path = "C:\Program Files\Microsoft Learning\6419\Drives\"

Now we run Get-ChildItem on the folder using the -Include parameter to filter for .bat files only and then -Recurse to browse sub folders and files.   These results are then placed into the $vhds array variable.

$vhds = Get-ChildItem $path -Include *.bat -Recurse

The next step is to execute the .bat files by piping the $vhds array containing the required .bat files to Start-Process.  One of the problems I encountered was that, although Start-Process was able to execute the .bat files it wasn’t passing it’s results down the pipeline.  Most cmdlets do return output but on occasion certain cmdlets needs to be encouraged to do this using the -PassThru  parameter.

$vhds | %{ start-process "$_" -PassThru |

These results are then pipelined to Foreach-Object where I found it necessary to pause the script and allow the .bat file to properly execute using Start-Sleep 1.  Without the pause there is a quick blip on the screen and then the .bat file exists.  I remember reading a script in a recent blog article by Greg Caporale regarding printing .pdf’s where he incorporated Start-Sleep ( or it’s alias sleep ) to provide enough time for the .pdf files to print before closing Adobe Reader.   The Foreach-Object also passes down the pipline each individually open cmd process to Stop-Process which exits the open cmd prompt.

$vhds | %{ Start-Process "$_" -PassThru |
        %{ Start-Sleep 1 ; $_} | Stop-Process }

I then wrapped the whole thing into a function called it Import-SymbolicLinks and defined a $Path parameter to make the script that much more flexible by accepting parameter input. Below if the final script.

Function Create-SymbolicLinks {            

 Param(
  $path = "C:\Program Files\Microsoft Learning\6419\Drives\"
  )            

 $vhds = Get-ChildItem $path -Include *.bat -Recurse
 $vhds | %{ Start-Process "$_" -PassThru |
            %{ Start-Sleep 1 ; $_} | Stop-Process }
 }            

Create-SymbolicLinks

The obvious next step is to script the import of these VM’s as well as create snapshots.

Categories: Powershell Tags: ,