Archive

Archive for the ‘Powershell’ Category

Using regular expressions to parse files in PowerShell

March 28, 2011 Leave a comment

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

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

Function: Get-Documentation

March 18, 2011 Leave a comment

Righto – As mentioned here in Get-Screenshot we can easily capture our working screen. Now more than just the novelty of having a .png file filling up a random folder…. think this concept through to automatically documenting a script / build / app install.

Function Get-Documentation ($cmd,$filename="screenshot",$fold="C:\wiki\Get-Documentation",[switch]$PAUSE,[switch]$CLS,[switch]$DISPLAY)
{
	# note use of script block $cmd =  {gf get-screenshot -de}
	# syntax Get-Documentation -cmd {gf get-screenshot -de} -PAUSE -CLS -DISPLAY

	if ($CLS) {clear}
	"`nRunning command ""$cmd""`n" | Write-Host -ForegroundColor Green
	. $cmd

	sleep 1
	if ($PAUSE) {pause}
	if ($display) {get-screenshot $filename -fold $fold  -DISPLAY} else {get-screenshot $filename -fold $fold}
}

Talking through the various param options:
$cmd………………..is the script block to execute
$filename……………name of the file, with a default of “screenshot”, but will also have datestamp appended
$fold…………………folder, default is “C:\wiki\Documentation”
[switch]$PAUSE……wait for user input before continuing
[switch]$CLS……….clear the powershell window before executing the script block
[switch]$DISPLAY….invoke the screengrab (for review purposes)

sample output from running command:
Get-Documentation {gf get-screenshot -de} SampleScreenShot -PAUSE -CLS -DISPLAY
get-screenshot

tbc.

Categories: Powershell

Function: Get-Screenshot

March 17, 2011 1 comment

Function: Get-Screenshot

Just as I was leaving for the day… From across the room came a granade:

I wish there was a way to grab a screenshot from the command line

Somewhere in the back of my mind I have a vague memory of catching this in a mail / post, and true enough, techtalkz.com had the sample I was after. I messed with it a few minutes, and refined it adding my usual variables. Then stripping out the taskbar (thanks to Shay Levi) and getting the screen size dynamically (thanks Keith Hill). The result is the Function below.

Function Get-Screenshot ($filename="PrintScreen",$ext="png",$fold="C:\temp",[switch]$DISPLAY,[switch]$FULL)
{
	$date = Get-Date -Format yyyyMMdd_HHmm
	if (!(Test-Path $fold)) {md $fold}
	$repfile = "$fold\$filename`_$date.$ext"
	
	if ($FULL) 
	{
		$screen = gwmi Win32_DesktopMonitor
		[int32]$height = $screen[0].screenHeight
		[int32]$width = $screen[0].screenWidth
	} else {
		$workarea = [System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
		[int32]$height = [System.Windows.SystemParameters]::FullPrimaryScreenHeight
		[int32]$width = [System.Windows.SystemParameters]::FullPrimaryScreenWidth
	}
			
	[reflection.assembly]::LoadWithPartialName("System.Drawing") > $null
	$Bitmap = New-Object System.Drawing.Bitmap $width,$height
	$Size = New-object System.Drawing.Size $width,$height
	$FromImage = [System.Drawing.Graphics]::FromImage($Bitmap)
	$FromImage.copyfromscreen(0,0,0,0, $Size,([System.Drawing.CopyPixelOperation]::SourceCopy))
	$Bitmap.Save($repfile,([system.drawing.imaging.imageformat]::png)); # the acceptable values: png, jpeg, bmp, gif...
	
	if ($DISPLAY) {
		Invoke-Item $repfile
	} else {
		return "PrintScreen saved to: $repfile"
	}
}

Notes:

  • The $DISPLAY switch is a simple way to flag the output to screen
  • The mix of WMI and .NET is here to show that both can be used
  • There’s a few ideas spawned off from here, tbc

    Categories: Powershell

    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.

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

    March 8, 2011 Leave a comment

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

    Categories: Powershell, Scripting, VMWare

    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