vSphere Cluster Host-VM rule affinity with PowerCLI

A colleague wanted a script that could make a Host-VM affinity rule so that certain VM´s that resided on one storage at one site would run on the hosts on that site, the cluster spanns over two sites so the VM´s can run at both sites but one datastore is primary at the first site and the other at the second site.  So He wanted to get the VM´s that have their storage on datastoreLocation1 to be in an host-vm affinity rule “Should Run” on the hosts that is in that site. In this community post I found most of the code, I made som adjustments but mostly the cred goes to @lucd22 and GotMoo.

Here you can see the screendump from the cluster settings

Here is the powerCLI that has been run

One thing here was also that this VMgroup should be updated once a day if there was new VM´s that needed to be included. So this lines should be run in a scheduled task, this will make the DRSVMGroup VMLocationDC1 to be updated with the VM´s currently located at that datastore:

1$VM = get-datastore delad02 | get-vm
2Update-DrsVMGroup -cluster vmw* -VMs $VM -groupVMName VMLocationDC1

Here are the PowerCLI functions for doing this. As you can see I have an switch parameter for -MustRun, that will create the rule as a Must Run on these hosts.

001function New-DRSGroupRule{
002<#
003.SYNOPSIS
004Create a new DRSGroupRule for VMs to reside on some hosts in a cluster
005 
006.DESCRIPTION
007Use this function to create vms in a group and hosts in a group and a host-vm affinity
008 
009.PARAMETER  MustRun
010A switch that will create the rule with Must Run on these host, if not set it will create the rule with should run.
011 
012.NOTES
013Author: Niklas Akerlund / RTS (most of the code came from http://communities.vmware.com/message/1667279 @LucD22 and GotMoo)
014Date: 2012-06-28
015#>
016    param (
017    [Parameter(Position=0,Mandatory=$true,HelpMessage="A Cluster",
018    ValueFromPipeline=$True)]
019    $cluster,
020    $VMHosts,
021    $VMs,
022    [string]$Name,
023    [switch]$MustRun
024    )
025     
026    $cluster = Get-Cluster $cluster
027 
028    $spec = New-Object VMware.Vim.ClusterConfigSpecEx
029    $groupVM = New-Object VMware.Vim.ClusterGroupSpec
030    $groupVM.operation = "add"
031    $groupVM.Info = New-Object VMware.Vim.ClusterVmGroup
032    $groupVM.Info.Name = "VM$Name"
033 
034    Get-VM $VMs | %{
035    $groupVM.Info.VM += $_.Extensiondata.MoRef
036    }
037    $spec.GroupSpec += $groupVM
038 
039    $groupESX = New-Object VMware.Vim.ClusterGroupSpec
040    $groupESX.operation = "add"
041    $groupESX.Info = New-Object VMware.Vim.ClusterHostGroup
042    $groupESX.Info.Name = "Host$Name"
043 
044    Get-VMHost $VMHosts | %{
045    $groupESX.Info.Host += $_.Extensiondata.MoRef
046    }
047    $spec.GroupSpec += $groupESX
048 
049    $rule = New-Object VMware.Vim.ClusterRuleSpec
050    $rule.operation = "add"
051    $rule.info = New-Object VMware.Vim.ClusterVmHostRuleInfo
052    $rule.info.enabled = $true
053    $rule.info.name = $Name
054    if($MustRun){
055        $rule.info.mandatory = $true
056    }else{
057        $rule.info.mandatory = $false
058    }
059    $rule.info.vmGroupName = "VM$Name"
060    $rule.info.affineHostGroupName = "Host$Name"
061    $spec.RulesSpec += $rule
062 
063    $cluster.ExtensionData.ReconfigureComputeResource($spec,$true)
064}
065 
066function Update-DrsVMGroup {
067<#
068.SYNOPSIS
069Update DRS VM group with a new collection of VM´s
070 
071.DESCRIPTION
072Use this function to update the ClusterVMgroup with VMs that are sent in by parameters
073 
074.PARAMETER  xyz
075 
076.NOTES
077Author: Niklas Akerlund / RTS (most of the code came from http://communities.vmware.com/message/1667279 @LucD22 and GotMoo)
078Date: 2012-06-28
079#>
080    param (
081    $cluster,
082    $VMs,
083    $groupVMName)
084     
085    $cluster = Get-Cluster $cluster
086    $spec = New-Object VMware.Vim.ClusterConfigSpecEx
087    $groupVM = New-Object VMware.Vim.ClusterGroupSpec
088    #Operation edit will replace the contents of the GroupVMName with the new contents seleced below.
089    $groupVM.operation = "edit"
090 
091    $groupVM.Info = New-Object VMware.Vim.ClusterVmGroup
092    $groupVM.Info.Name = $groupVMName
093 
094    Get-VM $VMs | %{
095        $groupVM.Info.VM += $_.Extensiondata.MoRef
096    }
097    $spec.GroupSpec += $groupVM
098 
099    #Apply the settings to the cluster
100    $cluster.ExtensionData.ReconfigureComputeResource($spec,$true)
101}

When I get the time I might add some more functionality to also remove rules and groups.

VM affinity when using vCloud Director and vApps

I made a blog post some time ago, where i created a powerCLI function for creating a VM affinity rule that would keep my VM´s in a vApp together in the cluster for better VM-VM performance. I then checked for MoRef to get the VM correlated between vCloud Director and vCenter.

The answer to my problem was much closer than I thought, the vCloud director creates a folder in the “VM´s and Template view” for each vApp and guess what, all VM´s are in there.. so I do not need to ask the vCloud Director anymore (well if I want the Cloud vApp name I do need to ask it..) but ironically I had not looked there and found out that the folders actually where created for each deployed vCloud vApp :-/

So the code is a bit simpler and to get all VM´s from a folder I just type this, the asterisk is because the folder also gets a unique id

1Get-Folder | where {$_.Name -like "ProdvCPod*"} | Get-VM

And then I just need to put those VM´s into an array and create a DRS rule, and here is the function that does this

01<#
02.Synopsis
03   Add an affinity rule for a vCloud Director vAPP
04.DESCRIPTION
05   This function takes a vApp name (string) as parameter and creates an affinity rule for them to keep them together
06.EXAMPLE
07   Add-vAppAffinity -CIVApp ProdCust01
08.NOTES
09Author: Niklas Akerlund
10Date: 2012-06-21
11#>
12function Add-vAppAffinity
13{
14    Param
15    (
16        # Parameter for the vAPP
17        [Parameter(Mandatory=$true,
18                   ValueFromPipelineByPropertyName=$true,
19                   Position=0)]
20        $CIVApp,
21 
22        # If the rule should apply on a different cluster
23        $Cluster = "Cluster01"
24        )
25 
26    if ($CIVApp -ne " " -or $CIVApp -ne $null -or $CIVApp.length -ne 0 ){
27        $Cluster = Get-Cluster $Cluster
28        $VAppname = $CIVApp + "*"
29        $VMs =  Get-Folder | where {$_.Name -like $VAppname} | Get-VM  
30         
31        if (!(Get-DrsRule -Cluster $Cluster -Name $CIVApp -ErrorAction SilentlyContinue)){
32            New-DrsRule -Name $CIVApp -Cluster $Cluster -KeepTogether $true -VM $VMs
33        }else{
34            Remove-DrsRule -Rule (Get-DrsRule -Cluster $Cluster -Name $CIVApp) -Confirm:$false
35            New-DrsRule -Name $CIVApp -Cluster $Cluster -KeepTogether $true -VM $VMs
36        }
37    }
38}

To run it:

and it looks something like this if I check the cluster settings

preferably you have set your DRS cluster to fully automated or not so much will happen 🙂

Clone VM on Win 2012 Hyper-V v3 when it is running using Powershell

I wanted to create a function that could help an IT Pro Admin with the task to create a clone of a running VM, Yes you can use the Export-VM cmdlet but then your VM must be turned off and in some cases, for example when you want to test a new release or patch on a production system but first test it in a safe environment and you are not allowed to stop the original VM.

The function exists in System Center Virtual Machine Manager but there it also must be turned off

So how do I do? I have made a PowerShell function that take a snapshot, copies the vhd files and creates a new VM, connect the vhd´s and network cards, configures the VM with number of processors, dynamic memory etc.

As the Snapshot merges the vhdx files when the VM is running in this new Hyper-V version I thought it was an sufficient way to solve that the VM actually was not writing to the .vhdx files when I copy them but into the .avhdx diff file.

This is a version 0.1 and yes it need some development but right now I do not have the time to make it supernice,

Here you can see it in action

And when It is finished It looks like this in the gui

And here is the PowerShell function

01<#
02.Synopsis
03   Function to clone a running VM
04.DESCRIPTION
05   This function can be used to clone a running vm and connect the copied vhd´s and network
06.EXAMPLE
07   Clone-VM -VMName vmdisktest -VMCloneName vmdisktest-clone -Path c:\VMs
08.EXAMPLE
09   Clone-VM -VMName vmdisktest -VMCloneName vmdisktest-clone -Path c:\VMs -Switch Private
10.Notes
11Author: Niklas Akerlund
12Date: 2012-06-19
13#>
14function Clone-VM
15{
16    [CmdletBinding()]
17    [OutputType([int])]
18    Param
19    (
20        # Name of the VM to be cloned
21        [Parameter(Mandatory=$true,
22                   ValueFromPipelineByPropertyName=$true,
23                   Position=0)]
24        $VMName,
25        [string] $VMCloneName,
26        # Where to store the VM-clone
27        [string] $Path,
28        # Set to another network switch
29        [string] $Switch = " "   
30    )
31 
32    # Get VM
33    $VM = Get-VM $VMName
34    $VHDs = Get-VMHardDiskDrive -VM $VM
35    #$VHDPath = (Get-VMHardDiskDrive -VM $VM).Path
36    $VHDClonePath = "$Path\$VMCloneName\Virtual Hard Disks\"
37     
38    # Create a new array of VHD info
39    $VHDOrg = @()
40    foreach ($VHD in $VHDs){
41        $data = New-Object PSObject -property @{
42            VHDName = (Get-Item $VHD.Path).Name
43            VHDPath = $VHD.Path
44            ControllerType = $VHD.ControllerType
45            ControllerNumber = $VHD.ControllerNumber
46            ControllerLocation = $VHD.ControllerLocation
47 
48        }
49        $VHDOrg +=$data
50    }
51 
52    $VMNics = Get-VMNetworkAdapter -VM $VM
53 
54    # Take snapshot
55    $VM | Checkpoint-VM
56 
57    New-Item -ItemType directory -Path $VHDClonePath
58 
59    New-VM -Name $VMCloneName -Path $Path -NoVHD -MemoryStartupBytes $VM.MemoryStartup -BootDevice IDE
60    $VMClone = Get-VM -Name $VMCloneName
61    Get-VMNetworkAdapter -VMName $VMCloneName | Remove-VMNetworkAdapter
62    
63    # Configure VM-Clon
64    if ($VM.DynamicMemoryEnabled){
65        Set-VM -VMName $VMCloneName -ProcessorCount $VM.ProcessorCount -DynamicMemory -MemoryMinimumBytes $VM.MemoryMinimum -MemoryMaximumBytes $VM.MemoryMaximum
66    }else{
67        Set-VM -VMName $VMCloneName -ProcessorCount $VM.ProcessorCount -StaticMemory
68    }
69 
70    # Add all network cards
71    foreach ($VMNic in $VMNics){
72        if ($Switch -eq " "){
73            Add-VMNetworkAdapter -VMName $VMCloneName -SwitchName $VMNic.SwitchName -IsLegacy $VMNic.IsLegacy
74        }else{
75            Add-VMNetworkAdapter -VMName $VMCloneName -SwitchName $Switch -IsLegacy $VMNic.IsLegacy
76        }
77    }
78 
79    # Copy all VHDs
80    foreach ($VHDcopy in $VHDOrg){
81         
82        $Dest = $VHDClonePath + $VHDcopy.VHDName
83        Copy-Item -Path $VHDcopy.VHDPath -Destination $Dest
84        Add-VMHardDiskDrive -VMName $VMCloneName -ControllerType $VHDCopy.ControllerType -ControllerLocation $VHDCopy.ControllerLocation  -ControllerNumber $VHDCopy.ControllerNumber -Path $Dest  
85    }
86 
87    # Remove snapshot
88    $VM | Remove-VMSnapshot
89 
90}

Good luck in testing, but do take in consideration that this is an copy of the running VM so do not start it at the same time on the same network or you will get IP collision etc, as you can see above I have added an -Switch parameter that you can use to set the network cards of the VM to be connected on another switch to avoid any problems.. 🙂

Using Powershell v3 scheduling for off hours Hyper-V VM maintenance/configuring

A colleague wanted a script for a reconfiguration of a VM´s settings. Once the VM is powered off that is not a big issue. The smart thing is when using Powershell version 3 I can configure a scheduled job that I want to execute off hours.

Imagine that you have the task to add a vCPU to a VM but you can only do it during the service window that happens to be around midnight, I don´t know about you but I rather sleep then if I can automate it. Yes I could in earlier versions use scheduled task but now I am using Win 2012 and Hyper-V 3 and the latest powershell.

So what do I need to do then, Jan Egil Ring has made a blog post about scheduled jobs and I want to show an example regarding Virtual Machine management..

In Powershell version 3 there are 16 cmdlets regarding scheduled jobs

First I need a Trigger

1$once = New-JobTrigger -Once -At 11:59PM

Then I need a job with the trigger, I could use a parameter -FilePath to use a script file instead of -ScriptBlock

1Register-ScheduledJob -ScriptBlock {$vm = Get-VM TestVM2 ; Stop-VM -VM $vm ; Set-VM -VM $vm -ProcessorCount 2 ; Start-VM -VM $vm} -Trigger $once

And then just wait or sleep 😛

And here you can see a screendump of the changing, Now I changed the trigger time to not have to wait to midnight for the blog post but I think you can imagine…

To check that the Job went ok, you run the cmdlet Get-Job ( and if you do not get any job you might need to run import-module PSScheduledJob to get the right Get-Job in that session)

God luck in scheduling your VM tasks 🙂

I passed the 71-246 at MMS 2012 -> MCSE: Private Cloud

As the buzz around twitter said that their result was showing I checked yesterday but on the Prometric site it just said “Tested”. When I woke up today I had to check again and now it looked better 🙂

As you can see I took the test at 4:30 PM on the first day of MMS 2012 so this means I am one of the first passing 🙂 , until someone else claims an earlier time I will say that I am the first MCSE : Private Cloud in Sweden 🙂

This as the requirement is MCSA and that I can count my 70-659 towards this certification 🙂

Good luck in testing!

Lets remove some VM´s with PowerShell on Hyper-V 3 in Windows 2012

In the PowerCLI world there is a kind of evil oneliner to remove all VM´s from a datacenter, I was searching for something alike in the Hyper-V v3 world

The PowerCLI command is

1Get-VM | %{Stop-VM $_ -Confirm:$false; Remove-VM $_ -DeletePermanently -Confirm:$false

And of course that is pure evil because the parameter -deletepermanently will remove not only the VM but also it´s files from the datastore, The % is to take care of each VM and in case the VM is running I will shut it down (otherwise if I only run like Get-VM | Stop-VM -Confirm:$false | Remove-VM -deletePermanently -Confirm:$false I will only remove the ones running, cause I will get an error on the others because I cannot change state to what it is already)

In Hyper-V and the powershell v3 It is not quite as easy but of course it can be done and don´t you dare use the gui 😉

This approach requires that you have put your VM in a separate folder for each of them (or of course it will be clean in your default Hyper-V VM folder..)

The Remove-VM cmdlet does not allow you to actually remove the VM´s virtual hard disk and the folders (which in some times can become a bit messy after a while)

Here is my VM´s

And here is my folder

So if I remove one VM now with the Remove-VM TestVM1, this also requires that the VM is off.

And lets see in the folders what do we have

So to clean both VM´s and folder data I run all at once and this looks like this

1Get-VM testvm* | %{ Stop-VM -VM $_ -Force; Remove-VM -vm $_ -Force ; Remove-Item -Path $_.Path -Recurse -Force}

And see in my VM´s folder how tidy and neat it is 😛

And as I started to describe in the top .. If you just use Get-VM | …  you will clean your Hyper-V host quick 🙂

PowerCLI installed on Windows2012 and working in PS Webaccess

Today my adventures continues, I had to test and install the PowerCLI on a Windows 2012 RC to see if it works and also if it could be used in the new Windows 2012 feature PowerShell Webaccess.

In an earlier post I showed how to configure the PowerShell Webaccess, then I was not successful to run the PowerCLI, the difference here is that I now installed the PowerCLI on the Win 2012 server.

To be able to install i had to enable the .net 2.0 (why the PowerCLI is built on that version is another discussion we will not go into here), as you can see on the dialog I had to enable that

Ok, and that should be done with PowerShell or? My server did not have an active Internet connection so I got a failure when trying to enable the .Net Framework, I had to use the Dism tool with the install media to get the .Net installed as it otherwise downloads the files necessary from MS.

1dism /online /enable-feature /featurename:NetFx3 /all /source:D:\sources\sxs

When this is enabled then there was no problem installing the PowerCLI and when installed I can use for example the V3 functions Get-VM | where powerstate -eq “PoweredOn” <- notice I am not using any curly brackets and $_

Now to the Webaccess, I connect to the site https://pstest.vniklas.com/pswa and log on with an authorized account. After that I run Add-PSSnapin VMware.VimAutomation.Core to enable the PowerCLI functionality . Then i need to get the credential before connecting to the vCenter because the pswa could not show the promt for credential.

But that is easily fixed by using $cred = Get-Credential and then Connect-VIServer vc.vniklas.com -Credential $cred 🙂 and as you can see I can now connect and use the PowerCLI in a web browser.

May the PowerCLI be with you!

Using Powershell v3 Workflow with HyperV deployment

Now that the Windows 2012 RC has arrived with the Powershell v3 I wanted to explore the functionality a bit more, I have seen some posts about how to use the Workflows but none when deploying VM´s in Hyper-V.

I have loaned some code from Mikael Nyström (Deployment Bunny) but had to rewrite a little to make it work with the workflow -parallel.

Also when I downloaded Mikael´s scripts they where automatically  blocked ( I had set my demo system to -Unrestricted) but as you see in the screenshot they are still blocked. but luckily I can use the Unblock-File cmdlet in the Powershell v3, when using the v2 there was a utility tool streams.exe from SysInternals that could help.

and here is it in the properties dialog

But if I have like 5-10 or 100 files I would like to use Powershell (Anyone out there unblocking like 100 scriptfiles by clicking in a dialog?)

To create the “Master” vhdx I used the Convert-WindowsImage.ps1 script, I am creating the master to be deployed with win 2012 RC datacenter core version (Updated:I got a tip in the comments to look at the Convert-WindowsImage instead of the Wim2VHD.ps1)

Now to my workflow, I am testing to create 5 VM´s at the same time with differential disks connected to the master. the workflows also set all VM´s to dynamic memory and starts them.

01# Inparallel.ps1
02#
03# Niklas Akerlund
04# 2012-06-03
05 
06workflow create-VMs
07{
08    $VMRefDisk = "C:\VMs\master.vhdx"
09    $VMNetwork = "Intern"
10    $VMBaseLocation = "C:\VMs"
11    $VMMemory = 384MB
12    foreach -parallel ($item in 1..5) {
13           
14        $VMName = "TestVM$item"   
15        $VMLocation = New-Item -Path "$VMBaseLocation\$VMName" -ItemType Directory -Force
16        $VMDiskLocation = New-Item -Path "$VMLocation\Virtual Hard Disks" -ItemType Directory -Force
17        $VMDisk01 = New-VHD –Path $VMDiskLocation\$VMName-OSDisk.vhdx -Differencing –ParentPath $VMRefDisk
18   
19        New-VM –Name $VMname –MemoryStartupBytes $VMMemory –VHDPath $VMDisk01.path -SwitchName $VMNetwork -Path $VMBaseLocation
20    
21        Set-VM -VMName $VMName -DynamicMemory
22 
23        Start-VM -VMName $VMName
24    }
25    Get-VM TestVM*
26}

It is quite simple but still powerfull, If I for example had like more hosts i can deploy many VM´s at the same time and the thing that would stop me is the hardware and the storage 😛

Upgrading my Win8 beta server to Windows 2012

as yesterday the RC of the Windows 2012 came I thought i would give it a try.

First of all, i wanted to test if I could upgrade my win8 beta server to the Windows 2012 RC but as you can see on the picture from the installation this is not possible. So what to do, as I had two nodes i live migrated my VM´s to the other win8 beta and did a fresh install.

When it was finished I wanted to add the server to the domain and of course this should be done with powershell. When installing you all know that the OS get a not so friendly name so with the parameter -NewName I rename it at the same time as I add it to the domain.

Next step was of course to add the Hyper-V role,

And what to do next, well i want to live migrate my VM´s from my other node but that was unfortunately not possible 🙁 cause of some kind of mismatch with the protocol as you can see on my next screendump

Ok so my next plan was to export the VM´s and then import them, this also with powershell, but as the win8 beta set the dynamic memory maximum to 2 TB i got a configuration issue so I had to handle that before i had an successfull import

After this I could import it ( notice though I could not use the parameter -copy when using -CompabilityReport, so I had to manually copy the VM to the Hyp31 server )

Good luck in your migration to the Windows 2012 RC 🙂