Archive

Posts Tagged ‘Script of the day’

Finding VMs with disks on multiple different datastores – Script of the Day

March 23, 2011 Leave a comment

I was looking at a VM on one of our hosts and noticed the rather odd configuration showed that the VM had 2 disks provisioned (not unusual), and that the 2 disks had been presented on different storage (very unusual for non clustered VMs in our environment)

I figured, the easiest way to identify all of the VMs that are using VMDKs on multiple different datastores was PowerCli.

The result – just a one liner.

PS:7 >get-vm | ?{$_.DatastoreIdList.count -gt 1}

Name                 PowerState Num CPUs Memory (MB)
----                 ---------- -------- -----------
labserver001     PoweredOn  1        8192
labserver 17a         PoweredOn  2        1280
labserver21        PoweredOn  1        8192
labserver17b         PoweredOn  2        1152
Advertisements

Identifying SAN disk usage using WMI – Powershell – Script of the Day

March 11, 2011 Leave a comment

So I was commissioned with identifying the amount of actual disk space used by a bunch of Microsoft Servers that were attached to various SANs on our network. Unfortunately, despite us having a rather expensive vendoir supplied product for doing this reporting, the vendor product(s) is/are dependent on agents running on the servers to actually report back what the performance stats look like.

I fugured that the information must be accessible via WMI so I set about trying to identify something that identified disks as being remote / SAN attached.

I had a crack using several different WMI classes, thinking that I may need to tie the results from a hardware based query to identify physical disks, against the results of something like the win32_logicaldisk class – but was drawing a blank.

to identify similarities / differences betwen hosts I decided to just spit our the results of the Disk / Volume classes for a handful of hosts

I tried the following classes:
win32_logicaldisk
win32_DiskDrive
Win32_Volume

After interrogating a few hosts (some SAN attached, some not . I noticed a similarity in the device IDs returned in the following class win32_Volume.

you see all of our servers are set to have local disk for the C: and CDROM for the Z: (so I had a control group)
Executing the following however . . against a LONG list of server seemed to always return C: and Z: with device IDs in the format:

\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-806e6f6e6963}\

Now I have looked around for an explanation as to why all the Local Disks and Local CD Roms appear with tat number at the end, but I can find no confirmation – but I figured I’d simply create a PowerShell script using this snippet of info to generate the report I require.

First of all, the wmi query that I would need :
gwmi -ComputerName <servername> -class win32_volume | select deviceid, driveletter

Now all I need to do is contruct a method to repeat the above and churn out a nice Excel style report to show disk utilisation . .

Here is the result



Function get-sandisks ([string]$InputFilename,[string]$OutputFilename)
{
$servers = gc $InputFilename

$MyObj = @()
# Cycle through the servers in the file
foreach ($server in $servers)
{Write-Host $server -ForegroundColor Green
# first test if we can ping the server (quicker than trying WMI straight away)
If (Test-Connection $server -Count 1)
{
if (Get-WmiObject -ComputerName $server Win32_LogicalDisk -ea 0)
{$disks = gwmi -ComputerName $server -class win32_volume | ?{$_.driveletter} #| ?{$_.deviceid -notmatch "806e6f6e6963"} #| select deviceid, driveletter
If ($disks)
{foreach ($disk in $disks)
{
$rep = "" | select "Server Name", "Drive Letter", "Disk Space", "Used Space", "Free Space", "DeviceID", "Type"
$Rep."Server Name" = $server
$Rep."Drive Letter" = $disk."driveletter"
$Rep."Disk Space" = $disk | %{$_.Capacity}
$Rep."Free Space" = $disk | %{$_.Freespace}
$Rep."Used Space" = $Rep."Disk Space" - $Rep."Free Space"
If ($disk.deviceid -notmatch "806e6f6e6963"){$rep.Type = "SAN"}
Else
{$Rep.Type = "Local"}
$Rep."DeviceID" = $disk | %{$_.deviceID}
Write-Host $rep
$MyObj += $Rep
$rep = $null
}
}
}
Else
{
$rep = "" | select "Server Name", "Drive Letter", "Disk Space", "Used Space", "Free Space", "DeviceID", "Type"
$Rep."Server Name" = $server
$Rep."Drive Letter" = "WMI"
Write-Host $rep "WMI" -ForegroundColor Yellow
$MyObj += $Rep
$rep = $null
}
}
Else
{              $rep = "" | select "Server Name", "Drive Letter", "Disk Space", "Used Space", "Free Space", "DeviceID", "Type"
$Rep."Server Name" = $server
$Rep."Drive Letter" = "PING"
Write-Host $rep "PING" -ForegroundColor Yellow
$MyObj += $Rep
$rep = $null
}
}
$MyObj | sort | Export-Csv -Path $OutputFilename -NoTypeInformation
}

While I appreciate that this is  not 100% accurate, I simply wanted to report on space that is in use by the SAN and is no local disk, so the result is fit for my purpose.

Script of the Day – Scripted start of Virtual Center when hosted as a VM

February 25, 2011 Leave a comment

There are many threads on the VM communities, debating whether it is better to run a VC on a physical host, or a VMWare host.

My answer is always that running it as a VM is better, but the arguement always comes back that if I have catastrophic faiilure and don’t know where my VC last lived . . I will be in trouble.

Of course, plan a is to simply set the restart policy on the VM to start with the host, but people tell me they have had mixed results with this approach.

The alternative is a quick PowerCli script that quickly connects to each ESX host in the cluster, checks if it owns the VM, then starts the VM.

</code>

$vCenters = "ESXHost1", "ESXhost2", "ESXHost3"
$VCServer = "VCServer"
$userName = "username"
$passwd = Read-Host ("Password for " + $userName) -AsSecureString:$true
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $userName,$passwd

<code>

One catch to be aware of though is that if you are using AD for DNS and all AD servers are VMs, you will be unable to resolve the ESX host names for the script to work, so you’ll need to specify IP addresses to the ESX hosts.

You do not however need to specify the DNS server IP for the VM, as the script look s as VM Names and

You could extend the above script then to start a series of VMs with a set wait time between VMs (e.g. start the DC for DNS etc, then start the SQL server, then start the VC, wait 60 seconds between each start)

</code>
<pre>Disconnect-VIServer * -Confirm:$false
$vCenters = "10.1.1.1", "10.1.1.2", "10.1.1.3"
$vms = "DNSServer", "SQLServer", "VCServerName"
$userName = "root"
$passwd = Read-Host ("Password for " + $userName) -AsSecureString:$true
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $userName,$passwd
# time to wait before starting next VM
$waittime = 60

Foreach ($vm in $vms){
 ForEach ($vCenter in $vCenters) {
 connect-VIServer -Server $vCenter -Credential $cred
 If (get-vm $VCServer -ea 0)
 {
 Start-VM $vm
 Write-Host "VM $VM Starting on $vCenter" -ForegroundColor Green
 Write-Host "Sleeping for $waittime to allow $vm to start up"
 sleep $waittime
 }
 disconnect-VIserver -confirm:$false
 }
}</pre>
<code>

And if you are feeling really flash, you could get each VM start, then monitor that VM for a particular services on thon that VM to run, before starting the next VM (if you have relevant access rights etc)

Prime example here is where I need a VM running my AD/DNS to start, before I can start the SQL server. Then, I want te SQL server to start and the service running, before I can start the Virtual Center.

</code>
# remove any VI connections that you may create in your PS Profile
Disconnect-VIServer * -Confirm:$false
# List of ESX hosts (by IP here as we are assuming DNS lives on a VM)
$ESXHosts = "10.1.1.1", "10.1.1.2", "10.1.1.3"
# 2 dimensional array, each row reflecting the VM to start and the service that I need to monitor
$vms = ("ADDNSServerName", "DNS"), ("SQLServerName","MSSQLSERVER"),("VCServerName","vpxd")
$userName = "root"
$passwd = Read-Host ("Password for " + $userName) -AsSecureString:$true
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $userName,$passwd

# Connect to all ESX hosts in array $ESXHosts
ForEach ($ESXHost in $ESXHosts) {connect-VIServer -Server $ESXHost -Credential $cred}
Foreach ($vm in $vms){
 Write-Host "Searching for $vm[0]" -ForegroundColor Blue
 ForEach ($ESXHost in $ESXHosts) {
 If (get-vm -Name $vm[0] -server $ESXHost -ea 0)
 {
 Start-VM -VM $vm[0] -Server $ESXHost
 Write-Host "VM $VM Starting on $ESXHost" -ForegroundColor Green
 $i = 0
 $running = "no"
 do {$running = Get-Service -ComputerName $vm[0] -Name $vm[1] -ea 0 | % {$_.status}; sleep 1; $i++; Write-Host "Waiting for $vm[1] service to start on $vm[0]- $i seconds elapsed" -ForegroundColor Yellow}
 while ($running -ne "Running")
 Write-Host "$vm[1] service started on $vm[0]" -ForegroundColor Green
 }
 }
}
Write-Host "VC should now be up and running" -ForegroundColor Red

Function : Get-Function

February 24, 2011 Leave a comment

Here’s a time saver… well a quick function that has saved me a whole herd of time when trying to find a function that has been “forgotten”

Function Get-Function ($searchterm,[switch]$DEFINITION)
{
	$functions = @(Get-Command "*$searchterm*" -CommandType "Function")
	"`nFunctions matching ""$searchterm""`n" | Write-Host -ForegroundColor Green
	if (!$DEFINITION) {$functions | %{$_.name}} else {$functions | %{"`n$($_.name)" | Write-Host -f Yellow ;$_.definition}}
}

sal gf Get-Function

the last line is setting an alias “gf”
– so let’s try this on something familiar… we had a function searching for apps…

Command: gf apps
Output:
Functions matching “apps”
Get-Apps
Get-CoreApps
Get-InstalledApps
Set-InfsApps

Ahh – “installed apps”, let’s try that one again, and get Details this time:

Command: gf installedapps -de
Output:
Functions matching “installedapps”
Get-InstalledApps
param (
$computername=$env:computername,
$regkey_x64 = “SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall”,
$regkey_x86 = “SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall”,
[switch]$DISPLAY,
[switch]$EXCLUDE32,
[switch]$EXCLUDE64
)
$bits_to_search = @()
if (!$EXCLUDE32){$bits_to_search += “x86”}
if (!$EXCLUDE64){$bits_to_search += “x64”}

$apps = @()
foreach ($bit in $bits_to_search)
{
$regkey = Get-Variable “regkey_$bit” | %{$_.value}
if (Test-Path HKLM:$regkey)
{
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(“LocalMachine”,$computername)
$key = $reg.OpenSubKey($regkey)
$SubKeys = $key.GetSubKeyNames() | %{$key.OpenSubKey($_)}

foreach ($subkey in $subkeys)
{
$app = $subkey | select @{N=”Bit”;E={$bit}},`
@{N=”Name”;E={$_.getvalue(‘DisplayName’)}},`
@{N=”Version”;E={$_.GetValue(‘DisplayVersion’)}},`
@{N=”Publisher”;E={$_.GetValue(‘Publisher’)}}
if ($app.name) {$apps += $app}
}
}
$key.Close()
$reg.Close()
}

# Remove duplicates, but annoyingly names like of “C++” error when using “-match”
$unique_apps = @()
foreach ($app in $apps)
{
if (!($unique_apps | ?{$_.Bit -eq $app.bit} | ?{$_.name -eq $app.name} |
?{$_.version -eq $app.version})) {$unique_apps += $app}
}

return $unique_apps | sort bit,name,version

Script of the day – Powercli one liner to get ESX host versions

February 24, 2011 Leave a comment

So I was looking at an ESX estate that is managed by someone else and was hoping to do a few ‘Get-EsxCli’ queries.
Of course Get-EsxCli only works properly from 4u2, so I needed to find a host that was patched up to date.

The easy way? PowerCli of course.

get-view -ViewType HostSystem -Property Name,Config.Product | select Name,{$_.Config.Product.FullName}

Of course, Craig likes it to be a little prettier, so suggested a quick tidy up:

get-view -ViewType HostSystem -Property Name,Config.Product | select Name,@{N=&quot;Build&quot;;E={$_.Config.Product.FullName}} | sort build,name

Comparing IPs using Powershell – Script of the Day

February 21, 2011 Leave a comment

Ever needed to script around IP addressing issues on hosts and needed to determine whether 2 hosts are in fact on the same subnet or not?

Try the following Function


Function Compare-Subnets {
param (
[parameter(Mandatory=$true)]
[Net.IPAddress]
$ip1,

[parameter(Mandatory=$true)]
[Net.IPAddress]
$ip2,

[parameter()]
[alias("SubnetMask")]
[Net.IPAddress]
$mask ="255.255.255.0"
)

if (($ip1.address -band $mask.address) -eq ($ip2.address -band $mask.address)) {$true}
else {$false}

}

The code simply does a binary comparison of the 2 IP addresses.

– much like you used to do when you first did TCPIP subnetting (some of you may remember this)

To use it, the syntax is simply as follows

Compare-Subnets -IP1 $IP1 -IP2 $IP2 -mask $SubnetMask

and the return is simply a boolean True or False. – Simple

e.g.PS:115 >Compare-Subnets -ip1 10.2.208.151 -ip2 10.2.208.251 -mask 255.255.255.240
False

Function : Get-InstalledApps

February 16, 2011 Leave a comment

So there I was happily using

gwmi win32_product 

to get me a list of installed applications, but this is slow, and often we just want a quick verification that an application is installed or not.

Thanks to powershell.com and their “PowerTip of the Day” I was reminded about hitting the registry for this information…
So, inspired by their code, I hacked together a quick function to do this.

Note the removal of duplicates was first done as per the concept here Script of the Day – remove duplicate lines in a CSV file but there were a few entries with special characters in their names, eg “C++” that don’t play nicely with “-match” so this was recoded comparing each entry of the object. This is going to be slower, but still solves the “Get Installed Applications” faster than the WMI route!

Function Get-InstalledApps 
{
	param (
		$computername=$env:computername,
		$regkey_x86 = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
		$regkey_x64 = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
		[switch]$DISPLAY
		)

	$apps = @()	
	foreach ($bit in "x86","x64")
	{	
		$regkey = Get-Variable "regkey_$bit" | %{$_.value}
		if (Test-Path HKLM:$regkey)
		{
			$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computername)
			$key = $reg.OpenSubKey($regkey)
			$SubKeys = $key.GetSubKeyNames() | %{$key.OpenSubKey($_)}
	
			foreach ($subkey in $subkeys)
			{
				$app = $subkey | select @{N="Bit";E={$bit}},`
										@{N="Name";E={$_.getvalue('DisplayName')}},`
										@{N="Version";E={$_.GetValue('DisplayVersion')}},`
										@{N="Publisher";E={$_.GetValue('Publisher')}}
				if ($app.name) {$apps += $app}
				}
			}
		    $key.Close()
		    $reg.Close()
		}
   
	# Remove duplicates, but annoyingly names like of "C++" error when using "-match"
	$unique_apps = @()
	foreach ($app in $apps) 
	{
		if (!($unique_apps | ?{$_.Bit -eq $app.bit} | ?{$_.name -eq $app.name} | 
		?{$_.version -eq $app.version})) {$unique_apps += $app}
	}

return $unique_apps | sort bit,name,version
}