Inspired by Indiana Jones, I have made a little Powershell function to search cluster nodes for VM´s that has not been cluster enabled. If you create a VM in the Hyper-V manager or the Hyper-V powershell cmdlets, the VM is not highly available by default, even if you added it to a SMB share.
What do I do then, I only use one parameter and that is the cluster name, from this I get the HA – enabled VM´s and then check them for all VM´s registered on the hosts. After this I do a comparison and get a list of the VM´s objects that are not Cluster enabled, this can be pipelined to Add-VMToCluster cmdlet (an alias for Add-ClusterVirtualMachineRole) and you are home safe :-). Of course there might be situations where you want a VM to reside only on one cluster node and not be highly available, Guest Clustering is one case where this might be a reason to not add them to a cluster. And if the VM has the configuration and storage locally you wont be able to add it to the cluster anyway
I can easily with PowerShell get the VM´s that are already HA enabled, but with this command I do not get the other VM´s on the cluster nodes.
So if I want to get only the VM´s not cluster enabled, here is the function
<#
.Synopsis
This function search the hosts for VMs tha are not HA enabled
.DESCRIPTION
This function lets you find what VM´s that is running on your hosts and not activated on the virtual machine role on the cluster
.EXAMPLE
Get-VMNotInCluster -Cluster HVCL30
.Link
vniklas.djungeln.se
.Notes
Author: Niklas Akerlund /20121004
#>
function Get-VMNotInCluster
{
[CmdletBinding()]
[OutputType([int])]
Param
(
# Name of the Cluster
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
$Cluster
)
Process
{
$ClusterNodes = Get-ClusterNode -Cluster $Cluster
$VMsInCluster = Get-VM -ClusterObject (Get-ClusterResource -Cluster $Cluster | where ResourceType -eq "Virtual Machine")
$VMsTotal = Get-VM -ComputerName (Get-ClusterNode -Cluster $Cluster).Name
$VMsNotInCluster =@()
foreach ($VM in $VMsTotal){
if($VMsInCluster -notcontains $VM){
$VMsNotInCluster += $VM
}
}
$VMsNotInCluster
}
}
And here is a screendump of it running
And here is when I enable so that all VM´s are Highly Available
Now I do not have so many VM´s in my test environment but in a production environment maybe you can see the potential of knowing that all VM´s on all Clustered nodes are made HA enabled.
I have tested a bit with the WinServ first in the early version 8 and then RC and now RTM, and what I thought was a bug that MS would fix before RTM seems to be still there.
What am I talking about then, well when you do a live or cold storage migration of a VM from for example your local storage to a SMB share either with the gui or preferebly with PowerShell, the built-in function leaves folders behind. And you can see where this is leading when moving a lot of VM´s, several VM´s folders retain with nothing inside and causing confusion!
As you can see on the screendump, the VM 2012 has been moved to another place but the folder still resides with no data in it, the subfolders are there but no disk files. And of course if I use the parameter -RetainVhdCopiesOnSource the folders should stay and also the configuration, vhd files 🙂
So I have done a modified Move-VMStorage function that actually removes the source folder also after moving the VM.
Here is the powershell function and a screendump how it actually deletes the folder also, And as you can see, I check if the VM resides on a share or locally on a hyper-v host and then I use Invoke-Command to delete the folder on the host´s local volume. The script can run on any machine that has RSAT-Hyper-V Powershell tools installed and with an account that has rights to delete folders on the shares/hosts.
<#
.Synopsis
An updated Move-VMStorage function
.DESCRIPTION
To also remove the folder where the VM was residing this function also deletes the folder after moving the VM
.EXAMPLE
Move-VMStorage2 -VM test -ComputerName HV02 -Path \\SMB-srv01\VMs\test
.NOTES
Author: Niklas Akerlund 20120926
#>
function Move-VMStorage2
{
[CmdletBinding()]
[OutputType([int])]
Param
(
# A name of a VM or a VM object
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
$VM,
# The name of the Hyper-V host
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
$ComputerName,
# The path where the VM is going to be relocated to.
[string]
$Path
)
# Lets move and tidy the source folder
$VM = Get-VM $VM -ComputerName $ComputerName
Move-VMStorage -VM $VM -DestinationStoragePath $Path
$VMOldPath = $VM.Path
if ($VMOldPath.StartsWith("\\")) {
Remove-Item -Path $VMOldPath -Recurse -Force
}else{
Invoke-Command -ComputerName $VM.ComputerName -ScriptBlock {Remove-Item -Path $Using:VMOldPath -Recurse -Force}
}
}
Make sure that you always add the VM´s name to the path otherwise you will put the VM´s folders and files in the SMB folder directly and that will cause a mess and when you run my function it will try to clean that folder and all subfolders wich means all VM´s not running in that folder. Maybe I will add the control that It checks that the path to delete includes the VM name in a future update 😛
This year I will present at the Technology Experts Conference by Quest again. Last year I had a session about how to successfully implement and transition into a Hyper-V platform. And this year I will do two sessions in the virtualization track. There are some very skilled speakers listed and I feel proud to be one of the listed.
Mythbusters go Hyper-V
Maybe not so much explosions as on TV but I will try to in a fun and entertaining way do some mythbusting in the Hyper-V virtualization world. We will go through some of the common myths out there among IT professionals and implementers. Microsoft Windows Hyper-V has gone through a massive facelift from 2008 R2 to the new 2012 with loads of new functionallity and performance. Maybe you still rely on old information about how to set up and configure your virtualization platform. I have put some of the common claims to test and will show if they are myths or true. Watch out for some irony and jokes during the session.
Powershell is the way to handle Windows Hyper-v 2012
In this session we will look at how to manage Hyper-V in the new Windows Server 2012 with powershell and the cmdlets in the Hyper-v Module. We will look at the new functions that have been released in the new Powershell version 3 and how we can use them when deploying and managing the hosts and the virtual machines. We will in this session go from scratch to a fully deployed environment without using the GUI. In the session we will also look at how to find Hyper-V servers in your Active Directory and what VM´s are running there, we will also look at how to schedule a daily health report on your VM´s and hosts.
Hint to save money on registering:
TEC are offering a discounted 850 Euro rate for delegates who register by 21 September! To receive the discount code, please email TEC2012@quest.com.
Yesterday I upgraded a Hyper-V node in a cluster from RC to RTM, Now I want to live migrate VM´s from the RC node to the RTM and then upgrade that.
As you can see on this screendump I have one node with build number 8400 (RC) and one with 9200 (RTM)
As you can see on this next image I have two VM´s running on my old node and I want to migrate them. The VM´s reside on a SMB 3.0 share and the cluster have a quorum share
And how do I move the VM´s to the other node, yes with powershell also, what you can see is that the Move-ClusterVirtualMachineRole can take the VMid from the VM objects as pipelined input, and that is really nice.
So now I have live migrated over the VM´s from the old RC to the RTM server and I can continue to upgrade that node.
I get an error if I try to live migrate them back to the old node, the only way I can do that is when the VM´s are powered off.
Of course I would rather have all my hosts in my System Center VMM 2012 SP1 but if I do not, or I just as an ITPro admin want to check if there are some new Hyper-V servers out there in my Active Directory and if they have VM´s on them I can use PowerShell.
I have looked at Ravikanth´s blog and done some modifications, also I am using the Win2012 and Powershell v3.
I can with the following powershell cmd get all hyper-v virtual machines in my Active Directory (of some reason the VMware machines do not get registered in the AD as virtual machines). With powershell version 3 it autoloads the Active Directory Module, this requires though that I have the feature RSAT-AD-PowerShell installed, easily done with Get-WindowsFeature RSAT-AD-PowerShell | Add-WindowsFeature.
I cannot do so much more with this information, what I want is to check a list of Hyper-V servers and also try to get the VM´s on them, sadly only win2012 servers can interact with the hyper-v powershell module and give that info, but hey whom has win 2008 r2 hyper-v servers 😉
This first query gives me all Hyper-V servers in the domain, 2008->2012
I am working in a project that want to get performance data from remote servers, this with WMI, the servers belong to an Active Directory but the user collecting is not allowed to be an administrator (As an Administrator this is easy because then you already are in control and in the right groups)
So How do I fix this with preferably PowerShell, Scripting Guy has made a post about how to add users to local groups and Steve Lee on the MSDN WMI Blog has made a post how to add permissions on WMI so I have combined them and got the solution I want.
The groups I am interested in on the remote computer are “Distributed COM Users”,”Performance Monitor Users”, to be able to connect and get monitoring data
To get it from the WMI interface I need some permissions there, I only grant read permissions on the root/CIMv2 subtree.
And here is the Powershell that does it, In the screendump you can see that I can use a foreach loop to set the permissions on several computers
To check that I can get the performance data I can use the powershell cmdlet Get-WMIObject , as you can see in the screendump, in the first test I do not have the permissions set but in the second it works..
Here are the functions
Set-UserLocalGroup
# Add/Remove user from local group
#
# Niklas Akerlund/Most code from Scriptingguy Blog http://blogs.technet.com/b/heyscriptingguy/archive/2010/08/19/use-powershell-to-add-domain-users-to-a-local-group.aspx
Function Set-UserLocalGroup
{
[cmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$Computer,
[Parameter(Mandatory=$True)]
[string]$Group,
[Parameter(Mandatory=$True)]
[string]$Domain,
[Parameter(Mandatory=$True)]
[string]$User,
[switch]$add,
[switch]$remove
)
$de = [ADSI]"WinNT://$Computer/$Group,group"
if($add){
$de.psbase.Invoke("Add",([ADSI]"WinNT://$Domain/$User").path)
} elseif ($remove){
$de.psbase.Invoke("Remove",([ADSI]"WinNT://$Domain/$User").path)
}
}
Set-WmiNamespaceSecurity, there was an error in the original code that I have corrected based on the comments, this error caused the function to always run on the local computer instead of the remote when using the -computer parameter.
I have made a function to set reservations on VM´s in PowerCLI. The function takes a VM as parameter and also percentage of CPU and Memory you want to reserve.
The function checks the Mhz of the physical CPU from the Host the VM is running on and makes the reservation from this.
In the function I can set both memory and cpu reservation or just one of them
here is a screendump of the function being used
and here is the PowerCLI
function Set-VMReservation{
<#
.SYNOPSIS
Set VM Reservation on Memory and/or CPU
.DESCRIPTION
Use this function set VM reservations
.PARAMETER MemoryMB
Use this parameter to set the percent of memory reserved for the VM
.PARAMETER CpuMhz
Use this parameter to set the percent of CPU Mhz from the host CPU to be reserved
.NOTES
Author: Niklas Akerlund
Date: 2012-07-19
#>
param (
[Parameter(Position=0,Mandatory=$true,HelpMessage="A VM",
ValueFromPipeline=$True)]
$VM,
$MemoryMB = 0,
$CpuMhz = 0
)
$VM = Get-VM $VM
if ($MemoryMB -ne 0 -and $CpuMhz -ne 0){
$MemoryMB = $MemoryMB/100
[int]$MemoryMB = $VM.MemoryMB*$MemoryMB
$CpuMhz = $CpuMhz/100
[int]$CpuMhz = ([math]::truncate(((Get-VMHost -id $VM.extensiondata.summary.runtime.host).ExtensionData.Hardware.CpuInfo.hz)/1000000))*$VM.NumCpu*$CpuMhz
$VM | Get-VMResourceConfiguration |Set-VMResourceConfiguration -CpuReservationMhz $CpuMhz -MemReservationMB $MemoryMB
}elseif ($MemoryMB -ne 0 -and $CpuMhz -eq 0){
$MemoryMB = $MemoryMB/100
[int]$MemoryMB = $VM.MemoryMB*$MemoryMB
$VM | Get-VMResourceConfiguration |Set-VMResourceConfiguration -MemReservationMB $MemoryMB
}elseif ($MemoryMB -eq 0 -and $CpuMhz -ne 0){
$CpuMhz = $CpuMhz/100
[int]$CpuMhz = ([math]::truncate(((Get-VMHost -id $VM.extensiondata.summary.runtime.host).ExtensionData.Hardware.CpuInfo.hz)/1000000))*$VM.NumCpu*$CpuMhz
$VM | Get-VMResourceConfiguration |Set-VMResourceConfiguration -CpuReservationMhz $CpuMhz
}elseif ($MemoryMB -eq 0 -and $CpuMhz -eq 0){
$VM | Get-VMResourceConfiguration |Set-VMResourceConfiguration -CpuReservationMhz 0 -MemReservationMB 0
}
}
In this example I have set the CPU reservation to 60 % and Memory to 40 %
And why do I want to add an registry key under AlternateShell instead of the Shell registry key that holds Explorer.exe. One reason is that when I enable the GUI from Core mode I do not get the full GUI. So when I run Get-WindowsFeature *gui-shell* | Add-WindowsFeature and then reboot the server it looks like the following screendump:
As you can see in the following screendump and in Andrew´s post, I keep the CMD and just set an new registry key that has a higher value, but not as high as Explorer. Doing this I get the Powershell console when in Core and the Explorer when in Full GUI mode. When in full GUI mode I get a temporary Pagefile error though 🙁 , the system seems to work correctly.. I have tested the Get-WindowsFeature *gui-shell* | Add-WindowsFeature on an installation where I have not done any alternation with the shells and there the page file dialog appears also so this is caused by another issue that I will investigate in another blog post.
In twitter yesterday I saw that there was a discussion about setting default shell to Powershell in Windows Core, Jeffery Hicks has done this in Windows 2008 R2 and I wanted to test if his little trick worked in Windows 2012 also (by the way, why is it not default by default in 2012 Core? )
I have my testmachine ready and so lets go
Interestingly, When I check the registry I got that my shell in the 2012 Core was Explorer.exe,
but when I got a bit deeper I could see under AlternativeShells that the CMD was there
I tried to edit that but got an error 🙁
well lets try to exchange the explorer.exe in shell to powershell.exe instead, I use the $env:userprofile to get the same directory as with cmd
And here you can see in my registry, notice that the $env:userprofile is still there thanks to the in the string variable and that means that when another user log in they will get their own directory:
And when I try to log in again I get the Powershell Console by default and also in the “right” directory
Lets hope Microsoft set Powershell console as default also in the Core version when Windows 2012 becomes RTM 🙂
Update: I did apparently not follow the tweets so far that I noticed that the PS master Thomas Lee already had made a blog about this, in his post I would just add this to get the users directory
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:
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.
function New-DRSGroupRule{
<#
.SYNOPSIS
Create a new DRSGroupRule for VMs to reside on some hosts in a cluster
.DESCRIPTION
Use this function to create vms in a group and hosts in a group and a host-vm affinity
.PARAMETER MustRun
A switch that will create the rule with Must Run on these host, if not set it will create the rule with should run.
.NOTES
Author: Niklas Akerlund / RTS (most of the code came from http://communities.vmware.com/message/1667279 @LucD22 and GotMoo)
Date: 2012-06-28
#>
param (
[Parameter(Position=0,Mandatory=$true,HelpMessage="A Cluster",
ValueFromPipeline=$True)]
$cluster,
$VMHosts,
$VMs,
[string]$Name,
[switch]$MustRun
)
$cluster = Get-Cluster $cluster
$spec = New-Object VMware.Vim.ClusterConfigSpecEx
$groupVM = New-Object VMware.Vim.ClusterGroupSpec
$groupVM.operation = "add"
$groupVM.Info = New-Object VMware.Vim.ClusterVmGroup
$groupVM.Info.Name = "VM$Name"
Get-VM $VMs | %{
$groupVM.Info.VM += $_.Extensiondata.MoRef
}
$spec.GroupSpec += $groupVM
$groupESX = New-Object VMware.Vim.ClusterGroupSpec
$groupESX.operation = "add"
$groupESX.Info = New-Object VMware.Vim.ClusterHostGroup
$groupESX.Info.Name = "Host$Name"
Get-VMHost $VMHosts | %{
$groupESX.Info.Host += $_.Extensiondata.MoRef
}
$spec.GroupSpec += $groupESX
$rule = New-Object VMware.Vim.ClusterRuleSpec
$rule.operation = "add"
$rule.info = New-Object VMware.Vim.ClusterVmHostRuleInfo
$rule.info.enabled = $true
$rule.info.name = $Name
if($MustRun){
$rule.info.mandatory = $true
}else{
$rule.info.mandatory = $false
}
$rule.info.vmGroupName = "VM$Name"
$rule.info.affineHostGroupName = "Host$Name"
$spec.RulesSpec += $rule
$cluster.ExtensionData.ReconfigureComputeResource($spec,$true)
}
function Update-DrsVMGroup {
<#
.SYNOPSIS
Update DRS VM group with a new collection of VM´s
.DESCRIPTION
Use this function to update the ClusterVMgroup with VMs that are sent in by parameters
.PARAMETER xyz
.NOTES
Author: Niklas Akerlund / RTS (most of the code came from http://communities.vmware.com/message/1667279 @LucD22 and GotMoo)
Date: 2012-06-28
#>
param (
$cluster,
$VMs,
$groupVMName)
$cluster = Get-Cluster $cluster
$spec = New-Object VMware.Vim.ClusterConfigSpecEx
$groupVM = New-Object VMware.Vim.ClusterGroupSpec
#Operation edit will replace the contents of the GroupVMName with the new contents seleced below.
$groupVM.operation = "edit"
$groupVM.Info = New-Object VMware.Vim.ClusterVmGroup
$groupVM.Info.Name = $groupVMName
Get-VM $VMs | %{
$groupVM.Info.VM += $_.Extensiondata.MoRef
}
$spec.GroupSpec += $groupVM
#Apply the settings to the cluster
$cluster.ExtensionData.ReconfigureComputeResource($spec,$true)
}
When I get the time I might add some more functionality to also remove rules and groups.