Archive

Posts Tagged ‘Script of the day’

Using regular expressions to parse files in PowerShell – Script of the Day

March 28th, 2011 No comments

How often do you find yourself needing to identify a string in a file somewhere.

For example, you have a log file, or a config file and you know it contains an IP address, but you do not want to manually trawl through this file (or even worse . . these files)

Regular expressions are pretty handy, as you can use them to identify (and edit) strings of text pretty simply. thik of it as a Replace function on steroids.

Here are some examples:
http://www.regular-expressions.info/quickstart.html

Anyway, back to our original question – we’d like to find an IP address in a file.

The first thing of course is to get hold of the text in our file – we’ll drop it into an array, so we can do a line by line comparison..

 
$var = @(Get-Content .\Access*.log)

Next, we need to create a Regex string pattern, which we will use as reference when we query out text – there are plenty examples available on the web (e.g. http://www.regular-expressions.info/examples.html) – but we just need one to find IP addresses:

 
$regex = [regex] "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"

Lastly, we simply need to return a list of elements in our array, where the array matches the REGEX search string – again simple:

 
$regex.matches($var) | Select-Object -unique -property "Value"

so the full bit of code:

 
$var = @()
$var = Get-Content .\Access*.log
$regex = [regex] "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
$regex.matches($var) | `Select-Object -unique -property "Value"

Value
-----
206.13.132.82

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

March 23rd, 2011 No comments

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

Powershell – (Get-virtual).info – RSS Capture with Powershell through a proxy

March 22nd, 2011 No comments

a little geekery . . .

As the blog is called ‘Get-Virtual’  . .I figure we may as well have a cmdlet / function to allow us to actually get the content of get-virtual . . . so I quickly put together an RSS reader to capture the RSS feed for the blog and export it to a GridView

The function is not entirely useless, as it will demonstrate 6 new things
1 – Using Powershell to get info from the web
2 – Accessing the web from Powershell through a proxy
3 – Authenticating through the proxy using Powershell
4 – Reading an RSS feed using Powershell
5 – Exporting a Powershell object to a GridView
6 – Reading / Parsing XML using Powershell

Exporting the info captured into a GridView makes it easy to filter the data and view what the latest posts etc are.

I have hashed a little ‘replace’ code out in the script as several of the older Get-virtual.info posts had special characters that get misinterpreted . . so you could remove the hashes to clean this up if you really wanted.

here is the code:

function get-virtual 
	([string]$proxyserver = (Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"  | %{$_.ProxyServer}),
	[switch]$auth, 
	$proxycred)
	{	$proxy = new-object System.Net.WebProxy $proxyserver
		If ($auth)
			{if (!$proxycred) 
			{$proxycred = Get-Credential}
			$proxy.credentials = $proxycred.GetNetworkCredential()
			}
		$WebClient = New-Object System.Net.WebClient
		$webclient.proxy = $proxy
		$WebClient = Get-WebClient
        $blog =  1$Webclient.DownloadString("http://feeds.feedburner.com/PsCget-virtualinfo")
		$obj = @() # New-Object psobject
		for ($i=1; $i -le $blog.rss.channel.item.count; $i = $i + 1)
		{
		$rep = "" | select "Title", "Link", "Creator", "Date"
		$rep."Title" = ($blog.rss.channel.item[$i].title)#.toString().replace("`â`€`“","-").replace("`â`€`˜","`"").replace("`â`€`™","`"")
		$rep."Link" = ($blog.rss.channel.item[$i].Link)#.toString().replace("`%e2`%80`%98","`'").replace("`%e2`%80`%99","`'").replace("","")
		$rep."Creator" = $blog.rss.channel.item[$i].Creator
		$rep."Date" = $blog.rss.channel.item[$i].PubDate
		$Obj += $Rep
		}
		$obj | Out-GridView -Title "Get-Virtual.info Fat(ish) client"
		return $obj
	}

	

and here is the result:

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

March 11th, 2011 No comments

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 vendor 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.

Copying data to/from a VM datastore using PowerCli – Script of the Day

March 8th, 2011 No comments

Ever needed to copy data from your local machine to a VMware datastore . .and not felt like messing around with winSCP / FastSCP, the Datastore browser etc?

PowerCli / PowerShell lets you create a new PSProvider item for your datastore, which in turn lets you copy data using the normal Copy-Item syntax. (though using Copy-DatastoreItem instead)

So, if you have a Datastore in your ESX environment called ‘Datatstore1’ and you want to copy an iso from your C:\ISO directory, it would be as simple as

PS: >new-PSDrive -location (get-datastore 7523_local) -name myds -PSProvider VimDatastore -Root '\'
Name           Used (GB)     Free (GB) Provider      Root                                               CurrentLocation
----           ---------     --------- --------      ----                                               ---------------
myds                                   VimDatastore  \lonlab001@443\Prod\7523_local
PS: >Copy-DatastoreItem -item C:\ISO\install.iso -Destination myds:\ISOS\Myiso.iso -force

Also, as the drive is now a normal PSProvider path, normal commands like get-childitem work like they do on a local drive

PS: >gci myds:
   Datastore path: [7523_local]

            LastWriteTime            Type       Length Name
            -------------            ----       ------ ----
     21/04/2010     07:40          Folder              esxconsole-4bcdbd...
     08/03/2011     16:31          Folder              ISOS

Genius,

Of course this gives you full access to all of the datat on your datastores directly form a PowerCli session, so you can run any sort of reports / inventories etc that you may need. Happy days . .

Script of the Day – Scripted start of Virtual Center (and supporting servers) when hosted as a VM

February 25th, 2011 No comments

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.


$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

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)

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
 }
}

And if you are feeling really flash, you could get each VM start, then monitor that VM for a particular services on 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.

# 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

so all you now need to do is keep a copy of the above script and make sure the few fields in the first few rows remain up to date with your ESX hostnames and the Servers / Services that you require to run your VC.

It is kind of a vApp in a script . .
have a great weekend

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

February 24th, 2011 No comments

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,@{N="Build";E={$_.Config.Product.FullName}} | sort build,name

Script of the day – testing if 2 IP addresses are on the same subnet

February 21st, 2011 No comments

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

Script of the Day – quick and easy VMware Powershell scripts

February 16th, 2011 No comments

Today’s script of the day is more a collection of scripts (or rather an easy way of generating a bunch of scripts)

Over at the VMware labs (http://labs.vmware.com/) they have released an awesome tool (in Alpha at the moment) that interecepts instructions sent to your Virtual Center and in the background generates PowerCli (or javascript or C# or Soap) code for you. Read more…

Script of the Day – remove duplicate lines in a CSV file

February 11th, 2011 No comments

Friday today, so we’ll keep it short and sweet.

Someone dropped me a CSV file recently and asked if there was a quick way to remove duplicates.

Easy . .

Sample source file:

Script:

$dest = @()
foreach ($row in (import-csv .\duplicates.csv)){if (!($dest -match $row)){$dest += $row}};
$dest

CDB_Cab                                   Computername                              FindIt_Cab
-------                                   ------------                              ----------
LAB SL07                               LAB9252                               Decommissioned
LAB SL07                               LAB9301                               SL 07
LAB SL07                               LAB9309                               SL 07
LAB SL07                               LAB9304                               SL 07
LAB SL07                               LAB9310                               SL 07
LAB SL07                               LAB9311                               SL 07
LAB SL07                               LAB9312                               SL 07

Pretty basic stuff, but he was getting caught up in the portion of the script that matches a row to a table.
thing to remember is that

if (!($dest -match $row))

is not the same as

if ($dest -notmatch $row)

We fixed that, but afterwards I showed him that there was perhaps a quicker, easier way:

&lt;/pre&gt;
gc .\duplicates.csv | Get-Unique
&quot;CDB_Cab&quot;,&quot;Computername&quot;,&quot;FindIt_Cab&quot;
&quot;LONDON SL07&quot;,&quot;loncmss9252&quot;,&quot;Decommissioned&quot;
&quot;LONDON SL07&quot;,&quot;loncmss9301&quot;,&quot;SL 07&quot;
&quot;LONDON SL07&quot;,&quot;loncmss9309&quot;,&quot;SL 07&quot;
&quot;LONDON SL07&quot;,&quot;loncmss9304&quot;,&quot;SL 07&quot;
&quot;LONDON SL07&quot;,&quot;loncmss9310&quot;,&quot;SL 07&quot;
&quot;LONDON SL07&quot;,&quot;loncmss9311&quot;,&quot;SL 07&quot;
&quot;LONDON SL07&quot;,&quot;loncmss9312&quot;,&quot;SL 07&quot;
if (!($dest -match $row))

Just depends on the format of your file.

Interestingly, you’d assume that if ‘get-unique’ worked on the import using gc, it would work on an import using import-csv, but alas:

&lt;/pre&gt;
PS:9 &gt;import-csv .\duplicates.csv | get-Unique

CDB_Cab                                   Computername                              FindIt_Cab
-------                                   ------------                              ----------
LONDON SL07                               loncmss9252                               Decommissioned

Powershell can be a very strange beast sometimes

[tags Script of the day, Scripting, Powershell]
[category Scripting]

Categories: Powershell Tags: