Get Windows Build Information from PowerShell

Updated: Tuesday, February 2, 2021

The value for Version changed in the October 2020 update. And I am missing the Experience value.

I have the Settings app and PowerShell side-by-side. We're comparing the values of Windows Edition, Version, Installed on, OS build, and Experience. The code from May 2020 does not give the correct value for Version. Experience is a new field that is not represented in my code.
# Windows Build Information

# Appx - Import-Module: Operation is not supported on this platform. (0x80131539)
# https://github.com/PowerShell/PowerShell/issues/13138

# How do I find the version of my Windows Feature Experience Pack?
# https://stackoverflow.com/questions/64831517/how-do-i-find-the-version-of-my-windows-feature-experience-pack

Switch ($PSVersionTable.PSVersion.ToString())
{
  "7.1.0" {Import-Module -Name Appx -UseWindowsPowerShell; Break}
  "7.1.1" {Import-Module -Name Appx -UseWindowsPowerShell; Break}
}

$buildInfo = [PSCustomObject]@{
  Version     = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name DisplayVersion
  InstalledOn = [DateTime]::FromFileTime((Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name InstallTime))
  OSBuild     = "{0}.{1}" -f (Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name CurrentBuild), (Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name UBR)
  Edition     = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName
  ReleaseId   = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseId
  Experience  = (Get-AppxPackage 'MicrosoftWindows.Client.CBS').Version
}

Version can be fixed by using the new registry value DisplayVersion. I keep the old value and rename it ReleaseId.

Experience is coming from (Get-AppxPackage ‘MicrosoftWindows.Client.CBS’).Version

Since I store this code in my profile, the module warning for PowerShell 7.1/Appx appears when the shell starts.

PowerShell output from new code. Version shows the correct value. The old value shows under ReleaseId. Experience only shows the version number: "120.2212.31.0". Experience does not show the full string from the Settings app: "Windows Feature Experience Pack 120.2212.31.0".

May 2020 Article

I’d like to know how to get this information from PowerShell:

Screen capture of the Windows 10 Settings > System > About page. We see the following information: Edition Windows 10 Pro, Version 1909, OS build 18363.836. This section also shows Installed on 7/31/2019.

A good place to start might be tracing the Settings app itself.

We can do that with Process Monitor which allows us to track what the Settings app is doing.

A small screen capture of Process Monitor simply showing what the application looks like. The name Process Monitor appears on the title bar. There is a traditional text-based menu which includes File, Edit, Event, Filter, Tools, Options, and Help. There is a toolbar which includes Open Folder, Floppy Disk, Magnifying Glass, Scrolling Page, Erase Page, Funnel, Capital A with Colored Underscore, Crosshair, Page with Lines suggesting a Document Tree, Binoculars, Large Green Arrow angled at 10 o'clock, Disintegrating Tower of Cubes with Magnifying Glass, File Cabinet with Magnifying Glass, Networked Computers with Magnifying Glass, Windows Logo with Magnifying Glass, and Monochrome Line Graph. The main area shows events. One event is shown per line. The visible headings read Time of Day, Process Name, PID (process identifier), and Operation. A sample entry gives a very precise time such as 12:49:13.6012822 PM. Process Name: svchost.exe. PID: 3660. Operation: Lock File. Scroll bars indicate columns and events hidden from view. In this image, we can tell the capture process is disabled because the lone Magnifying Glass icon (third toolbar icon) is crossed out.

Process Monitor requires administrator access. Make sure to run it as an administrator.

The Explorer context menu opened for Procmon. The option to Run as administrator is selected.

If we just turn Process Monitor on, we’ll see every action from every application.

A screen capture of Process Monitor in its natural state. The entire screen is filled with extraneous information.

That’s too much information.

So, the first thing we do is set our filter. Then we can see just where Settings is looking for information.

We can access the Filter window by clicking on the funnel icon.

The Process Monitor toolbar. The Capture and Filter buttons are highlighted. Respectively, these are a magnifying glass and a funnel. The magnifying glass will have a slash through it when the capture is paused.

Also important is the magnifying glass icon. This toggles the capture process on and off. Capturing is a strain on a computer and should be paused after the necessary data is collected. The icon will appear normal while the capture is running and will have a red slash while the capture is disabled. Let’s leave the capture running for now.

After clicking on the funnel icon, the Filter window shows. And we can enter the filter settings.

Process Monitor filter showing the five necessary filters for capturing Settings queries. Details are given in the main text.

We can add filters by using the fields at the top of the window.

For example, click the first drop-down box and select Process Name. Drop down the second box and select is. Type SystemSettings.exe into the third box. Drop down the fourth box and select Include.

Click the Add button when you’re ready.

This will add the first filter. The other filters can be added in a similar fashion.

Here are the filter settings I used:

  1. Process Name is “SystemSettings.exe” then Include
  2. Operation is “RegQueryValue” then Include
  3. Result is “SUCCESS” then Include
  4. Path begins with “HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\” then Include
  5. Path contains “ProfileList” then Exclude

After adding the rules, click OK to close the Filter window.

Note that, in my screen capture, I erased all the non-essential filters. You will see a default set of filters. Those can be ignored.

The default settings for Filter. These filter out background kernel events and overhead from Process Monitor and Process Explorer.
Default Filter Settings

Once we have our filter settings, we can run our program to see what it does. Make sure the capture is running: The Magnifying Glass icon on the toolbar should not be crossed out. Then run the program you are investigating.

In our case, we:

  1. Open the Windows 10 Settings app.
  2. Click System.
  3. Click About.
  • Start Menu > Settings
  • Click System in the Settings app.
  • Click About while viewing the System Settings.
  • We see the result: the "About" settings.
  • The settings we're interested in: Edition, Version, Installed on; OS build.

At this point, the Edition, Version, and OS Build are showing in the Settings app. This means our program did what we wanted. And we’ve captured what it was doing behind-the-scenes with Process Monitor.

Let’s take a look at Process Monitor.

First, stop the capture by clicking on the Magnifying Glass icon. Please wait a few seconds for the capture to stop. When it has stopped, the icon will change to a magnifying glass that is crossed out.

We see the results from our capture.

The filtered results in Process Monitor. Here we see only events that reflect the five filters we added. In particular, there are only registry entries which correspond to the build information in Settings plus a few other values which fall under that parent registry key.

There are many results in the Process Monitor window. Each points to a different registry value. Above, I’ve highlighted the Path column: showing where each value is located in the registry.

If we select the first entry and click the green Jump to Object button …

A demonstration of how to jump to a registry key. Select an entry in the main work area in Process Monitor. Then click the green arrow that points at 10 o'clock in the toolbar. This opens the Registry Editor to the proper key (not depicted).

… the corresponding key will open in the Registry Editor.

The Registry Editor open to the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion key. This is the result of using the green arrow in Process Monitor. The registry data is blurred for privacy. Many extraneous keys are visible in the key browser on the left. The values and data types for CurrentVersion are legible in the right pane.

Here the entries are blurred for privacy. But, on your system, you will be able to see each value.

Notice the Path column from Process Monitor matches the path shown in Registry Editor.

A mock-up of Process Monitor and Registry Editor. The printed path to the CurrentVersion key is highlighted in each application. This demonstrates how the two applications relate. In Process Monitor, the path is shown for each entry in the Path column. For Registry Editor, the path is shown in what appears to be an address bar. In addition, Process Monitor lists the registry value as a trailing path item: \CurrentVersion\InstallationType. No indication of InstallationType is shown in this image for Registry Editor.

Next, we’ll see that the trailing InstallationType shown in Process Monitor corresponds to the registry value.

For example, here is the registry data for the first result from Process Monitor. The value is InstallationType and the data is Client. The data type is REG_SZ or string.

Registry Editor. Here there is a snippet showing the InstallationType value and its data "Client". The data type of this value is REG_SZ or string.

After looking up this value, go back to Process Monitor and look at the next entry. Switch back to Registry Editor and view the data at that value. If you can’t find the value, try selecting the entry in Process Monitor and clicking the Jump to Object button again. This will take you to the correct registry key.

Keep going between Process Monitor and Registry Editor. In this fashion, you can determine where each value in the Settings app is stored in the Registry.

Our Process Monitor filter turned up more registry values than I’m interested in.

So, here is a list of the settings and values I want:

Registry ValueSettings AppData Type
ReleaseIdVersionREG_SZ
InstallTimeInstalled onREG_QWORD
CurrentBuildOS build before the dotREG_SZ
UBROS build after the dotREG_DWORD
ProductNameEditionREG_SZ

All of these values are located at this key:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\

This matches our Process Monitor filter if you notice. Though, some of the other results from Process Monitor appear in subkeys, our values are all present in the CurrentVersion key.

Discovering where settings are located is one of the main functions of Process Monitor. And we’ve demonstrated that function here today.

These results are also a clue that the Settings app is accessing the registry directly. There are other ways to get this information in a shell. But, I intend to follow this example and access the registry to retrieve the information I am interested in.

Before we leave Process Monitor, I’ll also talk about developing the filter. I mentioned there is a default filter. And that displays everything that is going on. So, where do my filter settings come from?

There is no magic book that says what filters to apply. I had to make the one we used today. And I’ve shared it for convenience. The normal process is to find some value in Process Monitor that should not be there. For example, ctfmon.exe. Then right-click that spot in the window and choose Exclude from the context menu. Over time, you can find what you’re looking for. But it takes effort and experience.

Building a filter the hard way. The default filter shows all application activity on the system. Here we are right-clicking specifically on the spot where ctfmon.exe appears in the Process Name column in the main Process Monitor window. By clicking on that specific spot, we get a context menu specific to "ctfmon.exe". That context menu is showing. The option to Exclude ctfmon.exe is selected in the context menu. If clicked, that would remove all events generated by ctfmon.exe from the main Process Monitor window. Also, the corresponding rule would appear in the Filter window.

At this point, we have a list of registry values.

For example:

key 
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion

value 
ReleaseId

Now, we just have to access the registry values from PowerShell.

I have a table that can help us.

After some experimentation, I’ve decided that Get-ItemPropertyValue is the best choice for our work. It will provide a value with a basic type rather than a custom object or RegistryKey object. I want basic data types returned.

Let’s try the first value.

Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseId

Notice we have to use the HKLM:\ drive before our path will work. We can fix our path by adding a colon after HKLM.

We also have to use quotes around the path because it contains spaces.

When we run our cmdlet, we get 1909. Remember that ReleaseId corresponds to Version in the mapping between the registry and settings app. When we examine Settings, we see the value 1909 under Version. So, the two values match. We successfully retrieved some build information with PowerShell.

  • Settings > About page. Shows Edition: Windows 10 Pro, Version: 1909, and OS build: 18363.836.
  • Get-ItemPropertyValue was run in PowerShell against ReleaseId. The data returned is 1909.

Let’s try the other values.

Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name CurrentBuild

Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName

It is pretty easy to do CurrentBuild and ProductName.

PowerShell showing the results of the Get-ItemPropertyValue cmdlet run against CurrentBuild and ProductName values. Respectively, 18363 and Windows 10 Pro are the results.

But, looking at InstallTime and UBR, we have funky data. UBR has the correct value in parentheses. But nothing about InstallTime looks like July 31 which is when I installed Windows.

The unusual binary values for InstallTime and UBR are highlighted in Registry Editor. InstallTime has a data type of REG_QWORD: representing a 64-bit binary value. UBR has a data type of REG_DWORD: representing a 32-bit binary value. Both data appear in hexadecimal. In parentheses, both data are converted to decimal. The decimal version of UBR's datum is recognizable as the minor version number from OS Build: 836. Neither representation of the InstallTime datum is recognizable as a date. Both representations of InstallTime are far, far too large to be interpreted as a date, time, or a reasonable unit of time. The InstallTime datum is represented as 0x1d547f32199aa97 (132090873643641495).

Our first clues come from the data types.

InstallTime uses a REG_QWORD and UBR uses a REG_DWORD. Using our earlier reference, we see that means InstallTime is storing a 64-bit integer and UBR is storing a 32-bit integer. The 0x shown in the data tells us Registry Editor is showing us a hexadecimal representation. Also, QWORD means quad word or 4 * 16 = 64. And DWORD means double word or 2 * 16 = 32. So, next time, we can tell how big these numbers are just by looking at the type present in Registry Editor.

For starters, let’s try accessing the data using Get-ItemPropertyValue.

Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name UBR

Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name InstallTime

Eyeballing the result, we see that we have been spared the chore of converting these values from binary.

The results from using Get-ItemPropertyValue on UBR and InstallTime. UBR gives 836. This is recognizable as the minor build version. InstallTime gives 132090873643641495.

Right away, we can see we have the right build number from UBR. So, we can use that value directly.

  • UBR data from PowerShell. We see 836.
  • UBR data from Settings. We see 836.

The only remaining mystery is InstallTime.

Let’s try casting it to [DateTime] to see what happens.

[DateTime] (Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name InstallTime)

Converting the InstallTime value to DateTime gives July 31, 419 as the result. This is 1600 years too soon compared to the actual install date of July 31, 2019.

Well, we appear to be on the right track. We have July 31.

But the year is 419: 1600 years too soon.

Is there anything we can find out about Windows dates and the 1600-year offset?

After a bit of searching, I was able to find this method:

DateTime.FromFileTime(Int64) Method (System) | Microsoft Docs

It explains the offset:

A Windows file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed since 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC). Windows uses a file time to record when an application creates, accesses, or writes to a file.

So, what happens if we use this method?

$a = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name InstallTime

[DateTime]::FromFileTime($a)

Result of using [DateTime]::FromFileTime() on the InstallTime value. Now the date is printed as July 31, 2019 which is the same value printed in Settings.

Now we have July 31, 2019 which matches Settings.


That was the last value we needed to decode.

So, what does everything look like when put together?

[PSCustomObject]@{
  Version     = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseId
  InstalledOn = [DateTime]::FromFileTime((Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name InstallTime))
  OSBuild     = "{0}.{1}" -f (Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name CurrentBuild), (Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name UBR)
  Edition     = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName
}

PowerShell object containing the output of the final code. Here we see a custom PowerShell object with four properties which correspond to Settings: Version 1909, InstalledOn 7/31/2019, OSBuild 18363.836, and Edition Windows 10 Pro.

If you want to know more about what the build numbers mean, you can use the Windows 10 release information. If you take care to scroll the embedded table, you will see that each minor build has a date, channel, and knowledge base article associated with it.

Finally, there is a good article about creating custom PowerShell objects.

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 )

Google photo

You are commenting using your Google 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