List installed .NET versions on remote computers
You could find yourself wanting to know which versions of .NET are installed on remote computers or the computer you're on. Here is a PowerShell script that takes a list of computers (or you can use -LocalHost for the current computer), collects data via remote registry access - or PSRemoting as of v2.0 - and the original script creates a CSV file with the data. To be friendly to people used to the old script, I added the parameter -ExportToCSV to the "DotNetVersionLister" module I've since then uploaded. It's a lot more standards-conforming as PowerShell goes. It emits/outputs objects instead of simply writing a CSV file by default, plus I added support for using PSRemoting instead of remote registry access, with the -PSRemoting parameter.
I'm not rewriting all of the documentation, so some of it could be partially outdated if you use the latest v2.x module - but I do recommend that.
The CSV can easily be parsed using PowerShell's usual mechanisms, some of which I demonstrate here. You can also import it in Excel, which there's a screenshot of here.
I have since creating the initial documentation also added support for:
- .NET 4.5
- .NET 4.5.1
- .NET 4.5.2
- .NET 4.6
- .NET 4.6.1
- .NET 4.6.2
- .NET 4.7
- .NET 4.7.1
- .NET 4.7.2
... and greater, but then it will just say "4.7.2 or later". Drop me a mail at svendsentech@gmail.com if a new .NET version is out. I'll try to add support if possible. This is now also on GitHub (see download section below).
The CSV has one field for errors, one for each .NET version from 1.1 till 4.0 (client/full differentiated for v4) and one field with the computer/host name. In v1.1 and later versions of the script there's also a field for 4.x which differentiates between the different 4.x versions above 4.0, from 4.5 and up.
Access to the remote registry service on the target computers is necessary unless you use the module version in v2.x or greater, where you also have a -PSRemoting parameter. There are a couple of example scripts for enabling and disabling the remote registry service remotely using sc.exe at the bottom of the article.
I didn't add detection of .NET v1.0, so you're on your own if you need to look for that. You will also have to rewrite the script if you need to detect service pack levels.
I initially wrote a simple version of this script some time ago, and I've now rewritten it to suit "public" standards a bit more, and it has then been patched with detection of new versions. As the years have passed since inception in 2011, this script has by now been patched numerous times, and rewritten almost completely. The documentation is partially outdated in this article.
Here's what the latest version looks like with the -LocalHost parameter in use:
Contents
Used Registry Keys
In the table below you can see the registry keys that are used. If the value is "1", the script reports them as installed. There's an article from Microsoft about this here (previous link: here.)
.NET 1.1 | HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322\Install |
.NET 2.0 | HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727\Install |
.NET 3.0 | HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Install |
.NET 3.5 | HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5\Install |
.NET 4.0 Client | HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client\Install |
.NET 4.0 Full | HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\Install |
.NET >4.5.x | HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\Release (parsed) |
End Result CSV Displayed In Excel
Here's a screenshot of the CSV imported to Excel.
In Excel 2010 the default delimiter appears to be a tab, not a comma, so you need to go to the ribbon "Data", choose "From Text" under "Get External Data" and specify the correct options when importing. Using either the default encoding ANSI or UTF-8 works for me with the lab data. You need to specify that the data is delimited, and that the delimiter is a comma.
There's more about this in the Excel help if you search for "excel import csv" or similar.
A trick to avoid this, is to use a semicolon as a delimiter in the CSV file, and save it with a .csv extension. That should cause Excel to magically import it correctly when double-clicked (yes, I boldly split infinitives!).
ipcsv file1.csv | epcsv -not -enc utf8 -delim ';' file1semicolon.csv
I've learned this depends on culture settings, so instead of -Delimiter ";" # you can use the parameter -UseCulture, this should make it open magically in Excel on that computer with current culture/locale settings.
Module Parameters
Parameters for Get-DotNetVersion as exported from the DotNetVersionLister module.
ComputerName | Alias "Cn". A list of computer names to process. Use "(gc x:\hosts.txt)", without the quotes, to use a file. |
PSRemoting | Use PowerShell remoting instead of remote registry access. Remote registry access requires RPC, which in turn requires lots of firewall openings. |
ExportToCSV | Export to a CSV file as well as files containing online and offline computers. |
LocalHost | Check the local computer. Introduced in version 2.1. |
ContinueOnPingFail | Try to gather even if the remote computer does not reply to ping. |
NoSummary | Do not display the end summary with Write-Host. |
Clobber | Only in use with -ExportToCSV. Overwrite potentially existing files without prompting. Date and time is in the file name by default |
Credential | Only in use with -PSRemoting. Specify explicit/alternate credentials. |
Script Parameters
Parameters for the original script.
ComputerName | Required. A list of computer names to process. Use "(gc x:\hosts.txt)", without the quotes, to use a file. |
Clobber | Overwrite potentially existing files without prompting. Date and time is in the file name by default |
Example Use
Here are the results from the home lab. The -ComputerName parameter takes a list of strings; to use a file, use "(gc .\computers.txt)". In the screenshot example I use a subexpression with a pipeline that just gets every computer from Active Directory. See this article for information about how to get computer names from Active Directory.
Test Run
Some errors and some successful checks.
Looking At The CSV Data
For computers where there are no errors, the error field will contain a hyphen/dash ("-"), so you can use -eq '-' or -ne '-' to filter on that using Where-Object. You will get "No ping reply" in the error field for computers that do not respond to ICMP echo / ping - and also for computers which do not resolve via DNS or WINS/NetBIOS.
PS C:\> Import-Csv .\DotNet-Versions-2012-03-18.csv | Select -First 1 Computer : esxi v4\Client : Unknown v4\Full : Unknown v3.5 : Unknown v3.0 : Unknown v2.0.50727 : Unknown v1.1.4322 : Unknown Error : No ping reply PS C:\> Import-Csv .\DotNet-Versions-2012-03-18.csv | Select -Skip 1 -First 1 Computer : SRV2003R2ESXI v4\Client : Installed v4\Full : Installed v3.5 : Installed v3.0 : Installed v2.0.50727 : Installed v1.1.4322 : Not installed (no key) Error : -
Inspecting Computers With Errors
To filter out and inspect all errors, you can do it like this:
PS C:\> Import-Csv .\DotNet-Versions-2012-03-18.csv | Where { $_.Error -ne '-' } | ft -auto Computer v4\Client v4\Full v3.5 v3.0 v2.0.50727 v1.1.4322 Error -------- --------- ------- ---- ---- ---------- --------- ----- esxi Unknown Unknown Unknown Unknown Unknown Unknown No ping reply WIN2K Unknown Unknown Unknown Unknown Unknown Unknown No ping reply SERVER2003 Unknown Unknown Unknown Unknown Unknown Unknown No ping reply VISTA64ESXI Unknown Unknown Unknown Unknown Unknown Unknown Unable to open remote registry: Exception calling... WINXPESXI Unknown Unknown Unknown Unknown Unknown Unknown Unable to open remote registry: Exception calling... WIN7ESXI Unknown Unknown Unknown Unknown Unknown Unknown Unable to open remote registry: Exception calling... SERVER2008 Unknown Unknown Unknown Unknown Unknown Unknown No ping reply XPTANKET Unknown Unknown Unknown Unknown Unknown Unknown No ping reply
Filtering On A Certain Installed .NET Version
To filter on which computers have .NET 4.0 Full installed, you can do it like this:
PS C:\> Import-Csv .\DotNet-Versions-2012-03-18.csv | Where { $_.'v4\Full' -eq 'Installed' } | Select 'v4\Full', Computer | ft -auto v4\Full Computer ------- -------- Installed SRV2003R2ESXI Installed SIEMENS Installed 2008R2ESXI2 Installed 2008R2ESXI
You can use "$_.'v4\Full' -ne 'Installed'" to see which clients or servers don't have it installed.
I started the remote registry service on the Vista client and now I got the data from that too:
PS C:\> Import-Csv .\DotNet-Versions-2012-03-18.csv | Where { $_.Computer -ieq 'vista64esxi' } Computer : VISTA64ESXI v4\Client : Installed v4\Full : Not installed (no key) v3.5 : Installed v3.0 : Installed v2.0.50727 : Installed v1.1.4322 : Not installed (no key) Error : -
Summary Of .NET Versions
Here's a screenshot of a summary of my test environment:
Here's how the 4.x versions are differentiated:
Here's a screenshot of the DotNetVersionLister module (v2.0 of this thing, you could say):
Download
- Get-DotNet-Version.ps1.txt - frozen, old, obsolete version for now - do not use, v1.x. Right-click, "save as" and rename to .ps1. Remember to unblock (PSv3 and up have the cmdlet Unblock-File).
- DotNetVersionLister.zip - v.2.2.5. Download, unblock, unzip, copy to a modules folder. Or download it from the PowerShell gallery (see below) or GitHub.
It's on GitHub here: https://github.com/EliteLoser/DotNetVersionLister
Note made 2019-04-22: The version on GitHub is the latest, with the version in the PowerShell Gallery (I'm having issues publishing) you need to still use:
-Localhost:$False
... when you use the -PSRemoting flag. It's a bug that's fixed in the version on GitHub. Sorry.
Version history
- 2019-04-22: No longer maintain version history here. See GitHub.
- 2018-08-04: v2.2.5: Add support for .NET 4.7.2 on all possible Windows versions.
- 2017-12-20: v2.2.4: Corrected a flaw/bug and added support also for .NET 4.6.2 on Server 2016 (in some editions) and on Windows 10 Anniversary Update. Based on feedback from Byron Wright. He had this issue with a Server 2016 server.
- 2017-12-01: v2.2.3. Added .NET version 4.7.1 detection.
- 2017-06-18: v2.2.2. Added .NET version 4.7 detection.
- 2017-06-07: v2.2.1. Added -Credential for use with PSRemoting only for now (needed it ;).
- 2017-04-20: v2.2. Bug fix. Removed calls to Dispose() causing errors on at least computers with PSv2. Windows 7 with PSv3 worked, with PSv2 it didn't - now both work. This was only in effect with the -PSRemoting flag.
- 2017-02-12: v2.1.1 uploaded. Documentation fixes/improvements.
- 2017-02-12: v2.1. Things are looking better. Also fixed the accidental feature loss of 4.x detection on Windows 10 and a few other OSs. Should be OK now.
- 2017-02-10: Uploading the DotNetVersionLister module. "v2.0" of this stuff. It's a lot better than the stand-alone script, but it became complex and difficult to test, so there are some small oddities/bugs in the error handling in more "obscure" cases. It has an experimental -PSRemoting parameter that uses PSRemoting instead of remote registry access. This became hacky quickly. Should be usable, though. I've been made aware that you can pass String.Empty to access the local registry with OpenRemoteBaseKey(), so I'll fix this mess soon. Just waiting for the next "energy wave" ...
- 2016-10-10: Uploaded v1.4. Added support for .NET 4.6.2. Thanks to Blake Masri for making me aware of the need.
- 2016-05-29: Uploaded v1.3. Significant code quality improvements, nothing functionally. Changed the property "Computer" to "ComputerName" (standards-conforming).
- 2016-01-13: Uploaded v1.2. Added support for .NET 4.6.1. Thanks to Jason Finch for making me aware of the need.
- 2015-10-31: Uploaded v1.1. Added support for detecting .NET versions 4.5, 4.5.1, 4.5.2 and 4.6, as someone requested it. Old script, needs a rewrite, but I just tacked on support for .NET 4.x (above 4.0 - as per 2015-10-31) for now.
Earlier versions of original script: File:Get-DotNet-Version.ps1.txt.
Earlier versions of DotNetVersionLister module: File:DotNetVersionLister.zip.
If you have Windows Management Framework 5 or higher (WMF 5 is available for Windows 7 and up), you can install my DotNetVersionLister module from the PowerShell gallery, a Microsoft project and online repository for scripts.
To install with WMF 5 and up (to get the latest DotNetVersionLister module version available), simply run this command (requires an internet connection):
Install-Module -Name DotNetVersionLister
Or for your user only (does not require elevation / administrator privileges):
Install-Module -Name DotNetVersionLister -Scope CurrentUser
Get the latest .NET version
"Random" (non-asynchronous) code for getting the latest .NET version in a fairly reliable way using the DotNetVersionLister module.
#$ComputerName = Get-ADGroupMember -Identity SomeComputerObjectGroup | Select-Object -ExpandProperty Name $ComputerName = Get-ADComputer -Filter * -Propert Name | Select-Object -ExpandProperty Name Import-Module -Name DotNetVersionLister -ErrorAction Stop $DotNetVersionsHash = @{} foreach ($Computer in $ComputerName) { $DotNetVersion = Get-DotNetVersion -ComputerName $Computer -PSRemoting -ContinueOnPingFail -NoSummary if ($DotNetVersion -and -not $DotNetVersion.Error) { if ($DotNetVersion.'>=4.x' -match '\S' -and $DotNetVersion.'>=4.x' -notmatch 'error|not installed|universe') { $DotNetVersionsHash[$Computer] = $DotNetVersion.'>=4.x' } elseif ($DotNetVersion.'v4\Client' -eq 'Installed' -or $DotNetVersion.'v4\Full' -eq 'Installed') { $DotNetVersionsHash[$Computer] = '4.0' } elseif ($DotNetVersion.'v3.5' -eq 'Installed') { $DotNetVersionsHash[$Computer] = '3.5' } elseif ($DotNetVersion.'v3.0' -eq 'Installed') { $DotNetVersionsHash[$Computer] = '3.0' } elseif ($DotNetVersion.'v2.0.50727' -eq 'Installed') { $DotNetVersionsHash[$Computer] = '2.0' } else { $DotNetVersionsHash[$Computer] = '<2.0' } } else { $DotNetVersionsHash[$Computer] = 'Error' } } $DotNetVersionsHash.GetEnumerator() | Format-Table -AutoSize
Enabling the Remote Registry Service
I got an inquiry from Anton Ostrovsky, and he had been nice enough to document how to start the remote registry service via sc.exe, by adding a few lines to my script. I took his idea, but feel like this is a separate task entirely, so I put it here in a couple of simple, "throw-away", example scripts at the bottom of the article.
To enable the remote registry service, and set the startup type to automatic, you would ideally use an AD Group Policy (GPO/GP), but if you ghetto it (temporarily), you can use a simple foreach loop that iterates a list of computers, checks the status of the service with Get-Service, and if it's not running, it tries to set the startup type to automatic and then to start it (it seems to get a start signal immediately when you set it to auto).
The scripts were written to be compatible with PowerShell version 2 and up.
The results will look like this for disabling and enabling:
Here's my example Enable-RemoteRegistryService.ps1.txt code. Read it, understand it, and adapt as you see fit, or just run it (you will need to populate the $ComputerName variable).
If the service is already running, it'll look like this:
PS C:\temp> C:\temp\Enable-RemoteRegistryService.ps1 server2012: The RemoteRegistry service is already running. vista64esxi: The RemoteRegistry service is already running. server2008: The RemoteRegistry service is already running.
Here's a similar, but reversed script, for disabling the RemoteRegistry service, if you want to do that after enabling it and then processing the computers with Get-DotNet-Version.ps1: Disable-RemoteRegistryService.ps1.txt.