Get your net together

Scripts for .NET in Windows

Windows is quite chatty when it comes to installed software. Well, as long as it follows standard installation procedures. So you might mean you could use Windows features to get a list of installed .Net frameworks. Nope. Or use Get-Hotfix or alike. Nope again. Or at least a some kind of WMI or PowerShell command. And again: Nope.
But, hey. Who needs build-in tools?

There are some website helping to build the right tool you should look at. Also to update the version numbers:

Support Ending for the .NET Framework 4, 4.5 and 4.5.1
.NET Framework Versions and Dependencies
How to: Determine Which .NET Framework Versions Are Installed

As you can read, you have to look into two different locations for the right version.

Before version 4:

$dotNetRegistry = 'SOFTWAREMicrosoftNET Framework SetupNDP'

Since version 4

$dotNet4Registry = 'SOFTWAREMicrosoftNET Framework SetupNDPv4Full'

Now, since version 4, each published version has its own release number. So we create a hash table of known versions

$dotNet4Builds = @{
30319  = '.NET Framework 4.0'
378389 = '.NET Framework 4.5'
378675 = '.NET Framework 4.5.1 (8.1/2012R2)'
378758 = '.NET Framework 4.5.1 (8/7 SP1/Vista SP2)'
379893 = '.NET Framework 4.5.2'
393295 = '.NET Framework 4.6 (Windows 10)'
393297 = '.NET Framework 4.6 (NON Windows 10)'
394254 = '.NET Framework 4.6.1 (Windows 10)'
394271 = '.NET Framework 4.6.1 (NON Windows 10)'
394747 = '.NET Framework 4.6.2 Preview (Windows 10 Insider Preview Build 14295)'
394748 = '.NET Framework 4.6.2 Preview (NON Windows 10)'
}

Using [Microsoft.Win32.RegistryKey] allows to access remote machines. The example uses the $env:COMPUTERNAME environment variable instead.

$regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $env:COMPUTERNAME)

Opening the registry hive before version .Net 4

$netRegKey = $regKey.OpenSubKey("$dotNetRegistry")

A little loop through the subkeys

foreach ($versionKeyName in $netRegKey.GetSubKeyNames()) {
[...]
}

taking only versions before .Net 4 (v1,v2,v3),

$versionKeyName -match '^v[123]'

reading the version key and casting the version number.

$versionKey = $netRegKey.OpenSubKey($versionKeyName)
[version]($versionKey.GetValue('Version', ''))

Nicely wrapped in a PSObject send to the pipeline

New-Object -TypeName PSObject -Property @{
ComputerName = $_
NetFXBuild = $version.Build
NetFXVersion = '.NET Framework ' + $version.Major + '.' + $version.Minor

.Net Version 4 uses release numbers which we stored in a hashtable above.
So we open the subkey for version 4,

$net4RegKey = $regKey.OpenSubKey("$dotNet4Registry")

verify if it does have a release number (if not asserting it is release 30319)

if(-not ($net4Release = $net4RegKey.GetValue('Release'))) {
$net4Release = 30319
}

and wrapping it again in a PSObject send to the pipeline.

New-Object -TypeName PSObject -Property @{
ComputerName = $_
NetFXBuild = $net4Release
NetFXVersion = $dotNet4Builds[$net4Release]
}

Here the complete script including a little help and version object so you can use it to test for mimimum version.
<#
.SYNOPSIS
List installed .Net Framework versions

.DESCRIPTION
List installed .Net Framework versions.
Latest version known to this function is .NET Framework 4.6.2 Preview

.EXAMPLE
Get-DotNetFrameworkVersion

.EXAMPLE
Get-DotNetFrameworkVersion Server1, Server2

.EXAMPLE
"Server1", "Server2" | Get-DotNetFrameworkVersion

.OUTPUTS
[PSObject]

.NOTES
NAME:      Get-DotNetFrameworkVersion
AUTHOR:    Patrick Sczepanski
VERSION:   20160804

.LINK
http://poshcode.org/6468
https://www.redtoo.com/ch/blog/get-your-net-together/

#>
param(
# One or more computer to get installed framework versions.
[parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string[]]
$Computer = $env:COMPUTERNAME
)

# Support Ending for the .NET Framework 4, 4.5 and 4.5.1
# https://blogs.msdn.microsoft.com/dotnet/2015/12/09/support-ending-for-the-net-framework-4-4-5-and-4-5-1/
# https://msdn.microsoft.com/en-us/library/bb822049%28v=vs.110%29.aspx
# https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx
begin {
$dotNetRegistry  = 'SOFTWAREMicrosoftNET Framework SetupNDP'
$dotNet4Registry = 'SOFTWAREMicrosoftNET Framework SetupNDPv4Full'
$dotNet4Builds = @{
30319  = '.NET Framework 4.0'
378389 = '.NET Framework 4.5'
378675 = '.NET Framework 4.5.1 (8.1/2012R2)'
378758 = '.NET Framework 4.5.1 (8/7 SP1/Vista SP2)'
379893 = '.NET Framework 4.5.2'
393295 = '.NET Framework 4.6 (Windows 10)'
393297 = '.NET Framework 4.6 (NON Windows 10)'
394254 = '.NET Framework 4.6.1 (Windows 10)'
394271 = '.NET Framework 4.6.1 (NON Windows 10)'
394747 = '.NET Framework 4.6.2 Preview (Windows 10 Insider Preview Build 14295)'
394748 = '.NET Framework 4.6.2 Preview (NON Windows 10)'
}
$dotNet4Versions = @{
30319  = '4.0'
378389 = '4.5'
378675 = '4.5.1'
378758 = '4.5.1'
379893 = '4.5.2'
393295 = '4.6'
393297 = '4.6'
394254 = '4.6.1'
394271 = '4.6.1'
394747 = '4.6.2'
394748 = '4.6.2'
}
}
process {
$Computer |
Foreach-Object {

if($regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $_)) {

if ($netRegKey = $regKey.OpenSubKey("$dotNetRegistry")) {
foreach ($versionKeyName in $netRegKey.GetSubKeyNames()) {
if ($versionKeyName -match '^v[123]') {
$versionKey = $netRegKey.OpenSubKey($versionKeyName)
$version = [version]($versionKey.GetValue('Version', ''))
New-Object -TypeName PSObject -Property @{
ComputerName = $_
NetFXBuild = $version.Build
NetFXVersion = '.NET Framework ' + $version.Major + '.' + $version.Minor
VersionNumber = $version
}
}
}
}

if ($net4RegKey = $regKey.OpenSubKey("$dotNet4Registry")) {
if(-not ($net4Release = $net4RegKey.GetValue('Release'))) {
$net4Release = 30319
}
New-Object -TypeName PSObject -Property @{
ComputerName = $_
NetFXBuild = $net4Release
NetFXVersion = $dotNet4Builds[$net4Release]
VersionNumber = [version]("{0}.{1}" -f $dotNet4Versions[$net4Release], $net4Release)
}
}
}
}
}