Category Archives: WMI


Quick Tip: Get All The Security Patches Installed On A Server Since A Specific Date

Recently, I needed to get a list of all the security patches I’d installed on a group of servers in the last year. It turns out that there’s a WMI class for this and it’s super easy to retrieve this info.

In the win32_quickfixengineering class, you’ll find all the security patches installed on a system. One of the properties is the InstalledOn attribute which more recent than a year ago.

If you have a list of servers to do this for, this is still really easy.

Just paste them into a here-string and execute this for each of them.


My September 2015 Scripting Puzzle Solution

If you haven’t heard, is taking the lead on organizing the PowerShell Scripting Games. There’s a new format that involves monthly puzzles. Here’s their post on September’s puzzle:

Here is my solution. The summarized instructions are: “You have a CSV with one column, “machinename”, and you need to return the friendly OS name for each. They’re a mix of machines dating back to WinXP. All have PowerShell 2.0 or better and WinRM is open between you and each host. Try to limit your usage of curly braces.”

I did this.

It’s pretty verbose but I made it that way for readability. I use a grand total of one pair of curly braces in the solution, which I hope satisfies the definition of “limited”. What I’m doing is importing the CSV, which is located wherever $InputFile is, and for each of the lines in that CSV, I am performing a task on the computer indicated. That task is to get the Win32_OperatingSystem WMI Object which contains all kinds of neat info about a system’s OS. Of the data returned, I am selecting the PSComputerName, which should equal the same value as the line in the input file (but doesn’t cost me any curly braces to return) and the Caption, which is the friendly name of the OS. I export that into $OutputFile’s location.

Fun times!


My July 2015 Scripting Puzzle Solution

If you haven’t heard, is taking the lead on organizing the PowerShell Scripting Games. There’s a new format that involves monthly puzzles. Here’s their post on July’s puzzle:

Here’s my solution. I did the extra challenges, as well except for the optional “go obscure, use aliases” challenge. I didn’t submit my solution, I just do them for funsies and to share my solutions on my blog.

The first thing I’m doing is defining an array of computer names which could have come from anywhere. Then, technically on the next line since there’s a semicolon, I have my command. It’s just a Get-WMIObject on the Win32_OperatingSystem class. The ComputerName parameter takes an array which is basically a built in ForEach-Object loop, satisfying the second and third challenge requirements. I pipe the results into a Format-Table command for easy reading but we’re not out of the woods yet. I select the PSComputername, ServicePackMajorVersion and Version properties pretty obviously but the last item, BIOSSerial looks strange.

The SerialNumber property in the Win32_OperatingSystem  WMI object is different than the SerialNumber property in the Win32_BIOS WMI object. The puzzle clearly requests the BIOS serial number, so I created a custom table column which retrieved the BIOS serial number. Technically speaking the only semicolon I use in this solution comes from the custom table command. I’m not counting the one that separates the definition of $Computers from the real command. I could have changed it to look like this, but I thought my solution was more readable.

Good puzzle! I don’t do a ton with WMI or custom tables so this was weird for me, even if it was a relatively simple puzzle.


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?


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.


Cheating To Fix Access Is Denied Error Using Get-WMIObject

I was doing a little work that involved using PowerShell to get a list of printers from several remote print servers. I figured this would be a great job for WMI and I was right. The command I used, looked like this.

I had a list of print servers that I imported into an array and looped through them but this is the important part of the code. I am simply using WMI to get some information about the logical printer objects on a given print server and exporting them to a CSV.

How boring! This isn’t a very old blog but we usually talk about more complicated things than that. Well things got weird on one print server that we’ll simply call PrintServer2. PrintServer2 threw an error instead of working nicely.

Not cool. I have Domain Admin rights… it’s a domain joined server… what do you mean access is denied? Running the command locally on the server worked, I just couldn’t do it remotely. There’s plenty of literature on trying to fix this error already but I was in a hurry so I tried the next thing that came to mind: cheat a bit and run the command locally on the server… remotely.

I didn’t do anything ground breaking, I just used invoke-command to run the command on the server instead of running the command on my local machine (to retrieve remote information).

Hah! I beat you, stupid Windows Server 2003 box that has been around since I was in junior high school and needs to be decommissioned! I got your printer information from you without having to fix any of your weird problems!

The moral of the story is that sometimes, you can cheat a little bit to accomplish your goal and avoid doing a whole bunch of terrible patches, regedits, etc. to your infrastructure.