Hey, Scripting Guy! How Can I Determine the Grace Period for a Computer Running Windows Vista?

Hey, Scripting Guy! How Can I Determine the Grace Period for a Computer Running Windows Vista?

  • Comments 1
  • Likes

Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I determine the remaining operating system grace period for a computer running Windows Vista?

-- LD

SpacerHey, Scripting Guy! AnswerScript Center

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
Licensed

Windows 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      : Unlicensed

Name                : 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
duct

Name                         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=57204

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

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • IT doesnt write if the grace period expired, but how many days do I have to activate...