powerCLI function to set HA Admission Control Policy in percent

I am attending a VMware vSphere ICM 5 course this week, this because I am planning to become a VCI.

As I am a bit of PowerCLI fan i am trying to do all the labs in the course from the powerCLI console 🙂

In the HA laboration i found that the default implementation of the powerCLI does not allow you to set the HA Admission Control Policy Percentage, only the Host failure the cluster tolerates, here is a link to the powerCLI reference page for Set-Cluster. In the slides VMware recommends that you set percentage in a HA setup.

This led me to develop a powerCLI function that can configure this. The function has a default value for CPU and Memory that is 25 %, so if you only give the Cluster parameter the Capacity setting will be 25 %.

function Set-HAAdmissionControlPolicy{
<#
.SYNOPSIS
Set the Percentage HA Admission Control Policy

.DESCRIPTION
Percentage of cluster resources reserved as failover spare capacity

.PARAMETER  Cluster
The Cluster object that is going to be configurered 

.PARAMETER percentCPU
The percent reservation of CPU Cluster resources

.PARAMETER percentMem
The percent reservation of Memory Cluster resources

.EXAMPLE
PS C:\> Set-HAAdmissionControlPolicy -Cluster $CL -percentCPU 50 -percentMem 50

.EXAMPLE
PS C:\> Get-Cluster | Set-HAAdmissionControlPolicy -percentCPU 50 -percentMem 50

.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-01-19
#>
   param (
   [Parameter(Position=0,Mandatory=$true,HelpMessage="This need to be a clusterobject",
    ValueFromPipeline=$True)]
    $Cluster,
    [int]$percentCPU = 25,
    [int]$percentMem = 25
    )
    
    if(Get-Cluster $Cluster){
    
        $spec = New-Object VMware.Vim.ClusterConfigSpecEx
        $spec.dasConfig = New-Object VMware.Vim.ClusterDasConfigInfo
        $spec.dasConfig.admissionControlPolicy = New-Object VMware.Vim.ClusterFailoverResourcesAdmissionControlPolicy
        $spec.dasConfig.admissionControlPolicy.cpuFailoverResourcesPercent = $percentCPU
        $spec.dasConfig.admissionControlPolicy.memoryFailoverResourcesPercent = $percentMem
    
        $Cluster = Get-View $Cluster
        $Cluster.ReconfigureComputeResource_Task($spec, $true)
    }
}

Here is a screenshot when running the function

Updated->Schedule PowerShell script in windows 2003 to clean any files

I have set up an scheduled task in a windows 2003 that run a PowerShell script, this script runs every week and the purpose is to clean out logs from IIS that otherwise fills the volume.

I used the script from Thomas Maurer to check a folder for files to delete and have customized it after excellent input from Jeffery Hicks, he suggested that i put in parameters instead to make the script more flexible and that way it can be run on any folder in the server with -Path and also set number of days to save -DaysBack, the last parameter has a default value of 30 days:

# Delete all Files in specifed parameter Path 
# and parameter older than 30 day(s) default or
# set by parameter DaysBack
#
# Parameter delyxe Niklas Akerlund / RTS
param(
	[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$Path,
	$DaysBack = 30)

$CurrentDate = Get-Date
$DatetoDelete = $CurrentDate.AddDays(-$Daysback) 

if($Path -ne ""){
	Get-ChildItem $Path | Where-Object { $_.LastWriteTime -lt $DatetoDelete } | Remove-Item
}

I saved it in C:\Scripts\Clean and you must set the execution policy in PowerShell console otherwise you will not be able to run any scripts,

Set-ExecutionPolicy -ExecutionPolicy Remotesigned

then i created the scheduled task, some screen shots follows with the way to set up this, first selesct PowerShell

then select interval that the script is going to be run

I have selected that the script is going to be executed every week at mondays 9 AM

Then set an account and password and click next, the magic comes when going into the advanced properties

As you can see i have added a -File C:\Scripts\Clean\clean.ps1 -Path “C:\windows\system32\LogFiles\W3SVC1” -DaysBack 20 to the PowerShell.exe in the run field, otherwise i will not run any scripts 😉 only the powershell, the -File was also a suggestion from Jeffery Hicks 🙂

C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -File c:\Scripts\Clean\clean.ps1 -Path "C:\windows\system32\LogFiles\W3SVC1" -DaysBack 20

Quite simple but so powerful to not have to manually clean folders with logfiles that grows.

If you do not have PowerShell on your windows 2003 you have to install the Windows Management Framework first: kb968929

This script can also of course be run on xp, win7 and win 2008/2008 R2.

Populate and administer Active directory users with Powershell

Some days ago I have been working on some powershell scripts for a lab environment and now i have edited the handlescript to work better and with more functionality.

First i created a small script for populating some OU with student accounts.

# Script: Createusers.ps1
# Create users in AD
# Version: 0.1
# Niklas Akerlund /RTS

Import-Module Activedirectory

$OUs =  "OU1","OU2","OU3","OU4","OU5","OU6"

foreach ($OU in $OUs){
	$path = "OU=" + $OU + ",OU=lab,OU=Users,DC=demo,DC=local"
	for ($i=1;$i -le 10;$i++){
    	$SAMid = $OU + "Student0" + $i 
    	$Name = $OU + "Student0" + $i 
    	New-ADUser -SamAccountName $SAMid -Name $Name -AccountPassword (ConvertTo-SecureString -AsPlainText "S0meP@ssw0rd" -Force) -Enabled $true -Path $path
   
	}
}

 

Then i created with help from Jeffery Hicks post a script with a menu to enable or disable accounts in the different OU´s. As i described in a earlier post it is quite powerful to use Active Directory cmdlets and i have tried to take care of that by checking that when i enable/disable accounts i check that i really have something in my searchbase. Below are two screenshots of the script running. Update I have now updated the script with a check for the searchbase that actually works and also an extra menu option with the ability to set password on the accounts i enable!

 

Here is the script that creates this, i have not yet come up with a better and more dynamic way to create the switch, maybe someone can give me some help there..

# Script: handleusers.ps1
# Version: 0.1
# Disable or enable lab account users
# 
# Niklas Akerlund / RTS
# Menu code from Jeffery Hicks 

Import-Module ActiveDirectory
 
Function Show-Menu {

	Param(
		[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter your menu text")]
		[ValidateNotNullOrEmpty()]
		[string]$Menu,
		[Parameter(Position=1)]
		[ValidateNotNullOrEmpty()]
		[string]$Title="Menu",
		[switch]$ClearScreen
	)

	if ($ClearScreen) {Clear-Host}

	#build the menu prompt
	$menuPrompt=$title
	#add a return
	$menuprompt+="<code>n"
	#add an underline
	$menuprompt+="-"*$title.Length
	$menuprompt+="</code>n"
	#add the menu
	$menuPrompt+=$menu

	Read-Host -Prompt $menuprompt

} #end function

$menu="<code>n"
$ListOU = Get-ADOrganizationalUnit -filter * -Searchbase "OU=lab,OU=Users,DC=demo,DC=local" | where {$_.Name -ne "lab"} | Sort-Object $_.Name

$i = 1
foreach ($OU in $ListOU){
	   $menu+= [string]$i + " " + $OU.Name + "</code>n"
      # write-host $menu
       $i = $i+1
     
}
$menu+= "Q Quit"
$menu+= "<code>n"

# Menu 2 
$menu2=@"
1 Disable accounts
2 Enable accounts
3 Set password and enable accounts
B Back to menu
"@
$menu2+= "</code>n"

#Keep looping and running the menu until the user selects Q (or q).
Do {
    #use a Switch construct to take action depending on what menu choice
    #is selected.
    Switch (Show-Menu $menu "Get OU to handle" -clear ) {
     "1" {
	 		if ($ListOU[0].DistinguishedName -ne $null){
		 		$menutext = "Handle " + $ListOU[0].Name + " users to disable/enable "
				$Users = Get-ADUser -filter * -searchbase $ListOU[0].DistinguishedName
		 		Switch (Show-Menu $menu2 $menutext -clear ) {
	     		"1" {
		 					$Users |  Set-ADUser -Enabled $false	
	         		} 
	     		"2" {
		 					$Users |  Set-ADUser -Enabled $true	
	          		}
	     		"3" {
							$newPassword = (Read-Host -Prompt "Provide New Password" -AsSecureString)
		 					$Users | Set-ADUser -Enabled $true	
							$Users | Set-ADAccountPassword -NewPassword $newPassword -Reset
	          		}
				"B" {
						Write-Host "Back to menu" -ForegroundColor Cyan
	        		}
				Default {Write-Warning "Invalid Choice. Try again."
	              sleep -milliseconds 750}
	         	}
			}
		 }
     "2"  {
	 		if ($ListOU[1].DistinguishedName -ne $null){
		 		$menutext = "Handle " + $ListOU[1].Name + " users to disable/enable " 
				$Users = Get-ADUser -filter * -searchbase $ListOU[1].DistinguishedName
		 		Switch (Show-Menu $menu2 $menutext -clear ) {
	     		"1" {
		 				
		 					$Users |  Set-ADUser -Enabled $false	
						
	         		} 
	     		"2" {
		 					$Users |  Set-ADUser -Enabled $true	
	          		}
				"3" {
							$newPassword = (Read-Host -Prompt "Provide New Password" -AsSecureString)
		 					$Users | Set-ADUser -Enabled $true	
							$Users | Set-ADAccountPassword -NewPassword $newPassword -Reset
	          		}
				"B" {
						Write-Host "Back to menu" -ForegroundColor Cyan
	        		}
				Default {Write-Warning "Invalid Choice. Try again."
	              sleep -milliseconds 750}
	         	} 
			}
		 }
     "3" {
	 		if ($ListOU[2].DistinguishedName -ne $null){
		 		$menutext = "Handle " + $ListOU[2].Name + " users to disable/enable " 
				$Users = Get-ADUser -filter * -searchbase $ListOU[2].DistinguishedName
		 		Switch (Show-Menu $menu2 $menutext -clear ) {
	     		"1" {
		 					$Users |  Set-ADUser -Enabled $false		
	         		} 
	     		"2" {
		 					$Users |  Set-ADUser -Enabled $true	
	          		}
				"3" {
							$newPassword = (Read-Host -Prompt "Provide New Password" -AsSecureString)
		 					$Users | Set-ADUser -Enabled $true	
							$Users | Set-ADAccountPassword -NewPassword $newPassword -Reset
	          		}
				"B" {
						Write-Host "Back to menu" -ForegroundColor Cyan
	        		}
				Default {Write-Warning "Invalid Choice. Try again."
	              sleep -milliseconds 750}
	         	} 
			}
		 }
	 "4" {
	 		if ($ListOU[3].DistinguishedName -ne $null){
		 		$menutext = "Handle " + $ListOU[3].Name + " users to disable/enable " 
				$Users = Get-ADUser -filter * -searchbase $ListOU[3].DistinguishedName
		 		Switch (Show-Menu $menu2 $menutext -clear ) {
	     		"1" {
		 					$Users |  Set-ADUser -Enabled $false		
	         		} 
	     		"2" {
		 					$Users |  Set-ADUser -Enabled $true	
	          		}
				"3" {
							$newPassword = (Read-Host -Prompt "Provide New Password" -AsSecureString)
		 					$Users | Set-ADUser -Enabled $true	
							$Users | Set-ADAccountPassword -NewPassword $newPassword -Reset
	          		}
				"B" {
						Write-Host "Back to menu" -ForegroundColor Cyan
	        		}
				Default {Write-Warning "Invalid Choice. Try again."
	              sleep -milliseconds 750}
	         	} 
			}
		 }
	 "5" {
	 		if ($ListOU[4].DistinguishedName -ne $null){
		 		$menutext = "Handle " + $ListOU[4].Name + " users to disable/enable " 
				$Users = Get-ADUser -filter * -searchbase $ListOU[4].DistinguishedName
		 		Switch (Show-Menu $menu2 $menutext -clear ) {
	     		"1" {
		 					$Users |  Set-ADUser -Enabled $false		
	         		} 
	     		"2" {
		 					$Users |  Set-ADUser -Enabled $true	
	          		}
				"3" {
							$newPassword = (Read-Host -Prompt "Provide New Password" -AsSecureString)
		 					$Users | Set-ADUser -Enabled $true	
							$Users | Set-ADAccountPassword -NewPassword $newPassword -Reset
	          		}
				"B" {
						Write-Host "Back to menu" -ForegroundColor Cyan
	        		}
				Default {Write-Warning "Invalid Choice. Try again."
	              sleep -milliseconds 750}
	         	} 
			}
		 }
	 "6" {
	 		if ($ListOU[5].DistinguishedName -ne $null){
		 		$menutext = "Handle " + $ListOU[5].Name + " users to disable/enable " 
				$Users = Get-ADUser -filter * -searchbase $ListOU[5].DistinguishedNam
		 		Switch (Show-Menu $menu2 $menutext -clear ) {
	     		"1" {
		 					$Users |  Set-ADUser -Enabled $false	
						
	         		} 
	     		"2" {
		 					$Users |  Set-ADUser -Enabled $true	
	          		}
				"3" {
							$newPassword = (Read-Host -Prompt "Provide New Password" -AsSecureString)
		 					$Users | Set-ADUser -Enabled $true	
							$Users | Set-ADAccountPassword -NewPassword $newPassword -Reset
					}          	
				"B" {
						Write-Host "Back to menu" -ForegroundColor Cyan
	        		}
				Default {Write-Warning "Invalid Choice. Try again."
	              sleep -milliseconds 750}
	         	} 
			}
		 }
     "Q" {Write-Host "Goodbye" -ForegroundColor Cyan
         Return
         }
     Default {Write-Warning "Invalid Choice. Try again."
              sleep -milliseconds 750}
    } #switch
} While ($True)


 

Create and destroy Logical Networks with powershell in SCVMM 2012

I am at a customer and setting up an new environment with Hyper-V hosts and SCVMM 2012 RC, we have succesfully created host-vhd and got bare-metal deploy working including HP PSP software and NIC-Teaming configuration. The hosts are deployed with Windows 2008 R2 Datacenter Core SP1 on HP BL460c G7 blades with flexfabric and an EMC SAN delivers the disks.

Now we had to get the network configured, the customer has several VLANs and i have configured the Hyper-V hosts with the HP nic team tool and have two teamed NICs for the VM traffic. To not have to configure one virtual nic/switch for each VLAN i have enabled promiscuous mode.

Now to make it easier we create a bunch of Logical Networks with some powershell in the SCVMM 2012 powershell console, this because setting up 20 networks manually and also associate them on every host in the cluster is kind of boring, cause if i do not associate the logical networks to the hosts VM adapter i can not assign them on the VMs.

So what does the script look like, the help info from SCVMM powershell console and also view script button in the helps allot when trying to find out what i need to make it work. There are some differences in the cmdlets between vmm 2008 r2 and the 2012 rc.

# Create SCVMM virtual networks and associate them with hosts
#
# Niklas Akerlund /RTS

$VLANs = Import-Csv vlan1.csv -Delimiter ";"
$VMHostGroup = Get-SCVMHostgroup
$VMHosts = Get-SCVMHost
$LogicalNetworks = @()
foreach ($VLAN in $VLANs){
    $LogicalNetwork = New-SCLogicalNetwork -Name $VLAN.Name
    $LogicalNetworks += $LogicalNetwork
    $Network = $VLAN.IPnet + $VLAN.Octet
    $SubnetVlan = New-SCSubnetVLan -Subnet $Network -VLanID $VLAN.VLAN
    New-SCLogicalNetworkDefinition -Name $VLAN.Name -LogicalNetwork $logicalNetwork -VMHostGroup $VMHostGroup -SubnetVLan $SubnetVlan
    
}

foreach ($VMHost in $VMHosts){   
    $vmHostNetworkAdapter = Get-SCVMHostNetworkAdapter -Name "VM" -VMHost $VMHost
    foreach ($LogicalNet in $LogicalNetworks){
        Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $vmHostNetworkAdapter -AddOrSetLogicalNetwork $logicalNet
    }
}

After running the script it looks like this in the properties on one Logical Network.

After this i also want to be able to remove the Logical Networks (or at least some) when they are not needed anymore, As there are some dependencies we have to remove these first and at last the Logical Network

# Remove Logical Networks from SCVMM2012
#
# Niklas Ã…kerlund /RTS
#

$VLANs = Import-CSV vlan1.csv -Delimiter ";"
$VMHosts = Get-VMHost

foreach ($VLAN in $VLANs){
    $LogicalNetwork = Get-SCLogicalNetwork $VLAN.Name
    if ($LogicalNetwork -ne $null){
        foreach ($VMHost in $VMHosts){
            $VMHostNetworkAdapter = Get-SCVMHostNetworkAdapter -Name "VM" -VMHost $VMHost
            Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $VMHostNetworkAdapter -RemoveLogicalNetwork $LogicalNetwork
        }
        $LogicalNetworkDefinition = Get-SCLogicalNetworkDefinition -LogicalNetwork $LogicalNetwork 
        Remove-SCLogicalNetworkDefinition -LogicalNetworkDefinition $LogicalNetworkDefinition
    
        Remove-SCLogicalNetwork -LogicalNetwork $LogicalNetwork
    }
}

To get some more information about the powershell cmdlets look at this reference site.
.

PowerCLI update on VM network cards and types report also with MAC´s

Today i added some fields in my little reportscript for the VM and their NICs, the reason was because of a customer that had an issue with duplicate MAC´s on their network.

We had earlier this year moved some VMs from an old vCenter to a new, i have made a blog post about the migration and the script we ran there.

Now when they started to deploy new VMs on the old vCenter it gave out the same MAC addresses as the ones on the VM´s that we had moved.. not so good, there is a fix that can be implemented on the old vCenter so it will start using new MAC´s instead, if you have this issue you can read the following KB 1024025 and set a new ID on the old vCenter and restart the service 🙂

But to check the VMs on both vCenter servers i ran the following script to get the data, the customer wanted both the VM name and the hostname/fqdn from the vm, also for every nic if it was generated or assigned.

# Get the Virtual Network Adapter
# 
# Niklas Akerlund / RTS

$VMs = Get-VM *
$Data = @()

foreach ($VM in $VMs){
 	$VMGuest = Get-View $VM.Id	
	$NICs = $VM.NetworkAdapters
	foreach ($NIC in $NICs) {
		$into = New-Object PSObject
		Add-Member -InputObject $into -MemberType NoteProperty -Name VMname $VM.Name
		Add-Member -InputObject $into -MemberType NoteProperty -Name VMfqdn $VM.Guest.HostName
		Add-Member -InputObject $into -MemberType NoteProperty -Name NICtype $NIC.Type
		Add-Member -InputObject $into -MemberType NoteProperty -Name MacAddress $NIC.MacAddress
		Add-Member -InputObject $into -MemberType NoteProperty -Name AddresType $NIC.ExtensionData.AddressType
		$Data += $into
		
	}

}
$Data | Export-Csv -Path c:\powercli\VMNICinfo.csv -NoTypeInformation

Matt Boren that has the site vnugglets.com helped me with an more efficient way of getting the data, my script took about 5-10 minutes and Matt´s took 30 seconds, his key to lower time is using the Get-View for everything (i was only using it to get the vm.guest.hostname)

&{Get-View -ViewType VirtualMachine -Property Name, Guest.HostName, Config.Hardware.Device | %{
    $viewThisVM = $_
    $viewThisVM.Config.Hardware.Device | ?{$_ -is [VMware.Vim.VirtualEthernetCard]} | %{
        New-Object -Type PSObject -Property @{
            VMname = $viewThisVM.Name
            VMfqdn = $viewThisVM.Guest.HostName
            NICtype = $_.GetType().Name
            MacAddress = $_.MacAddress
            AddressType = $_.AddressType
        } ## end new-object
    } ## end foreach-object
} ## end foreach-object
} | Select VMname,VMfqdn,NICtype,MacAddress,AddressType | Export-Csv -Path C:\VMNICinfo2.csv -NoTypeInformation -UseCulture

And the result when imported into excel looks something like this:

 

Virtual Machine VMDK file report with PowerCLI

I have created a simple report-script that gives a list of what kind of format and how many vmdk each VM has. The report tells me if the disks are Thin or Thick and what size they are i GB.

after some magic in Excel it looks like this 🙂

And the powerCLI script looks like this, it is quite simple but still gives me information that i need for all my VMs on all datastores and quickly i can tell which machines that uses a lot of disk on my precious SAN 😉

 

# Get data about vmdk and format
# 
# Niklas Ã…kerlund / RTS

$VMs = Get-VM *
$Data = @()

 foreach ($VM in $VMs){
	$VMDKs = $VM | get-HardDisk
	foreach ($VMDK in $VMDKs) {
		if ($VMDK -ne $null){
			$CapacityGB = $VMDK.CapacityKB/1024/1024
			$CapacityGB = [int]$CapacityGB
			$into = New-Object PSObject
			Add-Member -InputObject $into -MemberType NoteProperty -Name VMname $VM.Name
			Add-Member -InputObject $into -MemberType NoteProperty -Name Datastore $VMDK.FileName.Split(']')[0].TrimStart('[')
			Add-Member -InputObject $into -MemberType NoteProperty -Name VMDK $VMDK.FileName.Split(']')[1].TrimStart('[')
			Add-Member -InputObject $into -MemberType NoteProperty -Name StorageFormat $VMDK.StorageFormat
			Add-Member -InputObject $into -MemberType NoteProperty -Name CapacityGB $CapacityGB
			$Data += $into
		}
	}

}
$Data | Sort-Object VMname,Datastore,VMDK | Export-Csv -Path C:\temp\VM-VMDKs.csv -NoTypeInformation

Move VMs from an old SAN to an new using powerCLI part 2

Hi

I was informed that my script in the last post was not enough because @pfuhli has a bit more complex environment and then the Move-VM cmdlet is not sufficient because it moves the whole vm to the new datastore, no matter if the vmdk´s where located on different before.

As in some cases you have an virtual platform with different datastores for different performance levels and one VM has it´s vmdk configured to get the best throughput. So i  did this with help from a script that Luc Dekens did in a communities post, mine added some functionality as his only moved the config file.

please comment if you find something crazy, I have now started to get the hang of why I would use functions :-), yes i should add some error-checking, that will be in version 0.3

Probably it would take some time to get through 500 VM´s but instead of manual work it is worth it.

# Move VMs with sVMotion where vmdk is on different datastores and
# lastly move the config file to the same datastore as hard disk 1
#
# Niklas Ã…kerlund / RTS 20111127
# Part of code from Luc Dekens http://communities.vmware.com/message/1680735

# Here i extended Luc´s function for moving only config
function Move-VMs{
    param($vm)
	Write-Host $vm
    $HDDs = Get-HardDisk -VM $vm    
	# a foreach loop to move vmdk
	$HDDs | %{
		# Get the datastore name of the old
		$oldDS = $_.Filename.Split(']')[0].TrimStart('[')
		# as @pfuhli said the new lun has a preceding letter that differs from the old.
		$newDS = "N" + $oldDS 
		# Here i check which is the first hdd to later move the config there
		if ($_.Name -eq "Hard disk 1"){
			$dsNameHDD1 = $newDS
		}
		$newDS = Get-Datastore $newDS
		Set-HardDisk -HardDisk $_ -Datastore $newDS -Confirm:$false
	}
	
	# This part is for moving the config file
	$HDDs = Get-HardDisk -VM $vm
	$spec = New-Object VMware.Vim.VirtualMachineRelocateSpec 
	$spec.datastore = (Get-Datastore -Name $dsNameHDD1).Extensiondata.MoRef
    $HDDs | %{
        $disk = New-Object VMware.Vim.VirtualMachineRelocateSpecDiskLocator
        $disk.diskId = $_.Extensiondata.Key
        $disk.datastore = $_.Extensiondata.Backing.Datastore
        $spec.disk += $disk
    }
    $vm.Extensiondata.RelocateVM_Task($spec, "defaultPriority")
}

Get-VM | %{ 
	Move-VMs $_ 
}

 

Before running it on all VM´s i would test it on a few and then when feeling comfortable, you can move all 😀

Move VMs from an old storage array to a new using powerCLI

I have after reading a tweet written the simplest script for a SAN exchange, my script just look for the VMs associated with one datastore and storage vmotion them to an new datastore with no downtime 🙂 of course this requires Enterprise or higher in your vSphere licensing.

I have done some assumptions that there is equally many datastores provisioned in the new as the old and that no VMs have RDMs and vmdk on several datastores.

The Get-Datastore cmdlet can filter using wildcards like *c2* if your naming convension is complex and you need to find your old/new datastores objects.

For each old datastore i wait 30 minutes before starting on the next, maybe this must be set a bit higher depending on how long the storage vmotion takes and how large the datastores/vmdk´s are. Maybe we should put a sleep after starting move of each VM?! I have not yet had the pleasure testing in a large environment yet..

# Move VMs to new datastore using SVMotion
#
# Niklas Akerlund /RTS
#

# I want all old and new datastores as objects in arrays
$OldDatastores = Get-Datastore vmfs-volumes-old0*
$NewDatastores = Get-Datastore vmfs-volumes-new0*
$i = 0

# Get all VMs in each old datastore and move them
Foreach ($OldDatastore in $OldDatastores){
	$VMs = Get-VM -Datastore $OldDatastore
	
	Foreach ($VM in $VMs)
	{
		# Move the VM to a new datastore
		$VM | Move-VM -Datastore $NewDatastores[$i] -RunAsync
		
	}
	
	$i++
	# we want to give the SVMotions a little time before taking the next datastore 
	Start-Sleep 1800
}

I would recommend testing on a single datastore or a few VMs and when feeling comfortable running on all datastores..

HTML report checking your vSphere host configuration by powerCLI version 0.1

I have today started creating a script that is sort of a check that when installing new hosts, all of them are configured the same.

It is still very simple but yet kind of powerfull, we can easily see in the html report if some vmk nic is on the wrong IP subnet or not Jumboframes activated, the following screen dump shows the report, yes it is no fancy headlines and stuff yet 😉

I am going to work a lot more such as built in error checking and try to get some nice colors if a value differs with the other hosts, and also reporting on vSwitches and portgroups that they have the right uplinks etc, but that will have to be tomorrow or another day 🙂

# Check for Host Configuration and report
#
# Niklas Åkerlund / RTS
#

$vCenter = "vcenter.demo.local"
$AdvConf = @()
$Cluster = "Cluster1"

# We only want to get info from hosts that are online
Connect-VIServer $vCenter

$VMHosts = Get-Cluster -Name $Cluster | Get-VMHost | where {$_.ConnectionState -eq "Connected" }

# Get the cluster config 
$ClusterConf = Get-Cluster -Name $Cluster | Select-Object Name,HAEnabled,HAAdmissionControlEnabled,HAIsolationResponse,VMSwapfilePolicy,DrsEnabled,DrsMode,DrsAutomationLevel | ConvertTo-Html -Fragment


# Get Basic Conf 
$BaseConf = $VMHosts | Select-Object Name,Model,NumCPU,MemoryTotalMB,Version,Build,VMSwapfileDatastore | Sort-Object Name | ConvertTo-Html -Fragment

#adv config settings
foreach ($VMHost in $VMHosts){ 
		$into = New-Object PSObject
		Add-Member -InputObject $into -MemberType NoteProperty -Name VMHost $VMHost.Name
		$AdvScratch = Get-VMHostAdvancedConfiguration -VMHost $VMHost -Name ScratchConfig.ConfiguredScratchlocation
		$AdvScratch = [string]$AdvScratch.Values
		Add-Member -InputObject $into -MemberType NoteProperty -Name ScratchLocation $AdvScratch
		$AdvSwap = Get-VMHostAdvancedConfiguration -VMHost $VMHost -Name ScratchConfig.CurrentSwapState
		$AdvSwap = [string]$AdvSwap.Values
		Add-Member -InputObject $into -MemberType NoteProperty -Name Swapstate $AdvSwap
		$AdvSyslogRemote = Get-VMHostAdvancedConfiguration -VMHost $VMHost -Name Syslog.Remote.Hostname
		$AdvSyslogRemote = [string]$AdvSyslogRemote.Values
		Add-Member -InputObject $into -MemberType NoteProperty -Name SyslogRemote $AdvSyslogRemote
		$AdvSyslogLocal = Get-VMHostAdvancedConfiguration -VMHost $VMHost -Name Syslog.Local.DatastorePath
		$AdvSyslogLocal = [string]$AdvSyslogLocal.Values
		Add-Member -InputObject $into -MemberType NoteProperty -Name SyslogLocal $AdvSyslogLocal
		$AdvConf += $into

}

$AdvConf = $AdvConf | Sort-Object VMhost | ConvertTo-Html -Fragment

# Vmk ports and their MTU

$NetConf = $VMHosts | Get-VMHostNetworkAdapter | where {$_.Name -match "vmk"} | Select-Object VMHost,Name,IP,VMotionEnabled,FaultToleranceLoggingEnabled,ManagementTrafficEnabled,Mtu,PortGroupName | Sort-Object Name,VMHost | ConvertTo-Html -Fragment

# Create the html report from the different parts 
ConvertTo-Html -body "RTS Install documentation <p> $ClusterConf <p> $BaseConf <p> $AdvCOnf <p> $NetConf" -Title "RTS Installationscheck" | Out-File install.html

 

PowerCLI to the rescue, how to check all VMs for Network card type

I got a question what network card some VMs had in a datacenter, as a best practice you should use VMXNET 3 where it is possible because it gives the best performance.

So i wrote this very simple script in a few lines that do a csv export of all VMs and what kind of NIC they have, of course one could extend it with OS and stuff but that will have to be next time cause my schedule is kind of tight.

When you use the wizard to set up a Windows 2008 R2 the vSphere set an E1000 NIC by default and that is not what we want, so set up a correct template or remove this nic and add a new when installing single machines!

# Get the Virtual Network Adapter
#
# Niklas Ã…kerlund / RTS
$VMs = Get-VM *
$Data = @()
foreach ($VM in $VMs){
$NICs = $VM.NetworkAdapters
foreach ($NIC in $NICs) {
$into = New-Object PSObject
Add-Member -InputObject $into -MemberType NoteProperty -Name VMname $VM.Name
Add-Member -InputObject $into -MemberType NoteProperty -Name NICtype $NIC.Type
$Data += $into
}
}
$Data | Export-Csv -Path e:\temp\admna\NICs.csv -NoTypeInformation