A. Mikkelsen VMware ESX scripts, commands, tools and other nice to know things that will make your virtualization days easier!!!!

Friday, March 16, 2012

Powershell/ESXCLI – EMC VPLEX changing multipathing policy

Filed under: ESXi,PowerShell,Scripting,Tools,vSphere (ESX) — Tags: , , , , , , , , , — A. Mikkelsen @ 22:01 pm

When connecting some storage systems to vSphere, vSphere sometime doesn’t select the most optimal multipathing policy.

I came across the above problem when adding an EMC VPLEX storage system to a  large vSphere 4.1 installation.
vSphere selected the Fixed path multipathing policy. In smaller environments this isn’t normally a problem , but when a Fixed policy is selected in a large environment with multiple datastores. vSphere selects the first path for each datastore, this results in almost all datastores uses the same path. The result is that the path will be overloaded.

EMC’s best practices for EMC VMAX/VPLEX is to use Fixed policy with static load balancing or to use EMC PowerPath.
In our environment Fixed or PowerPath wasn’t an option as we have multiple hosts and datastores, so in close communication with EMC we chose to change the multipathing policy to Round Robin..
EMC has once now informed us that RR is only an option (not the best) if PowerPath or Fixed is not an option.

Changing the policy for a few LUN’s won’t take long, if you only have a few hosts and a few datastores, but if you have multiple hosts with multiple datastores, the task is massive.

Using Powershell and EsxCli the task is very easy and extremely fast.
Thanks to Arnim van Lieshout (http://www.van-lieshout.com/2011/01/esxcli-powercli/) for the basic script.

The below script illustrates how to set RoundRobin and not Fixed, this will be updated ASAP.
Until then take a look at this post from LucD http://communities.vmware.com/message/1774139 

<pre>function FuncMail {
 param($To, $From, $Subject, $Body, $smtpServer)
 $msg = new-object Net.Mail.MailMessage
 $smtp = new-object Net.Mail.SmtpClient($smtpServer)
 $msg.From = $From
 $msg.Subject = $Subject
 $msg.IsBodyHtml = 1
 $msg.Body = $Body

#load Vmware Module
if ((Get-PSSnapin | Where-Object { $_.Name -eq "VMware.VimAutomation.Core" }) -eq $null) { Add-PSSnapin VMware.VimAutomation.Core }

# ----------- Variables ---------------
$vcServer = "vcenterserver"
$vCenterUser = "vcenterusername"
$vCenterPWD = "vcenterpassword"
$DC = "*" # Use * for all DC else replace * with datacenter name
$cluster = "*" # Use * for all Clusters in DC else replace * with cluster name

# LUN settings
$LUNType = "EMC Fibre Channel Disk*"
$psp = "VMW_PSP_RR"
$satp = "VMW_SATP_INV"
$iops = 10

$esxUser = "esxuser-root"
$esxPWD = "esxuserpassword"

# Email
$strEmailTo = "to@mail.com"
$strEmailFrom = "from@mail.com"
$strEmailSubject = "PSP info '$vcServer' "
$strEmailSMTP = "smtpserver.com"

$strHeadHTML = "<STYLE TYPE='text/css'>"
$strHeadHTML += "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$strHeadHTML += "TH{border-width: 1px;padding: 10px;border-style: solid;border-color: black; background-color:thistle}"
$strHeadHTML += "TD{border-width: 0px;padding: 0px;padding-right: 5px;padding-left: 5px;border-style: solid;border-color: black}"
$strHeadHTML += "</STYLE>"

$strBodyHTMLStart = "<H3> Setting PSP multipath & IOPS : "
$strBodyHTMLStart += Get-Date -Format g
$strBodyHTMLStart += "</H3>"
$strBodyHTMLStart += "<TABLE><TR> <TH>vCenter</TH> <TH>Datacenter</TH> <TH>Cluster</TH> <TH>Host</TH> <TH>Device</TH> <TH>SATP</TH> <TH>Old PSP</TH> <TH>New PSP</TH> <TH>IOPS</TH> <TH>Old Paths</TH> <TH>New Paths</TH></TR>"

$strBodyHTMLinfoStart = "<H3> PSP multipath & IOPS not set : "
$strBodyHTMLinfoStart += Get-Date -Format g
$strBodyHTMLinfoStart += "</H3>"
$strBodyHTMLinfoStart += "<TABLE><TR> <TH>vCenter</TH> <TH>Datacenter</TH> <TH>Cluster</TH> <TH>Host</TH> <TH>Device</TH> <TH>SATP</TH> <TH>PSP</TH> <TH>Paths</TH></TR>"

# ---------- Logic (Don't Change) --------------------

$strBODYHTML = ""
$strBodyExcludeHTML = ""

#Connect to vCenter
Connect-VIServer $vcServer -User $vCenterUser -Password $vCenterPWD| Out-Null

#Get Datacenter Clusters
$arrDC = Get-Datacenter -Name $DC | Sort Name
 foreach($objDC in $arrDC){

 #Get Cluster list
 $arrCluster = Get-Cluster -Location $objDC -Name $cluster | Sort name
 foreach($objCluster in $arrCluster){

 #Connect to ESX hosts in cluster
 foreach ($esx in Get-VMHost -Location $objCluster | Sort Name) {

 Connect-VIServer $esx -User $esxUser -Password $esxPWD | Out-Null

#Retrieve the esxcli instances and loop through them
 foreach($esxcli in Get-EsxCli -Server $esx.name) {

 #Write-Host $esx.Name -BackgroundColor Red

 # Change PSP for EMC VPLEX/VMAX devices
 $arrDevice = $esxCli.nmp.device.list() | where {$_.PathSelectionPolicy -ne $psp -and $_.DeviceDisplayName -like $LUNType}
 foreach($myDevice in $arrDevice){
 #Write-Host "Updating $($myDevice.Device)" -ForegroundColor green
 $esxCli.nmp.device.setpolicy($null, $myDevice.Device, $psp)

 $newPSP = $esxCli.nmp.device.list($myDevice.device)
 $newIOPS = $esxcli.nmp.roundrobin.getconfig($myDevice.device)
 $strBODYHTML += "<TR> <TD>$($vcServer)</TD> <TD>$($objDC.Name)</TD> <TD>$($objCluster.Name)</TD> <TD>$($esx.Name)</TD> <TD>$($myDevice.Device)</TD> <TD>$($myDevice.StorageArrayType)</TD> <TD>$($myDevice.PathSelectionPolicy)</TD> <TD>$($($newPSP[0]).PathSelectionPolicy)</TD> <TD>$($newIOPS.IOOperationLimit)</TD> <TD>$($myDevice.WorkingPaths)</TD> <TD>$($($newPSP[0]).WorkingPaths)</TD></TR>"

 # Changes not set on
 $arrInfoDevice = $esxCli.nmp.device.list() | where {$_.PathSelectionPolicy -ne $psp -and $_.DeviceDisplayName -like $LUNType}
 foreach($myInfoDevice in $arrInfoDevice){
 $strBODYHTMLinfo += "<TR> <TD>$($vcServer)</TD> <TD>$($objDC.Name)</TD> <TD>$($objCluster.Name)</TD> <TD>$($esx.Name)</TD> <TD>$($myInfoDevice.Device)</TD> <TD>$($myInfoDevice.StorageArrayType)</TD> <TD>$($myInfoDevice.PathSelectionPolicy)</TD> <TD>$($myInfoDevice.WorkingPaths)</TD></TR>"

 #Change the default PSP for my SATP
 $esxcli.nmp.satp.setdefaultpsp($psp,$satp) | Out-Null
 Disconnect-VIServer $esx.name -Confirm:$false
#Disconnect from vCenter
Disconnect-VIServer $vcServer -Confirm:$false | Out-Null

$strBodyHTMLEnd = "</TABLE>"
$strBodyHTMLinfoEnd = "</TABLE>"

# Collect the HTML
$strHTML = "<HTML><HEAD>"
$strHTML += $strHeadHTML
$strHTML += "</HEAD><BODY>"
$strHTML += $strBodyHTMLStart
$strHTML += $strBODYHTML
$strHTML += $strBodyHTMLEnd
$strHTML += $strBodyHTMLinfoStart
$strHTML += $strBODYHTMLinfo
$strHTML += $strBodyHTMLinfoEnd
$strHTML += "</BODY></HTML>"

# Email the collected data
FuncMail -To $strEmailTo -From $strEmailFrom -Subject $strEmailSubject -Body $strHTML -smtpServer $strEmailSMTP

Script explained

Line 20-24:
Add you vCenter server name and the logon credentionls.

Line 27-30:
LUNTYPE, is the storage type you want to change PSP and SATP for (you can set this to *, if you want you set the PSP and SATP for all datastores.
PSP, is the multipathing policy you want to change to.
SATP, is the storage array type you want to change the default to.
IOPS, is the number of IO’s to be send, before switching to the next path. vSphere default is 1000, EMC recommends 1, but I found 10 to work for me.
Stephen Foskett has explained what is PSP and SATP in plain English  http://blog.fosketts.net/2011/06/06/vmware-esx-vsphere-satp-psp-support-matrix/.

Line 33-34:
Change the user to a user with root privileges.
Change the password to correspond with the user.

Line 37-40:
Change the info to receive an report of what has been changed.

Line 92-93:
Sets the PSP and IOPS for each datastore.

Line 95-96:
Retrieves the new settings (for verification) for  each datastore.

Line 101-107:
Generates a list of datastores, where the PSP setting wasn’t able to be set.
Run the script again to set them.

Line 111:
Sets the hosts default datastore PSP and SATP.
If your hosts connect to multiple different storage systems, I wouldn’t recommend setting this, but it’s to you.

If you want to see an output on the screen of the progress of the script, uncomment the lines 85, 91

Download the full RoundRobin script here.

An exampel of what a report can look like:

Post is updated after input from Josh Coen, www.valcolabs.com.

Friday, June 24, 2011

VMware vSphere PowerCLI Reference – Book

Filed under: Books,PowerShell,Tools — Tags: , , , , — A. Mikkelsen @ 22:18 pm

If you haven’t read or read about the must have PowerCLI book “VMware vSphere PowerCLI Reference: Automating vSphere Administration“, by Luc Dekens, Alan Renouf, Glen Sizemore, Arnim van Lieshout and Jonathan Medd, then you need to check it out.

The book will show you how to automate your VMware infrastructure from vCenter to VM’s.

  • Automate installations
  • Create and configure VM’s
  • Secure your environment
  • Create reports

and much more.

Read a few chaphers from the book or buy the book (like I did :-)) at:

Download the PowerCLI examples from each chapter:

Friday, June 3, 2011

List hosts CDP information

Filed under: Networking,PowerCLI Scripts,PowerShell — Tags: , , , — A. Mikkelsen @ 18:28 pm

If you have your hosts connected to a Cisco network infrastructure, you can see a hosts CDP information directly from within the VI Client.

As you can see in the exampels below it’s quite impossible to get the full CDP picture if you have many hosts with multiple NICs.
So I created a script that retrieves all CDP info from all your hosts (even across multiple vCenters) and displays it as a webpage.
Now it’s possible to search and share the information 🙂

The script is build up by a few functions

  • Retrieve the vCenter servers to retrieve host from.
    $objvCenterServer = Import-Csv -Path $strvCenterFilePath -Delimiter ";" | sort vCenter
    foreach($strvCenterServer in $objvCenterServer){
    # Check if VC is uncommented
    if ((!($strvCenterServer.vCenter.Contains("#"))) -and ($strvCenterServer.vCenter.Length -gt 0)){
    # Connect to vCenter Server
     Connect-VIServer -Server $strvCenterServer.vCenter -User $strvCenterServer.UserName -Password $strvCenterPWD
    # Add logic
    # Disconnect from vCenter server
    DisConnect-VIServer -Confirm:$false
  • Retrieve hosts from clusters.
    $arrDC = Get-Datacenter | Sort
    foreach($objDC in $arrDC){
     $arrCluster = Get-Cluster -Location $objDC | Sort
     # Only proceed if the Cluster isn't blank
     if ($($arrCluster | Measure-Object).count -gt 0){
     foreach($Cluster in $arrCluster){
     $vmhosts = Get-VMHost -Location $Cluster | Sort Name | Where-Object {$_.State -eq "Connected"} | Get-View
     #Only proceed if any hosts in cluster
     if ($vmhosts.Count -gt 0){
     foreach ($vmhost in $vmhosts){
    # Add logic for each host
  • Retrieve hosts NICs CDP info.
    foreach ($vmhost in $vmhosts){
     $networkSystem = Get-view $vmhost.ConfigManager.NetworkSystem
     foreach($pnic in $networkSystem.NetworkConfig.Pnic | Sort Device){
     $pnicInfo = $networkSystem.QueryNetworkHint($pnic.Device)
     foreach($Hint in $pnicInfo){
     # LinkSpeed & MAC
     $record = 0
     $tmpSpeed = ""
     $tmpMAC = ""
     If ($Hint.Device -eq $vmhost.Config.Network.Pnic[$record].Device){
     $tmpSpeed = $vmhost.Config.Network.Pnic[$record].LinkSpeed.SpeedMb
     $tmpMAC = $vmhost.Config.Network.Pnic[$record].Mac
     $record ++
     Until ($record -eq ($vmhost.Config.Network.Pnic.Length))
     # Duplex
     $tmpDuplex = ""
     if($Hint.ConnectedSwitchPort.FullDuplex -eq $true){
     $tmpDuplex = "Full"
     if($Hint.ConnectedSwitchPort.FullDuplex -eq $false){
     $tmpDuplex = "Half"
     # Status
     $tmpStatus = ""
     If (($tmpSpeed -ge 1000) -and ($tmpDuplex -eq "Full")){
     $tmpStatus = "OK"
     If (($tmpSpeed -gt 0) -and ($tmpDuplex -eq "")){
     $tmpStatus = "CDP not working"
     elseif (($tmpSpeed -gt 0) -and ($tmpDuplex -eq "Half")){
     if ($tmpStatus -ne ""){$tmpStatus += " / "}
     $tmpStatus = "Duplex config error"
     elseif (($tmpSpeed -gt 0) -and ($tmpSpeed -lt 1000)){
     if ($tmpStatus -ne ""){$tmpStatus += " / "}
     $tmpStatus = "Speed config error"
     $tmpStatus = "Link Down"
  • Seperate information included/excluded.
    $isExcluded = func_exclude_from_list -ExcludeHost $vmhost.Name -ExcludeVMNIC $Hint.Device -FromObj $objExcludeList
  • Generate html outputfile.
    Add-Content -Path $($strOutputPath + $strOutputFileName + ".TMP") -Value $strHTML
    if(Test-Path -Path $($strOutputPath + $strOutputFileName + ".htm")){
     Copy-Item -Path $($strOutputPath + $strOutputFileName + ".htm") -Destination $($strOutputPath + $strOutputFileName + $(Get-Date -uformat "%Y%m%d") + ".htm") -Force
    Copy-Item -Path $($strOutputPath + $strOutputFileName + ".TMP") -Destination $($strOutputPath + $strOutputFileName + ".htm") -Force
    Remove-Item -Path $($strOutputPath + $strOutputFileName + ".TMP") -Force

vCenter servers are added/removed from the file “_All_vCenter_Hosts_.csv” .
A hosts NICs are moved to the exclude section, by adding it to the host exclude list “host_CDP_exclude.csv”.

The CDP information is sorted and saved in a HTML output file. If the script has been run before and an older version of the output file exist, it’s renamed and the new is saved (This supply you with a sort of history).

Get all script files here.


For CDP information to be visible  in the VI client, CDP must also be enabled/configured on your physical Cisco switch – see example.

To enable/change the CDP information on a host you have several options.

  1. If you are using dvSwitches in vSphere 4.x you can enable it from GUI .

    • Connect to vCenter using VI Client
    • From Home click Networking
    • Right click your dvSwitch and select Edit Settings
    • Under the Properties tab select Advanced
    • Check Cisco Discovery Protocol
    • Set Operation to Both
    • Click OK
  2. Using ESX Command line
    (exchange vSwitch1 with the name of your vSwitch)

    • Show current setting
      esxcfg-vswitch -b vSwitch1
    • Set the new status (down, listen, advertise, both)
      esxcfg-vswitch -B both vSwitch1
    • Verify new settings
      esxcfg-vswitch -b vSwitch1
  3. Using vMA

     vicfg-vswitch --server <vcenter.domain.com> -h <esxi.domain.com> -B both <vSwitch1>

To view your CDP info you also a few options
See all options here:

  1. GUI

    • Connect to vCenter or ESX using VI Client
    • Select a ESX host
    • Click the Configuration tab
    • Select Networking
    • Click the Info icon to the right of the vSwitch
    • A tool tip opens with CDP information for the selected physical network interface
  2. PowerShell
     Get-VMHost | Where-Object {$_.State -eq "Connected"} |
     %{Get-View $_.ID} |
     %{$esxname = $_.Name; Get-View $_.ConfigManager.NetworkSystem} |
     %{ foreach($physnic in $_.NetworkInfo.Pnic){
        $pnicInfo = $_.QueryNetworkHint($physnic.Device)
        foreach($hint in $pnicInfo){
           Write-Host $esxname $physnic.Device
           if( $hint.ConnectedSwitchPort ) {
           else {
              Write-Host "No CDP information available."; Write-Host

Monday, May 16, 2011

Poor man’s hostprofiles including ESX deployment

Filed under: PowerShell — Tags: , , , , , , , , — A. Mikkelsen @ 22:11 pm

Last year I was asked to reduce the time spend on installing and configuring our ESX hosts.  Because we weren’t using Enterprise Plus licenses, we didn’t have Host Profiles.
I came up with a simple two-step process based on the EDA appliance and a custom PowerShell script.

  • Install the host from PXE.
    Only setting the minimum configuration, so it’s as versatile as possible.

    • Disk layout
    • Network teaming
    • FQDN and IP
  • Configure the host using a custom PowerShell script.
    Based on the Datacenter and Cluster the host is to be added to.

    • Add to vCenter
    • Set COS memory
    • Enable VMotion
    • License host
    • And much more…

A lot of blog posts are available on the net, on how to setup and install an ESX host using the EDA appliance so I won’t trouble you with this.

The PowerShell script is divided into several sections, I won’t explain everyone, only the most relevant, the rest is documented in the full script.
All steps in the script are logged to a host specific log file.

  • User input – Only 3 things are asked for when running the script, the rest is stored in the scripts Static section.
    • FQDN of the host (must be configured in DNS to work)
    • The environment – what environment is the host to be placed in, like PROD, DMZ, etc.
    • The hosts VMotion IP.
  • Add the host to the right cluster.
    You must have get the Datacenter/Cluster variable prior to adding the host, else it will just be placed in the first datacenter (See script for more info).

     add-vmhost $strHost -location $strvCenterDatacenter -user $strHostUser -password $strHostUserPWD -force: $true
  • Set the host in maintenance mode
     Get-VMHost -Name $strHost | Set-VMHost -State maintenance
  • License host.
    As you can see below, you have to supply the full name of the license you are adding, not just the license key.

     $targethostMoRef = (get-VMHost $strHost | get-view).MoRef
     $si = Get-View ServiceInstance
     $LicManView=Get-View $LicManRef
     $licassman = (Get-View $LicManView.LicenseAssignmentManager)
     #$licassman.UpdateAssignedLicense($targethostMoRef.value,”YOUR LIC KEY”,”vSphere4 Enterprise Plus (1-12 cores per CPU”)
     $licassman.UpdateAssignedLicense($targethostMoRef.value,”YOUR LIC KEY”,”vSphere4 Enterprise (1-6 cores per CPU”)
  • Set the correct time zone.
     $strTimeZone = "Europe/Copenhagen"
     $tmpHost = Get-VMHost $strHost | get-view
     $tmpDTSystem =  $tmpHost.ConfigManager.DateTimeSystem
     $tmpMoRef = Get-View  $tmpDTSystem
     $tmpDateConfig = New-Object      Vmware.Vim.HostDateTimeConfig
     $tmpDateConfig.timeZone = $strTimeZone
  • Add NTP servers.
    Use an array of NTP servers like
    $arrNTPServer = @(“dk.pool.ntp.org”,”de.pool.ntp.org”,”us.pool.ntp.org”,”clock.cimat.ues.edu.sv”,”ntp1.gbg.netnod.se”,”ntp1.theremailer.net”)

     Add-VMHostNtpServer -VMHost $strHost -NtpServer $arrNTPServer -Confirm:$false

    If you want you can restart the NTP service – only do it if you don’t plan on restarting the host after configuring.

     Restart-VMHostService $ntpd -Confirm:$false
  • Open firewall rules
    I always open the following firewall rules so that NTP and the SSH is working

     Get-VmhostFirewallException -VMHost $strHost -Name "NTP Client" | Set-VMHostFirewallException -enabled:$true
     Get-VmhostFirewallException -VMHost $strHost -Name "SSH Client" | Set-VMHostFirewallException -enabled:$true
    	<li>Set the correct DNS servers
     Use an array of DNS servers, this way you can add multiple DNS servers at      once.
     @("","") or @("")
     $strHost = @("","")
     Get-VMHost -Name $strHost | Get-View | %{$tmpNS = Get-View -Id $_.configManager.networkSystem
     $tmpDNS = $tmpNS.NetworkConfig.DnsConfig
     $tmpDNS.domainName = $strHostDomain
     $tmpDNS.address = $arrDNSsrv
     $tmpDNS.searchDomain = $strSearchDomain
  • Configuring network, adding network including VMotion.
    See the script for the full script.

     $vSwitch = Get-VirtualSwitch $strHost -Name vSwitch0
     #Set-VirtualSwitch -VirtualSwitch $vSwitch -Nic vmnic1
     New-VirtualPortGroup -VirtualSwitch $vSwitch -Name "VMOTION" -VLanId 0
     New-VMHostNetworkAdapter -VMHost $strHost -PortGroup "VMOTION" -VirtualSwitch $vSwitch -IP $strVMotion -SubnetMask $strSubnetMask -VMotionEnabled $true
     $strNetConfig = Get-View (Get-VMHost $strHost | Get-View).ConfigManager.NetworkSystem
     $strIPRoute = New-Object VMware.Vim.HostIpRouteConfig
     $strIPRoute.defaultGateway = $strGateway
     New-VirtualPortGroup -VirtualSwitch $vSwitch -Name "PROD" -VLanId 200
     New-VirtualPortGroup -VirtualSwitch $vSwitch -Name "PXE" -VLanId 0

    If needed you can remove the default VM network

     $vSwitch = Get-VirtualSwitch $strHost -Name vSwitch0
     $vmnetwork = Get-VirtualPortGroup -VirtualSwitch $vSwitch -Name "VM Network"
     Remove-VirtualPortGroup -VirtualPortGroup $vmnetwork -Confirm:$false}
  • Reboot the host J
     Get-VMHost -Name $strHost | %{Get-View $_.ID} | %{$_.RebootHost_Task($TRUE)}
  • If you want your script to wait for the host to be rebooted before continuing, you can do it with these two loops.
     $result = Get-VMHost $strHost
     Start-Sleep 10 # Wait 10 sec
     Until($result.State -ne "Maintenance")
     # Maintenance - because host entered Maintenence mode earlier
     $result =  Get-VMHost $strHost
     Start-Sleep 10 # Wait 10 sec
     Until($result.State -eq "Maintenance")
  • Finally exit the host from maintenance mode.
     Get-VMHost -Name $strHost | Set-VMHost -State connected

If everything is configured correctly, DRS should start migrating VM’s to the newly added host.

This script helped reducing the time spend on installing and configuring a host from 90 minutes to about 18 minutes. That’s a reduction of more than 500%, time you can use on other cool PowerShell tasks. J

By using a script to customize our hosts, we also gained two other benefits.

  1. Compliance – a host is always installed and configured the exact same way each time.
  2. Each step is documented in a host specific log file.

The full script can be downloaded here.

Wednesday, April 27, 2011

Changing the virtual videocard settings

Filed under: PowerShell,Scripting — Tags: , , , , — A. Mikkelsen @ 14:51 pm

Some time ago I was asked to create a script that could list each VM’s videocard settings.

$VMs = Get-View -ViewType VirtualMachine | sort Name 
foreach($VM in $VMs){
	write "$($VM.Name) $($VM.Guest.Screen.Width)/$($VM.Guest.Screen.Height)"

As you could see that was quite simple.
I using the Get-View -ViewType to speed up the query – See LucD’s post on http://communities.vmware.com/message/1511671#1511671 for more info.

Then I was asked to set all VM’s videocard setting to Auto Detect (Default setting).

$vms = Get-View -ViewType VirtualMachine | sort Name
foreach ($vm in $vms){
	if ($vm.Runtime.PowerState -eq "PoweredOff") {
		if ($vm.Config.Version -eq "vmx-07"){ 
			foreach($dev in $vm.Config.Hardware.Device){
				if ($dev.DeviceInfo.Label -like "Video*"){
					if ($dev.UseAutoDetect -match "False" ) {
						$VMName = $vm.Name
						$VMcidcrd = New-Object VMware.Vim.VirtualMachineVideoCard 
						$VMcidcrd.UseAutoDetect = 1
						$VMcidcrd.Key = 500
						$spec = new-object VMware.Vim.VirtualDeviceConfigSpec
						$spec.Device = $VMcidcrd
						$spec.Operation = "edit"
						$VMSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
						$VMSpec.DeviceChange = $spec
						write "$($vm.Name) - Video Settings Changed"

As you can see this requires a bit more.
The VM has to be
• Powered Off
• Running vHW version 7
Download the full script here.

If you want to set the videocard setting to a specific MB, then just change the line from the above script

$VMcidcrd.UseAutoDetect = 0


$VMcidcrd.UseAutoDetect = 0

And add the line

$VMcidcrd.VideoRamSizeInKB = 131072

Where the 131072 us the amount of MB you want multiplied with 1024
Ex. 128 x 1024 = 131072

Friday, November 19, 2010

Project Onyx 2.0 is released

Filed under: PowerShell,Project Onyx — Tags: , , — A. Mikkelsen @ 22:09 pm

September 15, 2010 VMware released version 2.0 of its Project Onya Alpha.

You can download and read more about Project Onyx on the projects home page.

This project is a must have for all PowerCLI geeks 🙂

Friday, June 18, 2010

Powershell – Check if a service is running

Filed under: PowerShell — Tags: , , , , , , — A. Mikkelsen @ 9:22 am

If you ever have experienced that after your vCenter server reboots – due to Windows updates, the “VMware VirtualCenter Server” service is not starting?

I have seen it a few times and every time it’s during a weekend 🙁

So to make sure your vCenter service (or other) is always running you could use this powershell script to check if a service is running, and if not start it.
To make sure you know if the service wasn’t started after a reboot or other cause, I have added a mail function to the script 🙂

function FuncCheckService{
    $arrService = Get-Service -Name $ServiceName
    if ($arrService.Status -ne "Running"){
        Start-Service $ServiceName
        FuncMail -To "to-email@domain.com" -From "from-mail@domain.com"  -Subject "Servername : ($ServiceName) service started." -Body "Service $ServiceName started" -smtpServer "relay.mailserver.com"

function FuncMail {
    #param($strTo, $strFrom, $strSubject, $strBody, $smtpServer)
    param($To, $From, $Subject, $Body, $smtpServer)
    $msg = new-object Net.Mail.MailMessage
    $smtp = new-object Net.Mail.SmtpClient($smtpServer)
    $msg.From = $From
    $msg.Subject = $Subject
    $msg.IsBodyHtml = 1
    $msg.Body = $Body

FuncCheckService -ServiceName "VMware VirtualCenter Server"

Create a PS1 file and schedule it to run every 15 or 30 minutes.

It works great and is simple….

Thursday, June 17, 2010

Powershell – Configure NTP server list

If you want to configure a hosts NTP server list through PowerCLI you have a few cmdlet at your disposal.

  • Get-VMHostNtpServer
  • Remove-VMHostNtpServer
  • Add-VMHostNtpServer
  • Get-VMHostService
  • Set-VMHostService
  • Get-VmhostFirewallException
  • Restart-VMHostService

Get a list of NTP servers on a host.

Get-VMHostNtpServer -VMHost "esx01.lab.local"

To remove a specific NTP server from a host NTP server list.

Remove-VMHostNtpServer -VMHost "esx01.lab.local" -NtpServer '' -Confirm:$false

To remove all NTP servers on a hosts NTP server list.

$arrNTPList = Get-VMHostNtpServer -VMHost "esx01.lab.local"
Remove-VMHostNtpServer -VMHost "esx01.lab.local" -NtpServer $arrNTPList -Confirm:$false

To add a single NTP server to a hosts NTP server list.

Add-VMHostNtpServer -VMHost "esx01.lab.local" -NtpServer "dk.pool.ntp.org" -Confirm:$false

To add a a list of NTP servers to a hosts NTP server list.

$arrNTPServers = ("dk.pool.ntp.org","de.pool.ntp.org","us.pool.ntp.org")
Add-VMHostNtpServer -VMHost "esx01.lab.local" -NtpServer $arrNTPServers -Confirm:$false

To set the NTP deamon (service) to start automatic.

Set-VMHostService -HostService (Get-VMHostservice -VMHost (Get-VMHost "esx01.lab.local") | Where-Object {$_.key -eq "ntpd"}) -Policy "Automatic"

Open firewall to allow the NTP deamon to communicate with the NTP server.

Get-VmhostFirewallException -VMHost "esx01.lab.local" -Name "NTP Client" | Set-VMHostFirewallException -enabled:$true

To restart the NTP deamon (service) after you have configured it – without restarting the host.

$ntpd = Get-VMHostService -VMHost "esx01.lab.local" | where {$_.Key -eq 'ntpd'}
Restart-VMHostService $ntpd -Confirm:$false

Sunday, June 13, 2010

Playing with the vRanger 4.5 vAPI (Powershell :-))

Filed under: PowerCLI Scripts,PowerShell,vRanger — Tags: , , , — A. Mikkelsen @ 22:52 pm

Since Vizioncore came out with vRanger 4.x, last year, I been working on porting the VBS script “vRanger Script version 2.0” to Powershell.
During this time I have found several bugs/missing functionality in the vAPI.
Vizioncore have corrected the bugs with new releases of vRanger.
The first vRanger version where all vAPI cmdlets worked in was version 4.2.3.

Porting the script from VBS presented me with a few challenges.

  • A reported error in the Get-Date cmdlet in Powershel 2.0.
    Had to use a workaround.

A few words on the scrips functionality.

  • Control vRanger backups from vCenter (simple – one interface).
  • Perform a FULL backup of DiskO of a VM.
  • Perform a FULL backup of all disks of a VM.
  • Backup a VM on a specific weekday.
  • Control what week (Even/Odd).
  • Create a log file.
  • Delete previous backup files from repository.
  • Delete previous savepoints.
  • Send an email when the jobs are completed – incl. succes/errors.
  • Offload the files to tape – TSM
  • Send mail when TSM is finished offloading backup files.
  • And many more…..

When I started coding the script, I found, that if I used functions for all functionality the script was easier to read. Using functions also supplied me with the opportunity of reusing functions – less work :-).

Some of the functions was created using examples posted and mailed by Scott Harold from Quest and TheVesi./vEcoShell.


The script can be downloaded here.

A quick overview of the functions.
Include the right PSSnapins

function LoadSnapin{
    Add-PSSnapin vRanger.API.PowerShell -ErrorAction SilentlyContinue
    Add-PSSnapin vmware.VimAutomation.core -ErrorAction SilentlyContinue


Delete all previous savepoints – from the repository.

function RemoveAllSavePoints{
	param ($RASP_RepoName)
	$RASP_repoID = Get-Repository | where {$_.Name -eq $RASP_RepoName}
	$RASP_SPlist = Get-RepositorySavePoint -ID $RASP_repoID.Id

	foreach ($RASP_SP in $RASP_SPlist){
		#write $RASP_SP
		Remove-SavePoint -SavePointsToRemove $RASP_SP

RemoveAllSavePoints "Remote-Location"

Is a backup job exist with the same name as the one to create – delete it.

function Del-vRangerBackupJobs {
	param ($delJobName)
	$delTemplateID = Get-JobTemplate | where {$_.JobName -eq $delJobName}
	# Verify that a BackupJob named XXX is present before deleting it.
	if ($delTemplateID){
		#write $delTemplateID.JobName
		Remove-JobTemplate -id $delTemplateID.Id

Del-vRangerBackupJobs "$Wednesday-Even"

Create the list of VMs to exclude.

function Filter-VMbyCF {
    param ($customF, $val)
    foreach ($vm in $vmlist) {
        $vm.CustomFields | ForEach-Object {
            $cf = $_
            if ($_.Key -like $customF -and $_.Value -like $val){
                    return $vm.name

function New-vRangerExcludeList {
    param ($exclude, $include)
    $excludeArray = @()
    $exclude | ForEach-Object {
        if ($include -notcontains $_.Name){
            $excludeArray += ($_.Config.Uuid)
    return $excludeArray

$vmlist = Get-VM
$tmplist = Get-Template
$vmlist= $vmlist + $tmplist
$vmFilter = Filter-VMbyCF $customField $CFValue
$vmlistview = $vmlist | Get-View
$excludeList = New-vRangerExcludeList -exclude $vmlistview -include $vmFilter

Create the new backup job.

function New-vRangerBackupJob {
	param ($customJobName, $customCFValue, $customFieldName, $customRepoName, $customExcludeList, $customEmail)
        $jobDesc = "This backup job is created by Logica for use in DR.  All VMs with a Custom Field labeled: $customFieldName with a value of: $customCFValue will be backed up"
        $jobEntity = Get-InventoryEntity -Type VirtualCenter
	$jobRepos = Get-Repository | where {$_.Name -eq $customRepoName}
        $jobFlag = New-BackupFlag -CheckDestinationFreeSpace:$true -UseCompression:$true -PerformNetworkBackupOnFailure:$true

	# Check if all disks or just disk 0
	if ($customJobName -like "*-All*"){
		#write "All - Disks"
		#Add-BackupJobTemplate -JobName $customJobName -JobDescription $jobDesc -JobEntity $jobEntity -ExcludedVMList $customExcludeList -NotificationList $email -TargetRepository $jobRepos -Flags $jobFlag -NumberOfSavePoints 1 -SpaceSavingTechnologyTypeFlag None
		Add-BackupJobTemplate -JobName $customJobName -JobDescription $jobDesc -JobEntity $jobEntity -ExcludedVMList $customExcludeList -NotificationList $customEmail -TargetRepository $jobRepos -Flags $jobFlag -NumberOfSavePoints 1 -SpaceSavingTechnologyTypeFlag None -SpaceSavingCountThreshold 6 -SpaceSavingPercentSizeThreshold 50
		#write "Only Disk 0"
		#Add-BackupJobTemplate -JobName $customJobName -JobDescription $jobDesc -JobEntity $jobEntity -ExcludedVMList $customExcludeList -NotificationList $email -IncludedDiskList 0 -TargetRepository $jobRepos -Flags $jobFlag -NumberOfSavePoints 1 -SpaceSavingTechnologyTypeFlag None
		Add-BackupJobTemplate -JobName $customJobName -JobDescription $jobDesc -JobEntity $jobEntity -ExcludedVMList $customExcludeList -NotificationList $customEmail -IncludedDiskList 0 -TargetRepository $jobRepos -Flags $jobFlag -NumberOfSavePoints 1 -SpaceSavingTechnologyTypeFlag None -SpaceSavingCountThreshold 6 -SpaceSavingPercentSizeThreshold 50

New-vRangerBackupJob "Wednesday-Even" "Wednesday-Even" "Backup" "Remote-Location" $excludeList "to@email.com"

Start the backup job.

function Run-BackupJob{
	param ($runJobName)
	$runTemplateID = Get-TemplateID $runJobName
	Run-JobsNow $runTemplateID

Run-BackupJob "Wednesday-Even"

Wait for the backup job to finish.

function Get-TemplateID{
	$arrTemplate = get-jobtemplate | where {$_.JobName -eq $tmpJobName}
	return $arrTemplate.ID

function WaitForJobToFinish{
	param ($strWaitJob)
	$strCompleted = "NotStarted"
        Start-Sleep -Seconds 300	# 5 minutes
	$jobTemplates = get-JobTemplate | where {$_.JobName -eq $strWaitJob}
	foreach ($job in $jobTemplates){
			$tasksinfo = get-job |where {$_.ParentJobTemplateID -eq $job.TemplateVersionID}
			foreach ($task in $tasksinfo){
				Start-Sleep -Seconds 300	# 5 minutes
				$strCompleted = $task.JobState

                # Make sure job is running - if not start it...
                if ($task.JobState -eq $NULL -or $task.JobState -eq "NotStarted"){
                    Run-BackupJob $strWaitJob
                    Start-Sleep -Seconds 120	# 2 minutes
		while ($strCompleted -notmatch "Completed")

WaitForJobToFinish "Wednesday-Even"

Send TSM finished mail.

function FuncMail {
	#param($strTo, $strFrom, $strSubject, $strBody, $smtpServer)
	param($To, $From, $Subject, $Body, $smtpServer)

	$msg = new-object Net.Mail.MailMessage
	$smtp = new-object Net.Mail.SmtpClient($smtpServer)
	$msg.From = $From
	$msg.Subject = $Subject
	$msg.IsBodyHtml = 1
	$msg.Body = $Body

FuncMail -To "to@email.com" -From "from@email.com" -Subject "vRanger Pro: TSM backup finished" -Body "Your Body" -smtpServer "your.mailserver.com"

Monday, May 24, 2010

Smart and simple backup script with PowerCLI

Filed under: PowerCLI Scripts,PowerShell — Tags: , , , , , , , — A. Mikkelsen @ 21:32 pm

A friend on the Danish VMUG pointed me towards this simple backup script, from GestaltIT.com.

The script creates a snapshot of a VM (from a CSV file) and creates a Thin Provisioned clone based on the snapshot, on a specified datastore (from the CSV file).

This a smart and simple way to create a backup of a list of VM’s.

It’s a must read for PowerCLI users.

Older Posts »

Powered by WordPress