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
Whoops! Forgot to power on the server… Lets try this one more time!
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
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.






how can i provide a list of computers from a “computers.txt: and export the result to “result.txt”
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.
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.
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
You will need to unblock the file,either via the properties of the script or using Unblock-File in PowerShell V3.
I run the script on window 7 but no result after running the script ( no error as well)
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…..
There is a function in the .ps1 file that needs to be dot sourced before it will work.
ex. . .\get-productkey.ps1
Get-ProductKey -Computername
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?
In case anyone else wants to follow along, this discussion was taken to powershell.org. http://powershell.org/discuss/viewtopic.php?f=29&t=238
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.
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.
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
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.”
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)?
Hi, sorry but download of your script is not possible
http://gallery.technet.microsoft.com/scriptcenter/Get-product-keys-of-local-83b4ce97
Weird, I can download it just fine. What problems are you seeing with the download?
It does nothing on my system – reports nothing back either – bummer
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…)?
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 …