Tricky PowerShell Pipeline Tricks – Playing With WMI

Here’s a quick task: Get the WMI object win32_bios for a computer. Using PowerShell, that’s really easy. You just run Get-WMIObject win32_bios. Now what if you wanted all the extended properties of the object (not just the five that it normally returns) and ONLY to return the properties that actually have a value assigned?

Well this question just got trickier. win32_bios isn’t very big so it’s an easy one to play with in this example. Let’s step through a couple commands so we know what we’re dealing with.

Well okay, there are the five properties that we knew the command returns by default. We know the extended properties are in there and we can prove it.

There’s everything! That’s a lot of blanks, though. Depending on the type of reporting you’re doing, that might not be so nice to look at. I know that I’d like to get them out. Luckily with PowerShell 4.0, it’s really easy if you use the -PipelineVariable parameter.

I’ve left out the output since it’s every property that has a value and none of the ones that have a blank. Let’s take a look at what’s actually happening here.

On Line 1, we’re running the same command we ran the last two times except we assigned a Pipeline Variable which will be named $bios (you omit the $ when assigning the name of the variable). We enter a foreach loop on Line 2.

On Line 3, we’re setting the value of $props and on Line 4, we’re writing out the part of the WMI object that contains it. The tricky thing here is how we get the value of $props. Look at how we use the Pipeline Variable and the .PSObject.Properties.Name property to identify the items with a value.

Well that’s great but what if I don’t have PowerShell 4.0 or found that example really confusing?

Don’t worry, this is pretty easy to do in earlier versions of PowerShell, too.

I started using some more shortcuts (gmi and % instead of Get-WMIObject and foreach).

In Line 1, I’m doing the same ol’ Get-WMIObject command that I did before and in down the pipe, I’m assigning the value to $wmi. I could have also done “$wmi = gmi win32_bios” but this strategy has benefits if you’re planning on scaling this script out.

In Line 2, I’m doing some tricky Select-Object work. The input for the command is $wmi, which isn’t so tricky, but the property that we’re selecting is pretty tricky. -Property takes an array, so we can put a command in there as long as it returns an array.

$wmi has a property of .Properties.Name which is an array of all the names of the properties attached to the WMI object that got returned in Line 1 (that we are using as our input). We don’t just want all the properties, though, so we need to select only the ones with a value (not null). We do that by piping the list of all the properties into a Where-Object command.

The Where-Object command has a tricky property called -FilterScript which basically acts as an implicit IF statement. If you wrote Where-Object -FilterScript {$true} then you would return every object in the pipe. If you wrote Where-Object -FilterScript {($num = $((Get-Random -Minimum 1 -Maximum 100) % 2) -eq 1)} you would get random properties because sometimes the statement will be true and sometimes the statement will be false. These are all silly items to filter on, though.

In my script, I’m filtering on if the current property the script is looking at has a value in $wmi. If the property is 0 or null and therefore doesn’t exist, $wmi.item($_) will return false and that line won’t be returned. It’s basically a test to see if there’s a string or not. Consider this example:

Because $var1 doesn’t have an actual value assigned to it, an if ($var1) will return false. That’s the same logic we are using all throughout the above code.

Tricky, right?


Quick Tip: When was an Exchange Online Protection Transport Rule Changed?

What if you have an Exchange Online Protection (EOP) transport rule that isn’t behaving the way you thought it should? I’ve been the victim of some strange inconsistencies with EOP since they tried to migrate us from Forefront Online Protection for Exchange (FOPE) in March (actually summer) of last year.

So did a transport rule get changed administratively by some cowboy admin colleague? Or is EOP conspiring against you? In EOP’s GUI, you can’t tell when a transport rule was changed last but you can if you make a remote connection to EOP using PowerShell.

You just need to know the name which you can find by running a Get-TransportRule command and looking for the one that you’re interested in. Then run this…

… which will give you the date and time that the rule was last changed.


Quick Tip: Opening An Exchange Online Protection Shell

There’s lots of big, exciting, non-blogable things happening at work this week so here’s a very quick tip.

Last week I wrote a post on a PowerShell function I threw in my profile to connect quickly to Exchange. That’s great, but what if you also want to manage Exchange Online Protection (EOP) from a PoweShell console? Well it turns out to be pretty easy.

This looks a lot like the function I showed you last week except it’s connecting to Office 365 and you need to use your Live ID instead of AD credentials.


Opening A Remote Exchange Management Shell

Here’s a function I stuck in my PowerShell profile. I found myself making lots of remote connections to my Exchange 2013 environment so I put together a quick function to create the connection for me. It’s far from perfect but it saves me time every single time I use it so check it out.

On Line 1, we’re declaring the function – no big deal. I’m naming mine “gimme-exchange” so once my profile loads, I can just type that to start the function.

On Lines 3 to 5 I’m setting a few variables. Line 3 is weird. I made an array of the different Exchange servers I had rather than going through some autodiscovery process. The script will try to open a connection to the first one, if it fails, try the second one, and so on. It’s inefficient but I don’t add and remove a lot of Exchange servers so I can get away with it since this function is just for me. Line 4 is going to be used to detect if we made a connection or not. Line 5 will prompt and store the administrative credentials that you will use to create the connection.

On Line 6, we start looping through all the servers I specified in Line 3. In a Try/Catch block, if we haven’t already made a successful connection to a previous Exchange server, we’re going to make a new connection and import it. You have to make sure you use the -configurationname item because we’re not just creating any old PSSession, Exchange is funny and we connect to it using some special parameters shown in Line 12. When we import the session on Line 13, we’re going to allow clobbering of other existing cmdlets and suppress the output of the import command.

If we run into an error anywhere in the Try block, the Catch block is setup to echo out the error message and continue looping through servers depending on how severe the error is.

That’s it! Yay, saving time.

Update: If you’re already logged in as the user you want to connect to Exchange as, you can skip the credential gathering part and run this instead.



Renewing Exchange 2013 Certificates: SHA-256 Style

I recently ran into an issue that I think is actually pretty funny. It was time to renew the publicly trusted certificate that we install on our Exchange 2013 servers that gets tied to SMTP, OWA and some other IIS services like autodiscover. Since SHA-1 is on the road to deprecation, our cert vendor pushed pretty hard to get something with a hashing algorithm of SHA-2 (or SHA-256, it’s the same thing). Sounds reasonable, right?

Well, here’s the problem. Even though Microsoft is one of many vendors who is pushing the deprecation of SHA-1, Exchange 2013 doesn’t seem to have a mechanism built into it that generates a SHA-2 cert request. Even the New-ExchangeCertificate PowerShell command doesn’t have a way to change which hashing algorithm is used. Windows 2008 R2 and later support SHA-2 but at the time of writing this article, Exchange 2013 doesn’t have a way to generate such a request.

There are other ways to generate cert requests, though. Since Windows Server 2012 R2 supports SHA-2, and even SHA-2 certificates, perhaps – I thought – the Certificates MMC can be used to generate such a cert. I was right, it can be used to generate a SHA-2 cert. “Great!” I can hear you exclaim, “Now give me the steps on how to do it!” Not so fast. I’m not going to put the full steps to generating a SHA-2 certificate using the Windows Certificates MMC here because of a problem.

To generate a SHA-2 request using the Certificates MMC, you add the Certificates for Local Computer snapin to MMC.exe, right click on the Personal certificate store and generate a new request. When asked to choose a request template, you are offered two choices: Legacy or CNG. Legacy doesn’t support changing your hashing algorithm and therefore only generates SHA-1 requests. CNG it is, then! I continued on, generated my SHA-2 cert request, got it approved and took the certificate from my provider and went to test it. Almost everything worked except I couldn’t log into OWA or ECP. Why not? Because Exchange 2013 stores lots of info in encrypted cookies about you when you log into these services and it can’t use a CNG certificate to decrypt the data. Whenever I logged in, I would be immediately redirected back to the login page as if nothing had happened because the encrypted cookie with all my info (like “you logged in as username“) couldn’t be decrypted since Exchange 2013 can’t use the CNG provider in Windows 2012 R2.

How else can you generate a SHA-2 certificate request, then? The Windows MMC requires you to associate it with a CNG provider that Exchange 2013 can’t use and Exchange 2013 doesn’t allow you to create a SHA-2 request? This isn’t looking good, SHA-1 is being deprecated and I have to renew my certificate!

The answer turns out to be unbelievably easy. I couldn’t believe this worked.

Here’s the answer. Here’s how to renew your Exchange 2013 public certificate with a SHA-2 hashing algorithm…

  1. Use the New-ExchangeCertificate PowerShell command to generate a cert request that is perfect for your needs, minus the fact that it will request a SHA-1 hashing algorithm.
  2. Submit the request to your public certificate provider but indicate that it is a SHA-2 certificate request. Your provider should have an option to indicate what sort of certificate your request is set up for. Make sure you say SHA-2.
  3. Your provider will give you back a SHA-2 certificate that is not associated with a CNG provider that will work with your Exchange 2013 environment for all SMTP, IIS, IMAP and POP services you wish to bind it to.

That’s right, the answer is to generate a SHA-1 request anyway and tell your provider that it’s SHA-2.

Credit to Microsoft Premier Support for figuring this out.


SMA Runbook Daily Report On SMA Runbook Failures

The sad reality of using Service Management Automation is that it can be a little iffy in the stability department. That being so, I decided to put together an SMA runbook that would report on all the other SMA runbook failures of the last 24 hours. Yes, I realize the irony in using SMA to report on its own runbook failures. One must have faith in one’s infrastructure and this particular runbook.

First things first, I need to declare my runbook/workflow and get the stored variable asset which holds my SMTP server.

Easy! Now the SMA PowerShell cmdlets work best in actual PowerShell, not in workflows so I’m going to cheat and use an inlinescript block to hold pretty much everything else. Now before we get to the good stuff, I’m going to knock out the easy task of setting up my try and catch blocks as well as my function that sends email.

Most of this is pretty straight forward. I’m going to put some stuff in a try block and email it out if it works. If I catch an error, I’m going to email a notification that something screwed up in the try block.

Now the meat and potatoes. Something actually worth making a blog entry for! We need to build the content of the email we’re sending in the try block. Right now it’s just text that says “better put something in here” and it’s right. We better.

Wow that got a little ugly really quickly. What you need to keep in mind is that a lot of this ugliness is styling to make the email report pretty. That’s a little counter-intuitive but, hey, welcome to scripting as a working sysadmin. Let’s break it down line by line.

Line 8 is getting an array of failed jobs in the last day. It’s a big pipeline which:

  • Gets the jobs
  • Where there’s a JobException property on the job
  • Where the endtime is greater than within the last day

In Line 9 t0 20, if there are jobs in the array of failed jobs within the last day, we have to build a report. Line 11 initializes the HTML that will become the body of our report by putting in a table, its column headers and styling it.

Starting on Line 12, for each failed job in the array of failed jobs, we’re adding a row to our HTML table with the start time, end time, the runbook ID, the runbook name and the exception that was thrown.

On lines 19 to 21 we finish off our HTML for the body of our email. Then we use the code we already wrote to send it to us.

Boom. Pretty nice report on failed jobs. Hopefully you never see one in your inbox, otherwise you’re going to have some troubleshooting to do.


Quick Tip: Run An SMA Runbook At A Specific Date/Time

Happy New Year’s Eve! Here’s a quick tip just before New Year’s.

I recently answered a question on Technet about scheduling SMA runbooks. It’s no secret that the scheduling engine in Service Management Automation leaves something to be desired. Here’s how I like to use PowerShell to get specific about when an SMA runbook is going to be triggered.

You’ll need the SMA PowerShell tools installed and imported for this to work.

Line 1 is easy, it’s just a variable for a datetime object and it’s going to represent the time you want to trigger the runbook. Line 2 is a variable for what the name of the SMA schedule asset will be. I like to add something dynamic here to avoid naming collisions.

Now the interesting parts. On Line 3, we’re creating an SMA schedule asset using set-smaschedule. It’s going to be named our Line 2 variable, it’s going to be a onetimeschedule (instead of recurring), start at our start time (Line 1) and expire three hours after the start time. On Line 4, I’m triggering the runbook with start-smarunbook and specifying the schedule we created on Line 3. I’m also passing parameters in a hash table.

You’re done! The only hiccup with this I’ve seen is if one of your parameters for your runbook is a hashtable. Matthew at sysjam.wordpress.com covered this weird situation in a blog post very recently.


Quick Tip: Get All SMA Runbook Schedules That Will Run Between Now And Then

I wanted to do some maintenance on my SMA runbook servers but couldn’t remember which jobs were going to run in the next 12 hours (if any). Luckily there’s a quick way of getting that information! This work assumes that you have the SMA tools installed and that you ran the below command or have it as part of your profile.


This isn’t a very crazy command. “your-server” is the server where you have the SMA management items installed, not an individual runbook server.

You’re getting all the SMA schedules from your SMA instance and filtering for items whose next run is after “now” and before “now plus 12 hours”. You can change the get-date related items easily to suit your needs. For instance, what ran last night? What will run tomorrow? What ran on October 31?



SMA Runbooks And UTC Time

I don’t know about you but I hate dealing with systems that use UTC time. I have SMA runbooks that work with Exchange 2013, Exchange Online Protection and other services that annoyingly return results in UTC instead of my local timezone. I wrote an SMA runbook that can be called from other SMA runbooks to do the conversion for me.

It’s pretty simple runbook! It has one mandatory parameter $UTCTime which, as the name would suggest, is the UTC time that you want to convert to your local time.

Line 7 gets the local timezone by performing a WMI query. Line 8 uses the [SystemTimeZoneInfo]::FindSystemTimeZoneByID to convert the value returned from the WMI query into a timezone. Line 9 performs the actual conversion from whatever the UTC time is to the timezone determined in line 8.

This whole thing assumes that the time and timezone are set correctly on your SMA runbook servers.


Print Everything In A Folder To A Specific Printer

For one reason or another, I found myself in a situation this week where I needed to print all the contents of a directory on an hourly basis. Not only did I need to print the contents, I needed the jobs to go to a specific printer, too.

SMA runbooks to the rescue! I wrote my solution in PowerShell and stuck it in an inlinescript block in my runbook that I invoked on a print server.

First, I needed to get everything in the directory and print it. I originally looked at using Out-Printer but I have images, PDFs, all kinds of non-plaintext files. I needed another solution and it was this:

Foreach file in this directory, we’re starting a process on the file that prints it. It will effectively open the file in whatever the default application is, render it and print it to your default printer. Great! Except what if I don’t want to print to the default printer? The Start-Process cmdlet doesn’t seem to lend itself to that very well. As usual, I had to cheat.

Since we’re printing to the default printer, why don’t we just change the default? Well, because maybe the default printer (that we don’t want to print to) is default for a reason. So let’s change the default printer and change it back after.

Line 1 gets the name of the default printer. Line 2 sets the default printer to My Desired Printer which is presumably the name of a valid printer on the server. Line 4 sets the default back to whatever the original default was and we already know what line 3 does. Obviously, this is a solution that works in my specific environment that can tolerate a brief interruption to which printer is default.

The rest was easy. I setup a new SMA runbook, invoked the above script on my print server (in an inlinescript block) and scheduled it to run hourly.