Updating An Existing Get-ProductKey Function

A couple of days ago I received my daily PowerTips email from PowerShell.com talking about how to get the Windows Product key via PowerShell from your local system.

The code is simple and to the point to look for and translate the data into a readable product key:

function Get-ProductKey {    
    $map="BCDFGHJKMPQRTVWXY2346789" 
    $value = (get-itemproperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").digitalproductid[0x34..0x42]  
    $ProductKey = ""  
    for ($i = 24; $i -ge 0; $i--) { 
      $r = 0 
      for ($j = 14; $j -ge 0; $j--) { 
        $r = ($r * 256) -bxor $value[$j] 
        $value[$j] = [math]::Floor([double]($r/24)) 
        $r = $r % 24 
      } 
      $ProductKey = $map[$r] + $ProductKey 
      if (($i % 5) -eq 0 -and $i -ne 0) { 
        $ProductKey = "-" + $ProductKey 
      } 
    } 
    $ProductKey
} 

However, being that I had a little free time and really like this code, I wanted to enhance it to make it more robust by adding a few extra things here and there. By doing this, you can see just how easy it is to take some existing code and modify it to meet your own specific requirements.

The list of items I wanted to add to the existing code are:

  • Run against multiple systems.
  • Allow this function to query remote systems; not just locally
  • Output an object instead of just text
  • In the object, show the following properties
    • Computername, ProductKey,OSDescription,OSVersion
  • Allow to query against 64bit systems
  • Error handling
  • Comment Based Help

First off, lets add some comment based help to this code:

function Get-ProductKey {
     <#   
    .SYNOPSIS   
        Retrieves the product key and OS information from a local or remote system/s.
         
    .DESCRIPTION   
        Retrieves the product key and OS information from a local or remote system/s. Queries of 64bit OS from a 32bit OS will result in 
        inaccurate data being returned for the Product Key. You must query a 64bit OS from a system running a 64bit OS.
        
    .PARAMETER Computername
        Name of the local or remote system/s.
         
    .NOTES   
        Author: Boe Prox
        Version: 1.1       
            -Update of function from http://powershell.com/cs/blogs/tips/archive/2012/04/30/getting-windows-product-key.aspx
            -Added capability to query more than one system
            -Supports remote system query
            -Supports querying 64bit OSes
            -Shows OS description and Version in output object
            -Error Handling
     
    .EXAMPLE 
     Get-ProductKey -Computername Server1
     
    OSDescription                                           Computername OSVersion ProductKey                   
    -------------                                           ------------ --------- ----------                   
    Microsoft(R) Windows(R) Server 2003, Enterprise Edition Server1       5.2.3790  bcdfg-hjklm-pqrtt-vwxyy-12345     
         
        Description 
        ----------- 
        Retrieves the product key information from 'Server1'
    #>  

I also add some parameters to the existing code with a default parameter value for the Computername to point to the local machine. I also add some aliases to support the ValueFromPipeLineByPropertyName which allows extra pipeline support. You will also notice that I set the type for Computername to [string[]] which means that it will take a collection of strings for that parameter.

    [cmdletbinding()]
    Param (
        [parameter(ValueFromPipeLine=$True,ValueFromPipeLineByPropertyName=$True)]
        [Alias("CN","__Server","IPAddress","Server")]
        [string[]]$Computername = $Env:Computername
    )

I start off with a quick check to see if the system is reachable on the network and then perform a WMI query for some pieces of information regarding the Caption (Operating System name), Version and also the Architecture which will tell me if the OS is 64bit or 32bit which will be vital in determining what to do next for the product key.

    Process {
        ForEach ($Computer in $Computername) {
            Write-Verbose ("{0}: Checking network availability" -f $Computer)
            If (Test-Connection -ComputerName $Computer -Count 1 -Quiet) {
                Try {
                    Write-Verbose ("{0}: Retrieving WMI OS information" -f $Computer)
                    $OS = Get-WmiObject -ComputerName $Computer Win32_OperatingSystem -ErrorAction Stop                
                } Catch {
                    $OS = New-Object PSObject -Property @{
                        Caption = $_.Exception.Message
                        Version = $_.Exception.Message
                    }
                }

The rest of the code deals with the actual operations to query the specified system’s registry key based on whether it is a 64bit system or 32bit. This is important because if the system is 64bit, the DigitalProductId4 key value must be used instead of DigitalProductId, otherwise you will not receive the accurate data for the product key. The other part of the code performs the translation of the value of the registry key to find the product key and also using a .Net class ([Microsoft.Win32.RegistryKey]) which will allow me to perform a remote query of the registry.

                Try {
                    Write-Verbose ("{0}: Attempting remote registry access" -f $Computer)
                    $remoteReg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Computer)
                    If ($OS.OSArchitecture -eq '64-bit') {
                        $value = $remoteReg.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue('DigitalProductId4')[0x34..0x42]
                    } Else {                        
                        $value = $remoteReg.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue('DigitalProductId')[0x34..0x42]
                    }
                    $ProductKey = ""  
                    Write-Verbose ("{0}: Translating data into product key" -f $Computer)
                    for ($i = 24; $i -ge 0; $i--) { 
                      $r = 0 
                      for ($j = 14; $j -ge 0; $j--) { 
                        $r = ($r * 256) -bxor $value[$j] 
                        $value[$j] = [math]::Floor([double]($r/24)) 
                        $r = $r % 24 
                      } 
                      $ProductKey = $map[$r] + $ProductKey 
                      if (($i % 5) -eq 0 -and $i -ne 0) { 
                        $ProductKey = "-" + $ProductKey 
                      } 
                    }
                } Catch {
                    $ProductKey = $_.Exception.Message
                } 

 

Lastly we need to make this into an object because this is PowerShell, after all. I use the New-Object cmdlet and supply a hash table of data that is saved to a variable. After the object is created, I opt to create a custom type name for this object to make it more unique which is then displayed to the user running the code.

                $object = New-Object PSObject -Property @{
                    Computername = $Computer
                    ProductKey = $ProductKey
                    OSDescription = $os.Caption
                    OSVersion = $os.Version
                } 
                $object.pstypenames.insert(0,'ProductKey.Info')
                $object
            } Else {
                $object = New-Object PSObject -Property @{
                    Computername = $Computer
                    ProductKey = 'Unreachable'
                    OSDescription = 'Unreachable'
                    OSVersion = 'Unreachable'
                }  
                $object.pstypenames.insert(0,'ProductKey.Info')
                $object                           
            }
        }
    }
}

Code in Action

Ok, lets run this against a local and remote system and see it in action.

Get-ProductKey -Computername Boe-PC,DC1

image

Whoops! Forgot to power on the server… Lets try this one more time!

image

Ok, that looks better. As you can see, not only is the product key displayed, but the computername as well as the Operating System and version are also displayed as well. This allows for better use of the code to see not only what the product key is, but what it also applies to as well.

Download This Function

Script Repository

Hope you enjoy my updated take on this already great function! This also shows how you can take some existing code and modify it to your own requirements to accomplish a goal.

About these ads

About Boe Prox

Sr. Systems Administrator who uses Powershell daily for everything from reporting to automating daily tasks to just seeing what I can do with it.
This entry was posted in powershell, scripts and tagged , , . Bookmark the permalink.

20 Responses to Updating An Existing Get-ProductKey Function

  1. msuk says:

    how can i provide a list of computers from a “computers.txt: and export the result to “result.txt”

  2. Justin says:

    I’m not sure where I’m going wrong. I run:

    .\get-productkey
    get-productkey -computername (servername)

    Result:

    + FullyQualifiedErrorId : CommandNotFoundException

    I run:
    .\Get-ProductKeydownloaded.ps1 Get-productkey -computername (servername)
    Result: Nothing

    Even if I output it to a txt file:
    .\Get-ProductKeydownloaded.ps1 Get-productkey -computername (servername) >> C:\Tmp\test.txt

    The result is an empty txt file.

    • Boe Prox says:

      Running this as a script will not work. You have to dot source the function into the session.
      . .\Get-ProductKey.ps1

      Then run the function to get it to work.

  3. Raghav Singh says:

    When I am executing i am getting below error
    File E:\Get-ProductKey.ps1 cannot be loaded. The file E:\Get-ProductKey.ps1 is not digitally signed. The script will no
    t execute on the system. Please see “get-help about_signing” for more details..
    At line:1 char:22
    + ..\Get-ProductKey.ps1 <<<<
    + CategoryInfo : NotSpecified: (:) [], PSSecurityException
    + FullyQualifiedErrorId : RuntimeException

  4. chungboon says:

    I run the script on window 7 but no result after running the script ( no error as well)

  5. Stephen says:

    Why doesnt the script work for me? I set set-exec pol unrestricted run the ps1 file and get the warning to allow Run once and returns nothing…..

  6. John says:

    When I run this on some PC’s I get this “Exception calling “OpenRemoteBaseKey” with “2″ argument(s): “The network path was not found….” where the product key is supposed to be. Any Ideas?

  7. John says:

    When I run the script it asks me if I want to run. Then I run once and it doesn’t output anything. Just puts me right back in the directory I started.

  8. Aram says:

    Hi, I have run your script on windows 7 64bit as “PS C:\Scripting> .\Get-ProductKey.ps1″ and I get no report back. Tried it with -Computername… and still got no results. I have also tried it on other windows 7 32bit comps and did not receive any results.

    • Boe Prox says:

      This is actually a function that resides in the script, so you need to dot-source the script before using the function.

      . .\Get-ProductKeys.ps1
      Get-ProductKeys

  9. Zul says:

    Hi, I tried executing this on several systems which consists of combinations of 32 & 64bits systems. On the output, on the license key column show “Cannot index into a null array.”

    • Boe Prox says:

      Hmmm… When running against 64 bit systems, you need to make sure that you are running it from a 64 bit system. Also, while I don’t think it should matter, are you also running it from an Administrator prompt (run as administrator)?

    • Boe Prox says:

      Weird, I can download it just fine. What problems are you seeing with the download?

      • Kelly says:

        It does nothing on my system – reports nothing back either – bummer

      • Boe Prox says:

        Can you provide the syntax that you are using to run the code? Also can you let me know how you are running it (elevated prompt, OS, etc…)?

      • Michal Zobec says:

        Hi, I do not understand this – at 8 July I not download script – after accept licence agreement at MS Script Repository is nothing happen (not action for file download). but today is working…
        thanks for you script I test …

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s