VM inventory function with Powershell on SCVMM 2012

Had a plan to do a blog post last week but was so busy studying for the VCP 5 test. I wrote the test on friday and i passed 🙂

I have created a little powershell function, this to get the inventory information about the virtual machines running in hosts or clusters that are connected to a System Center Virtual Machine Manager. This function collects number of configured vCPU´s, Memory, information about the connected virtual harddisks. This kind of information could be handy as in the case with one customer that has not yet implemented a host based backup solution and still use agents in their VM´s, so if they need to recover machines it is good to know what size and configuration it had if it is lost by some reason. This report can also be handy for some info on how much your datastores are filled. As you can se i both report the maximum size and the current (as dynamic disks are not allocating the whole size at creation time)

The function both export a csv and a HTML report

After importing it to excel it can look something like this:

And the html output is very simple but can be put on a internal web for IT production

I have used the Get-SCVirtualDisk to get the Bus Type and LUN id as this information is not available in Get-SCVirtualHarddisk, this information can be good if i lost the virtual machine configuration and want to connect the virtual harddisks to the right controller.

the function looks like this: (if you copy this you have to move the first # up to the <, the blog syntax highlighter does something and move it down :-( )

function Get-VMdata{
<#
.SYNOPSIS
Get the configuration data of the VMs in Hyper-V via SCVMM 2012
 
.DESCRIPTION
Use this function to get all VMs configuration in case of disaster or just statistics
 
.PARAMETER  xyz 
 
.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-02-13
#>
param (
	$VMHostGroup = "All Hosts",
	[Parameter(ValueFromPipeline=$True)][Alias('ClusterName')]
	$VMHostCluster = $null,
	$VMHost = $null,
	[string]$CSVFile = "VMdata.csv",
    [string]$HTMLReport = "VMdata.html"
    )
	
	$report = @()
	if ($VMHostCluster -eq $null){
		$VMHosts = (Get-SCVMHostGroup -Name $VMhostGroup).AllChildHosts
	}else{
			$VMHosts = (Get-SCVMHostCluster -Name $VMHostCluster).Nodes
	}
	$VMs = $VMHosts | Get-SCVirtualMachine
	
	foreach ($VM in $VMs) {
		$VHDs = $VM | Get-SCVirtualDiskDrive
		$i = "1"
		foreach ($VHDconf in $VHDs){ 
			if($i -eq "1"){
				$data = New-Object PSObject -property @{
					VMName=$VM.Name
			        vCPUs=$VM.CPUCount
			        MemoryGB= $VM.Memory/1024
					VHDName = $VHDconf.VirtualHardDisk.Name
					VHDSize = $VHDconf.VirtualHardDisk.MaximumSize/1GB
					VHDCurrentSize = [Math]::Round($VHDconf.VirtualHardDisk.Size/1GB)
					VHDType = $VHDconf.VirtualHardDisk.VHDType
					VHDBusType = $VHDconf.BusType
					VHDBus = $VHDconf.Bus
					VHDLUN = $VHDconf.Lun
					VHDDatastore = $VHDconf.VirtualHardDisk.HostVolume
				}
				$i= "2"
			}else{
				$data = New-Object PSObject -property @{
					VMName=""
			        vCPUs=""
			        MemoryGB= ""
					VHDName = $VHDconf.VirtualHardDisk.Name
					VHDSize = $VHDconf.VirtualHardDisk.MaximumSize/1GB
					VHDCurrentSize = [Math]::Round($VHDconf.VirtualHardDisk.Size/1GB)
					VHDType = $VHDconf.VirtualHardDisk.VHDType
					VHDBusType = $VHDconf.BusType
					VHDBus = $VHDconf.Bus
					VHDLUN = $VHDconf.Lun
					VHDDatastore = $VHDconf.VirtualHardDisk.HostVolume
				}
			}
			$report +=$data	
		}
	}
	$report | Select-Object VMName,vCPUs,MemoryGB,VHDName,VHDSize,VHDCurrentSize,VHDType,VHDBusType,VHDBus,VHDLUN,VHDDatastore | Export-Csv -Path $CSVFile -NoTypeInformation -UseCulture
	$report | Select-Object VMName,vCPUs,MemoryGB,VHDName,VHDSize,VHDCurrentSize,VHDType,VHDBusType,VHDBus,VHDLUN,VHDDatastore | ConvertTo-HTML | Out-File $HTMLReport
}

and i can run it in some different ways, as you see in this screenshot, if i do not give any parameters it will use VMHostGroup “All Hosts” as default

SCVMM 2012 powershell function for configure network on hosts

After bare metal deployment of the hosts we need to configure the network on the hosts, this can be done from the SCVMM 2012 powershell console.

I have made a function of the creation of first a virtual switch and also adding the logical networks, if i do not add the networks to the particular host nic where the virtual switch is i cannot deploy virtual machines and get them connected to the network.

You can add the logical networks and their associated vlan/subnet before any host is imported into SCVMM and of course i have created a function for that also, this can take a csv file or just the parameters

Here is a screen dump if i run it without a csv file

and the result is the following

And here is the powershell code for the add logical networks function

function Add-SCLogicalNetworks{
<#
.SYNOPSIS
Add logical networks to your SCVMM fabric
 
.DESCRIPTION
With this function you add logical networks
 
.PARAMETER  CSVNetworks
Path to a CSV file with the logical network info 
 
.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-02-02
#>
param (
    $CSVNetworks = "",
	$VMHostGroup = "All Hosts",
	[string]$Name = "",
	[string]$IPnet = "",
    	[string]$Octet = "24",
	[int]$VLAN = 0
    )
$VMHostGroup = Get-SCVMHostgroup $VMHostGroup

if ($CSVNetworks -ne "") {	
	$VLANs = Import-Csv $CSVNetworks -Delimiter ";"

	foreach ($VLAN in $VLANs){
	    $LogicalNetwork = New-SCLogicalNetwork -Name $VLAN.Name
	    $Network = $VLAN.IPnet + "/" + $VLAN.Octet
	    $SubnetVlan = New-SCSubnetVLan -Subnet $Network -VLanID $VLAN.VLAN
	    New-SCLogicalNetworkDefinition -Name $VLAN.Name -LogicalNetwork $logicalNetwork -VMHostGroup $VMHostGroup -SubnetVLan $SubnetVlan 
	}
}else{
	$LogicalNetwork = New-SCLogicalNetwork -Name $Name
	$Network = $IPnet + "/" + $Octet
	$SubnetVlan = New-SCSubnetVLan -Subnet $Network -VLanID $VLAN
	New-SCLogicalNetworkDefinition -Name $Name -LogicalNetwork $logicalNetwork -VMHostGroup $VMHostGroup -SubnetVLan $SubnetVlan 
}
}

when i have the logical networks i want to configure the host

And the result is the following

And this is also displayed on the physical nic

and here is the powershell code for configuring the host:

function Configure-SCVMhostNetwork{
<#
.SYNOPSIS
Configure the network on the Hyper-V Host
 
.DESCRIPTION
With this function you set virtual switch and configure logical networks
 
.PARAMETER  VMHost
The new host that is going to be configurered 

.PARAMETER refVMHost
if there are different hyper-v clusters and logical networks, use a ref host for the configuration to set it up
 
.PARAMETER VMHostnic
The nic that is going to be configured with a virtual switch (in this case a NIC team named "VM"
 
.EXAMPLE
PS C:\> Get-SCVMHost Hyp04 | Configure-SCVMhostNetwork

.EXAMPLE
PS C:\> Configure-SCVMhostNetwork -VMHost Hyp04 -refVMHost Hyp06
 
.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-02-02
#>
param (
   [Parameter(Position=0,Mandatory=$true,HelpMessage="This need to be a Hyper-V Host name or object",
    ValueFromPipeline=$True)]
    $VMHost,
	[string]$refVMHost = "",
	[string]$VirtualNetworkName = "VMs",
    [string]$VMHostnic = "VM"
    )
 
$VMHost = Get-VMHost $VMHost
if ($VMHost -ne $null) {
	if ($refVMHost -eq ""){
		$LogicalNetworks = Get-SCLogicalNetwork
	}else
    {
		$LogicalNetworks = Get-SCLogicalNetwork -VMHost $refVMHost
	}
	
	$vmHostNetworkAdapter = Get-SCVMHostNetworkAdapter -VMHost $VMHost -Name $VMHostnic
	$virtualNetwork = New-SCVirtualNetwork -VMHost $VMHost -Name $VirtualNetworkName -Description "" -BoundToVMHost $false -VMHostNetworkAdapters $vmHostNetworkAdapter	   
	$vmHostNetworkAdapter = Get-SCVMHostNetworkAdapter -Name $VMHostnic -VMHost $VMHost
	foreach ($LogicalNet in $LogicalNetworks){
		Set-SCVMHostNetworkAdapter -VMHostNetworkAdapter $vmHostNetworkAdapter -AddOrSetLogicalNetwork $logicalNet
	}
}
}

 

Updated powerCLI function for setting HA Admission control policy in percent with number of hosts

I got the question from a colleague if i could add a parameter in my function, no problem, just some dividing..

So you can either set percent CPU/Memory or number of hosts and from that get a percent on the policy. I have not taken care of the case if you have more than 100 hosts in your cluster (as this is not possible, at least not yet 😉 )

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 numberHosts
When this parameter is set the percentage is set based on number of hosts in cluster
 
.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

.EXAMPLE
PS C:\> Set-HAAdmissionControlPolicy -Cluster $CL -numberHosts 5
 
.NOTES
Author: Niklas Akerlund / RTS
Date: 2012-01-25
#>
   param (
   [Parameter(Position=0,Mandatory=$true,HelpMessage="This need to be a clusterobject",
    ValueFromPipeline=$True)]
    $Cluster,
	[int]$numberHosts = 0,
    [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
		if($numberHosts -eq 0){
        	$spec.dasConfig.admissionControlPolicy.cpuFailoverResourcesPercent = $percentCPU
        	$spec.dasConfig.admissionControlPolicy.memoryFailoverResourcesPercent = $percentMem
 		}else{
			$spec.dasConfig.admissionControlPolicy.cpuFailoverResourcesPercent = 100/$numberHosts
        	$spec.dasConfig.admissionControlPolicy.memoryFailoverResourcesPercent = 100/$numberHosts
		}
        $Cluster = Get-View $Cluster
        $Cluster.ReconfigureComputeResource_Task($spec, $true)
    }
}

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)


 

Check number of running VMs on datastores

If you are running too many VM´s on your datastores in your vSphere environment you can have some problems, this if your SAN is not VAAI compliant and can handle SCSI locking etc.

Alan Renouf has made a blog post about how to get a report about how many VM´s you have on every datastore, I have extended to only report on running VM´s as these are the interesting number..

Get-Datastore | Select Name, @{N="NumVM";E={@($_ | Get-VM | where {$_.PowerState -eq "PoweredOn"}).Count}} | Sort Name | Export-Csv -Path C:\temp\vms-datastore.csv -NoTypeInformation -UseCulture

And the report loooks like this when imported into excel

Use of PowerCLI and invoke-script to get HAL running of Windows VM´s

I was thinking of how to get an report after reading about the converter best practice from Vladan and he also has this blog post about how to change the HAL, but first i want to know what running guests are there that has the wrong HAL running. I have selected to get an report out of the running windows 2003 ( windows 2008 and later has an uniform HAL that is the same for one or more cpu´s).

I have made an little script somewhat alike to Alan Renouf´s to get the HAL of the VM guest running in your environment, the difference in my approach is that i use invoke-script and get the info from inside the VM, as i have a case where the VMs are isolated and i can not get the wmi info from the network, Arnim Van Lieshout has made one blog post about  running wmi queries this way.

 

# Check the HAL in the VM with invoke-VMScript
#
# Niklas Akerlund / RTS

$VMs = Get-VM | Where {$_.Guest.OSFullName -eq "Microsoft Windows Server 2003 Standard (32-bit)" -and $_.PowerState -eq "PoweredOn"}
$guestcred = get-Credential
$report = @()

foreach ($VM in $VMs){
    $HAL = Invoke-VMScript "wmic path Win32_PnPEntity WHERE ""DeviceID='ROOT\\ACPI_HAL\\0000'"" GET NAME /VALUE" -VM $VM -GuestCredential $guestcred -scripttype "bat"
	$HAL = $HAL.Split('=')[1]
    $data = New-Object PSObject -property @{
        VMName=$VM.Name
        vCPUs=$VM.NumCPU
        HAL=$HAL.Trim('')
    }
    $report +=$data
}

$report | Export-Csv -path c:\temp\hal4.csv -NoTypeInformation -UseCulture

And the simple report looks like this:

 

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: