Thursday 2 April 2015



ArsTechnica - Google's ARC now runs Android apps on Chrome OS, Windows, Mac, and Linux

I'm trying to get my head around this. ARC now allows Android apps to run inside desktop version of Google Chrome.

Did this just give Microsoft a huge boost in the app ecosystem?  Windows 10 is on the horizon and Google just made it so all Android apps will run on it, within Chrome.

Redmond must be happy.

Tuesday 17 February 2015

Submit-Slack awesomeness



I've been playing around with Slack a bit lately.  I don't see us using it internally as a collaboration tool - we're an MS shop that's more likely to go Lync - but I have been using it as a way to get Nagios notifications to my phone.  That works really well and means that I don't have to tie my personal phone to my work email so I can get notifications about important stuff but not random email.

I've also been doing a bit of custom monitoring with various PoSH scripts.  Checking backups have ran, that shadow copies are working, that sort of thing.  That got me thinking, it would be pretty cool to have those PoSH scripts also post to Slack if they come across something I should be aware of, right?  And a great excuse to have a play with RESTful web services through PoSH into the bargain :)

Slack has an integration called Incoming Webhooks that can be used for custom integrations such as this.  Long story short, you can send a JSON payload to Slack which ends up in a channel on your network.  It's very easy to setup too!

I started at Christopher Maneu's post here where he describes how to post to slack using the Invoke-RestMethod cmdlet.  Slack's Incoming Webhooks documentation has everything else you need to know to get this working.

Your finished script will look a little like the one below.  You'll need to use your own Slack network name and token.  I've also included a specified default icon to use with the post if you don't specify one with either the -IconEmoji or -IconURL parameters but that's optional.  If you don't do that, the default icon you configure in Slack for the Incoming Webhooks integration on your network will be used instead.  I've also used parameter sets to make sure that only the -IconEmoji or -IconURL options can be specified but not both.  A very useful feature!

Enjoy :)

Tuesday 20 January 2015

PowerCLI and CD Drives

We were doing a bit of housekeeping today.  We've already moved our vSphere Centre from 5.0 to 5.5 and it's time to update the hosts.  That should be straightforward; each host should be put into maintenance mode while ESXi is updated then the host is brought back into the cluster.  One possible problem though, is VM CD Drives.

I'm terrible for not managing VM CD Drives properly.  I have an annoying habit of uploading an ISO to a host's local storage and making it available to a VM.  Or, I'll stick a CD in the host and pass it through to a VM.  These are both really bad things to do in VMware.  When a VM has a CD Drive attached to an ISO stored locally on the host or to the host CD device then it cannot be vmotioned.  That means when the host goes into maintenance mode, you're going to have a problem.  The best thing to do is to check the CD drives first.  The easiest way to do it is in PowerShell with PowerCLI.



VMware PowerCLI is, according to VMware, "a Windows Powershell interface to the VMware vSphere and vCloud APIs".  This means we can perform operations from within PowerShell that we would normally do from the vSphere GUI.  Sometimes it's quicker and simpler to use the GUI but we can automate from PowerShell.  When dealing with a lot of objects, that can be a huge time saver.

Back to my original problem - finding any VMs that have a CD Drive connected.  Once you have PowerCLI running and you've connected to a vCenter (Connect-VIServer servername) We can start with:

Get-Command *CD*

Sensible place to start.  This will show us all commands that include the term CD.  This actually returns 39 commands, many of which are not relevant, but one looks very promising.  Get-CDDrive.



That's what I'm looking for.  The syntax shows me I can give it a VM name and find CD drives on that VM.


Nothing attached here.  If I pipe that command through Format-List, I can see all attributes that are available to me.


I'm interested in the Parent, which is the name of the VM that the CD Drive is connected to and the ConnectionState of the CD Drive.  Using both those parameters I can see which VM I have and the state of any CD Drive attached.  It would be really useful to see that for all VMs at once though.

Get-VM without any parameters returns an array of objects, each object being a VM in your vCenter.  


Trust me, there are VMs under there.  That means, I can pipe those results to our earlier Get-CDDrive command and add a little formatting to select the Parent and ConnectedStat properties to get a nice list!


After all that, I was worrying about nothing, everything showing as NotConnected.  Still, at least now I can upgrade with confidence!

Tuesday 23 December 2014

Splitting Strings in PowerShell

I had a need to split some strings the other day, which turned out to be a little more complicated that I was expecting.  Take this string:

$string = "this is a very nice string"

Splitting this on, say, the letter 'v' is as easy as I thought it would be.

$string.Split("v")

This is a

ery nice string

Nice.  I had to split on a string though, such as "very".

$string.Split("very")

This is a



 nic
 st

ing

Uhm, what happened there?  Well, calling the Split() method as we have done actually passes an array of characters rather than a string.  This is an important distinction as we're now splitting on 'v' or 'e' or 'r' or 'y' instead of "very".  There is a simple alternative in the -split paramter.

$string -split "very"

This is a

 nice string

That's the result I was hoping for!  Job done.

Not really.  .Split() was nagging away at me.  There must be a way to make that work?  Where do I find out if there is any way to call Split() with a string?  From MSDN.  MSDN is a great resource.  It's not the first place I would go to when I need a tutorial or I'm picking up a topic for the first time but if you have a good idea of what you're looking for and need a detailed reference guide, it's an excellent place.

Searching MSDN for "String.Split Method" gets me here.  This page lists all the overloads for the method.


This shows that there is one overload method that has a single argument and that argument must be a character array.  This explains the behaviour we saw above.  However, looking down that list, there are two overload methods that both support string arrays as separators!  Exactly what I was looking for!  

Split(String[], StringSplitOptions)
Split(String[], Int32, StringSplitOptions)

Both these overloads require the StringSplitOptions argument.  This is defined here but basically all this argument defines is whether or not we want blank entries returned in the array or left out.  The second overload also contains an Int32 argument which can be used to define the maximum number of substrings that are returned.  So if we only wanted the first substring returned after the split, we could use a "1" there.

StringSplitOptions are defined as follows:

[System.StringSplitOptions]::None
[System.StringSplitOptions]::RemoveEmptyEntries

The first option returns array elements that include blank strings, the second one removes them.  It doesn't matter which we use, but we need to use one to match the overloads that allow us to pass a string.  Speaking of which, we can't just use an argument of "very" either.  You will see that there are also overloads for the character array arguments that include the StringSplitOptions argument as well.  This means that "very" will still be treated as a character array and we'll be back to square one.  We need to define it as a string.

@("very")

will do nicely.  In the end, this means we can do this:

$string.Split(@("very"), [System.StringSplitOptions]::None)

This is a

 nice string

Yay!  Now, is that easier that using -split?  Well, no, it's not.  But I scratched an itch.



Wednesday 10 December 2014

Monitoring Shadow Copies with PowerShell




Shadow Copies are a wonderful thing.  Yup, they're not a replacement for backups but they are not meant to be.  What they are is a very quick, very simple way to restore data.

For peace of mind, I like to have a quick glance over my servers to make sure Shadow Copies are turned on for the drives I need and are creating copies on schedule.  That's the sort of regular, repetitive task that PowerShell is great for.

I'm a fan of using PowerShell with Get-WMIObject to get data back from WMI and there's a WMI class for managing Shadow Copies called, well, Win32_ShadowCopy.  Get-WMIObject Win32_ShadowCopy $servername will get you a list of all current shadow copies on that server for all drives.  If you run that against a server with no shadow copies, all you're going to get back is an "InvalidOperation" error.

There are two properties of Win32_ShadowCopy that I'm really interested in, InstallDate and VolumeName.  InstallDate holds the date the shadow copy was created and VolumeName holds the ID of the device (drive) that the shadow copy was created on.  Both these fields need a little manipulating to be useful though.

A typical InstallDate is going to look something like this.

20141201094535.549577+000.  

I'm only interested in the part before the period which is the date and time in the format yyyymmddhhmmss as a long string so we need to break this up a bit.  Something like this.

$date = [datetime]::ParseExact($copy.InstallDate.Split(".")[0], "yyyyMMddHHmmss", $null)

That looks a little complicated but it's not really.  What we're doing is creating a new variable, using [datetime] to force the type and ParseExact to turn the string into something that's usable as a datetime.  The bit after ParseExact takes the string, splits it at the period - which gives a two element array, one before and one after the period - uses [0] to select the first element of that new array and uses the string yyyyMMddHHmmss to define which parts of that string to use as each element of the datetime.  Case is important here, for example, hh signifies hours in a 12-hour clock but HH signifies them in a 24-hour clock.  If you use the wrong case, you're going to get the wrong time.  There's great info on that here.  What we're left with is a variable called $date which holds the date and time the snapshot was created.  Perfect.  How do we find the drive though?  We use the VolumeName attribute, which looks like this.

\\?\Volume{aja08123-6c44-12e2-7693-006021310173}\

That actually looks worse than what we started with for the InstallDate!  This is an identifier for the drive but it doesn't really turn us anything.  I'd much rather see a drive letter here.  Fortunately, we can turn this ID into a drive letter using another wmi class, Win32_Volume.  Get-WMIObject Win32_Volume -ComputerName $server returns a list of all volumes present on $sever.  There's a lot of info in this class but only two properties I'm interested in here, DeviceID and Name.  DeviceID holds the same value that we get back from the VolumeName attribute in Win32_ShadowCopy and Name holds the relevant drive letter.  Perfect.  Actually, there are two other attributes you can use to get the driveletter, both DriveLetter and Caption.  Any of these three will work although the actual DriveLetter parameter does not include the trailing backslash.  Take your pick.  What we can do though, is create a hashtable then go through each result returned from Win32_Volume and add both the DeviceID and Name to the hashtable.

$volumes = @{}
$allvolumes = Get-WmiObject win32_volume -ComputerName $server -Property DeviceID, Name
foreach ($v in $allvolumes)
    {
        $volumes.add($v.DeviceID, $v.Name)
    }
$volumes

This is cool.  Now to return a drive letter, we only need to use the following.

$driveletters.Item($VolumeName)

How easy is that?  We give it the VolumeName we get back from Win32_ShadowCopy and it gives us the drive letter.  So now we need to wrap all this in a loop to iterate through all the shadow copies and find the date/time they were created and which drive they were created on.  That'll look something like this.

There are *lots* of improvements to be made here.  Error checking is non-existent, the output format could be tidied up and this really should be scheduled and emailed but it's a good place to start.