Hey, Scripting Guy! How can I determine the remaining operating system grace period for a computer running Windows Vista?
— LD
Hey, LD. You know, a number of years ago the Scripting Guy who writes this column came outside after work only to discover that his car wouldn’t start. At that time he was working as a sports writer, which occasionally meant working very late at night. This, of course, happened to be one of those occasions: it was 2 o’clock in the morning, no one else was around, and his car wouldn’t start.
To make matters worse, the Scripting Guy who writes this column knows even less about cars than he does about scripting. Not knowing what else to do he opened the hood and took a peek inside. Everything looked fine. although, to tell you the truth, unless something was on fire he wouldn’t know the difference anyway. And even then he might have assumed, “Well, that thing is probably supposed to be on fire.”
Feeling the need to do something the Scripting Guy jiggled a few wires, closed the hood, and tried starting the car again. Much to his surprise, the car started right up. Not only did he make it home safe and sound that evening, but he never had problems starting the car again. Problem solved!
Now, unlike most of the stories that appear in Hey, Scripting Guy! there’s actually a point to this one: sometimes you can fix something even if you don’t have the slightest idea what you’re doing. That’s as true with scripting as it is with cars. After looking under the hood and jiggling a few wires we were able to come up with a way to determine the remaining grace period for Windows product activation; however, we can’t say for sure how we came up with that solution, nor can we tell what it all means.
The problem we ran into is that we don’t fully understand Windows Vista licensing. (And yes, we know: we probably are the only people in the world who don’t fully understand Windows Vista licensing.) Among other things, Windows Vista has a new feature known as Volume Licensing, feature designed for large enterprises like, well, Microsoft. When we check for licensing and grace period information on our test computer we get back a report that looks like this:
Windows Operating System – Vista, RETAIL channel Grace period remaining (hours): 0 LicensedWindows Operating System – Vista, OEM_COA_NSLP channel Grace period remaining (hours): 0 Unlicensed
Windows Operating System – Vista, OEM_SLP channel Grace period remaining (hours): 0 Unlicensed
Windows Operating System – Vista, RETAIL channel Grace period remaining (hours): 0 Licensed
Windows Operating System – Vista, OEM_COA_SLP channel Grace period remaining (hours): 0 Unlicensed
What does all that mean? Well, to tell you the truth, we aren’t sure. In addition, we aren’t totally sure what the returned data will look like on a Vista machine that doesn’t use Volume Licensing. But hey, half of the fun of scripting is simply closing your eyes, running the script, and waiting to see what happens.
Note. Shouldn’t the Scripting Guys make an effort to learn about Volume Licensing, especially in light of the fact that Vista includes a huge new WMI class devoted to software licensing? Yes, we should, and we will. Until that happens, however, you might want to take a few minutes to read up on Volume Licensing and the SoftwareLicensingProduct class on your own. |
At any rate, and with no further ado (mainly because we don’t have any further ado) here’s a script for determining the remaining grace period on a Windows Vista computer:
strComputer = “.”Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)
Set colItems = objWMIService.ExecQuery _ (“Select * From SoftwareLicensingProduct”)
For Each objItem in colItems Wscript.Echo objItem.Description
intGracePeriod = Int(objItem.GracePeriodRemaining / 60) Wscript.Echo “Grace period remaining (hours): ” & intGracePeriod
Select Case objItem.LicenseStatus Case 0 Wscript.Echo “Unlicensed” Case 1 Wscript.Echo “Licensed” Case 2 Wscript.Echo “Out-Of-Box Grace Period” Case 3 Wscript.Echo “Out-Of-Tolerance Grace Period” Case 4 Wscript.Echo “Non-Genuine Grace Period” End Select
Wscript.Echo Next
OK, let’s see if we can at least kind of figure out what’s going on here. As you can see, we start out by connecting to the WMI service on the local computer (although this script works equally well against remote computers). We then use this line of code to retrieve all the instances of the SoftwareLicensingProduct class 9which, in case you’re wondering, replaces the Win32_WindowsProductActivation class found in Windows XP and Windows Server 2003:
Set colItems = objWMIService.ExecQuery _ (“Select * From SoftwareLicensingProduct”)
OK, so maybe that part was pretty easy, wasn’t it? After issuing the query we then set up a For Each loop to walk through all the items in the returned collection. Inside that loop, the very first thing we do is echo back the value of the Description property:
Wscript.Echo “Description: ” & objItem.Description
Still pretty easy, isn’t it? OK, let’s take a look at the GracePeriodRemaining property. As it turns out, this property tells you the remaining time (in minutes) before the grace period expires. (Or, on Volume Licensing clients, the remaining time, again in minutes, before re-activation is required.) Just for the heck of it, we grab the value of this property and then divide it by 60; that allows us to express the remaining grace period in hours:
intGracePeriod = Int(objItem.GracePeriodRemaining / 60) Wscript.Echo “Grace period remaining (hours): ” & intGracePeriod
Would you rather have this value expressed in days? No problem; just divide by 1440 instead (60 minutes in an hour times 24 hours in a day yields 1440 minutes in a day):
intGracePeriod = Int(objItem.GracePeriodRemaining / 1440) Wscript.Echo “Grace period remaining (hours): ” & intGracePeriod
That’s actually all LD asked for, but we decided to go one step further and report back the license status as well. To do that we simply use a Select Case statement that takes action based on the value of the LicenseStatus property:
Select Case objItem.LicenseStatus Case 0 Wscript.Echo “Unlicensed” Case 1 Wscript.Echo “Licensed” Case 2 Wscript.Echo “Out-of-Box Grace Period” Case 3 Wscript.Echo “Out-of-Tolerance Grace Period” Case 4 Wscript.Echo “Non-Genuine Grace Period” End Select
Again, the code itself isn’t very complicated. LicenseStatus returns a value of 0, 1, 2, 3, or 4. All we do is take a look at this value and then echo back the actual license status. For example, if LicenseStatus equals 2 that means that the operating system is in the Out-of-Box Grace period. Fittingly enough, we then echo back that very fact:
Case 2 Wscript.Echo “Out-of-Box Grace Period”
So what exactly does it mean to be in the Out-of-Box Grace period, and what are the implications of that? Well, that’s the problem: we don’t really know. But with any luck you can find the answers here.
We hope that this points you in the right direction, LD. As soon as we get home tonight we’ll start digging a little deeper into Vista licensing.
Well, assuming we get the car started and can actually get home, that is. Hmmm, wonder if anything bad would happen if we jiggled this wi–
Determining the Grace Period Using Windows PowerShell
We can’t promise to do this every day, but whenever possible we’ll try to provide a Windows PowerShell solution along with the VBScript solutions typically used in the Hey, Scripting Guy! column. Here’s a sample solution from PowerShell pro June Blender.
I knew that I this task could be done easily in Windows PowerShell, because PowerShell can access any WMI class. (There are a few caveats, but I don’t think this one qualifies.) And the Scripting Guy did all of the heavy lifting: he determined which WMI class to use and found interesting properties to display.
Here’s a simple Windows PowerShell command that displays the grace period for Windows Vista license:
get-wmiobject -class SoftwareLicensingProduct | format-List -property Name, Description, ‘GracePeriodRemaining, LicenseStatus
Here’s a more complex version that calculates the grace period in days, instead of minutes, and translates the LicenseStatus property integer values to their meanings:
$L = get-wmiobject -class SoftwareLicensingProduct $L | format-list -property Name, Description, ` @{Label=”Grace period (days)”; Expression={ $_.graceperiodremaining / 1440}}, ` @{Label= “License Status”; Expression={switch (foreach {$_.LicenseStatus}) ` { 0 {“Unlicensed”} ` 1 {“Licensed”} ` 2 {“Out-Of-Box Grace Period”} ` 3 {“Out-Of-Tolerance Grace Period”} ` 4 {“Non-Genuine Grace Period”} ` } } }
Here’s the output. It’s not exactly like the VBScript output, but it will do:
Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, VOLUME_KMS channel Grace period (days) : 0 License Status : UnlicensedName : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, VOLUME_MAK channel Grace period (days) : 0 License Status : Unlicensed
Name : Windows(TM) Vista, OCUR add-on for Ultimate,HomePremium Description : Windows Operating System – Vista, RETAIL channel Grace period (days) : 0 License Status : Unlicensed
Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, RETAIL channel Grace period (days) : 0 License Status : Unlicensed
Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, VOLUME_KMSCLIENT channe l Grace period (days) : 173.5 License Status : Licensed
Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, OEM_SLP channel Grace period (days) : 0 License Status : Unlicensed
Windows PowerShell makes this otherwise complex task rather easy. The Get-WmiObject cmdlet has a Class parameter (-class) that lets you specify any WMI class. The Scripting Guy told us to use the SoftwareLicensingProduct class, and we always do what he says. I saved the output in a variable named $L (for “license”):
$L = get-wmiobject -class SoftwareLicensingProduct
At this point we have all of the data that we need in the $L variable; all that’s left to do is to format the output. I chose to format the data as a list, because the long values would be truncated in a table.
The Format-List cmdlet has a Property parameter (-property) that lets you select the properties that are displayed in the list. And I copied the properties that the Scripting Guy used in his script:
format-List -property Name, Description, GracePeriodRemaining, LicenseStatus
This command is designed to find the grace period on my local computer, but I could easily add the ComputerName parameter of Get-WmiObject to run the same command on a remote computer, such as one named Vista2.
get-wmiobject -class SoftwareLicensingProduct -computername Vista2 | format-List Name, Description, GracePeriodRemaining, LicenseStatus
I’m done with the task, but I’m still curious about this new SoftwareLicensingProduct object. Perhaps, just perhaps, the Scripting Guy didn’t show all of the cool properties of the class. Or, maybe the objects in this class have methods that I might want to use later.
Because I have objects, not just strings, I don’t need to retrieve the data again; I just need to re-format the display. First, I’ll look at the properties that we didn’t use in this display.
I can easily view all of the properties and methods of the objects in $L by using the Get-Member cmdlet. In this command, I use a pipeline operator (|) to send the objects in the $L variable to Get-Member:
$L | get-member
And here’s the result: the properties and methods of a SoftwareLicensingProduct object.
TypeName: System.Management.ManagementObject#root\cimv2\SoftwareLicensingPro ductName MemberType Definition —- ———- ———- Activate Method System.Management.ManagementBaseOb… DepositOfflineConfirmationId Method System.Management.ManagementBaseOb… UninstallProductKey Method System.Management.ManagementBaseOb… ApplicationID Property System.String ApplicationID {get;s… Description Property System.String Description {get;set;} EvaluationEndDate Property System.String EvaluationEndDate {g… GracePeriodRemaining Property System.UInt32 GracePeriodRemaining… ID Property System.String ID {get;set;} LicenseStatus Property System.UInt32 LicenseStatus {get;s… MachineURL Property System.String MachineURL {get;set;} Name Property System.String Name {get;set;} OfflineInstallationId Property System.String OfflineInstallationI… PartialProductKey Property System.String PartialProductKey {g… ProcessorURL Property System.String ProcessorURL {get;set;} ProductKeyID Property System.String ProductKeyID {get;set;} ProductKeyURL Property System.String ProductKeyURL {get;s… UseLicenseURL Property System.String UseLicenseURL {get;s… __CLASS Property System.String __CLASS {get;set;} __DERIVATION Property System.String[] __DERIVATION {get;… __DYNASTY Property System.String __DYNASTY {get;set;} __GENUS Property System.Int32 __GENUS {get;set;} __NAMESPACE Property System.String __NAMESPACE {get;set;} __PATH Property System.String __PATH {get;set;} __PROPERTY_COUNT Property System.Int32 __PROPERTY_COUNT {get… __RELPATH Property System.String __RELPATH {get;set;} __SERVER Property System.String __SERVER {get;set;} __SUPERCLASS Property System.String __SUPERCLASS {get;set;} ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime(); ConvertToDateTime ScriptMethod System.Object ConvertToDateTime(); Delete ScriptMethod System.Object Delete(); GetType ScriptMethod System.Object GetType(); Put ScriptMethod System.Object Put();
Hmm, Activate and UninstallProductKey methods. An OfflineInstallationID property. Okay, these are interesting, but I need to see an example.
Because the Get-WmiObject cmdlet retrieved more than one object, I actually have a group or array of objects. I can use the . (that’s a dot) property operator and the Count property of an array to find out how many objects I have.
$L.count 6
And I discover that I have six of them; a bit too much for one display.
I’ll select the most interesting one — the one with the license — and display the values of its properties. This command uses the Where-Object cmdlet (alias = “where”) to select one of the objects, the one with the word “client” somewhere in the description text. Again, I use the Format-List cmdlet and use its Property parameter with a value of all (*).
$L | where {$_.description -like “*client*”} | format-list -property *__GENUS : 2 __CLASS : SoftwareLicensingProduct __SUPERCLASS : __DYNASTY : SoftwareLicensingProduct __RELPATH : SoftwareLicensingProduct.ID=”cfd8ff08-c0d7-452b-9f60-ef 5c70c32094″ __PROPERTY_COUNT : 14 __DERIVATION : {} __SERVER : JUNEB-TESTV __NAMESPACE : root\cimv2 __PATH : \\JUNEB-TESTV\root\cimv2:SoftwareLicensingProduct.ID=”c fd8ff08-c0d7-452b-9f60-ef5c70c32094″ ApplicationID : 55c92734-d682-4d71-983e-d6ec3f16059f Description : Windows Operating System – Vista, VOLUME_KMSCLIENT chan nel EvaluationEndDate : 16010101000000.000000-000 GracePeriodRemaining : 249840 ID : cfd8ff08-c0d7-452b-9f60-ef5c70c32094 LicenseStatus : 1 MachineURL : http://go.microsoft.com/fwlink/?LinkId=57203 Name : Windows(TM) Vista, Enterprise edition OfflineInstallationId : 013421174935129263064954303550341742525985120465630192 PartialProductKey : 4BWMV ProcessorURL : http://go.microsoft.com/fwlink/?LinkId=57201 ProductKeyID : 89579-00142-236-020020-03-1033-6000.0000-0292007 ProductKeyURL : http://go.microsoft.com/fwlink/?LinkId=57204 UseLicenseURL : http://go.microsoft.com/fwlink/?LinkId=572
Now, I can display some of these other properties, just by changing the Format-List command. This time, I’ll pipe the objects in the $L variable to a Format-List command with a different set of properties:
$L | format-List Name, Description, GracePeriodRemaining, LicenseStatus, __SERVER, ProductKeyURL
Here’s the result:
Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, VOLUME_KMS channel GracePeriodRemaining : 0 LicenseStatus : 0 __SERVER : JUNEB-TESTV ProductKeyURL : http://go.microsoft.com/fwlink/?LinkId=57204Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, VOLUME_MAK channel GracePeriodRemaining : 0 LicenseStatus : 0 __SERVER : JUNEB-TESTV ProductKeyURL : http://go.microsoft.com/fwlink/?LinkId=57204
Name : Windows(TM) Vista, OCUR add-on for Ultimate,HomePremium Description : Windows Operating System – Vista, RETAIL channel GracePeriodRemaining : 0 LicenseStatus : 0 __SERVER : JUNEB-TESTV ProductKeyURL : http://go.microsoft.com/fwlink/?LinkId=57204
Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, RETAIL channel GracePeriodRemaining : 0 LicenseStatus : 0 __SERVER : JUNEB-TESTV ProductKeyURL : http://go.microsoft.com/fwlink/?LinkId=57204
Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, VOLUME_KMSCLIENT chann el GracePeriodRemaining : 258900 LicenseStatus : 1 __SERVER : JUNEB-TESTV ProductKeyURL : http://go.microsoft.com/fwlink/?LinkId=57204
Name : Windows(TM) Vista, Enterprise edition Description : Windows Operating System – Vista, OEM_SLP channel GracePeriodRemaining : 0 LicenseStatus : 0 __SERVER : JUNEB-TESTV ProductKeyURL : http://go.microsoft.com/fwlink/?LinkId=57204
Okay, it’s not much more useful in this case. Once again, we’ll have to concede to the Scripting Guy.
One last issue is how I managed the fancy formatting in the second version of the command:
$L = get-wmiobject -class SoftwareLicensingProduct $L | format-list Name, Description, ` @{Label=”Grace period (days)”; Expression={ $_.graceperiodremaining / 1440}}, ` @{Label= “License Status”; Expression={switch (foreach {$_.LicenseStatus}) ` { 0 {“Unlicensed”} ` 1 {“Licensed”} ` 2 {“Out-Of-Box Grace Period”} ` 3 {“Out-Of-Tolerance Grace Period”} ` 4 {“Non-Genuine Grace Period”} ` } } }
To manipulate a property value before displaying it, I used a calculated property. This takes the form of a “hash table,” (formally called an associative arrayto avoid any hint of impropriety). Each hash table can have Label, Expression, Format, and Alignment keys. You can use them with Select-Object and the formatting cmdlets (Format-*). For examples, see “get-help format-table -examples”.
Here’s the format:
@{<key>=<value>; [<key>=<value>…]}
such as:
@{Label=”Name”; Expression={$_.Property * Int}}
(The tricky part is remembering the correct number of parentheses and curly braces…)
In this case, to convert the value of the GracePeriodRemaining property from minutes to days, I divided the value by 1440, the number of minutes in a day. The expression starts with a $_ , the symbol that represents the current object. You need it whenever you are displaying more than one object (in this case, 6 of them).
@{Label=” Grace period (days)”; Expression={$_.graceperiodremaining / 1440}}
To change the integer values of the LicenseStatus properties to meaningful names, I typed a switch statement in the value of the Expression key. Windows PowerShell uses a pretty standard form of the switch statement. For a detailed description of switch statements in PowerShell, at the Windows PowerShell command prompt, type “get-help about_switch”.
These simple commands can be very powerful. Again, the trick was finding the SoftwareLicensingProduct class of WMI. In this case, like many others, I relied on the venerable Scripting Guy, but if he was off at a baseball game — I mean, occupied in an important meeting — you can use the List parameter of Get-WmiObject to list all of the available WMI classes.
get-wmiobject -list
I’ll continue to rely on the Scripting Guy.
0 comments