Get-FrameworkVersion 1.6

In early December of 2017 I posted Get-FrameworkVersion 1.5. The cmdlet’s job was to examine a particular computer for all versions of the Windows .NET Framework installed on that computer. It has the capability of scanning the local computer or a remote computer. Remote computers can be accessed using CIM (with or without DCOM) or Windows RPC (also known as the Remote Registry Service).

This new version adds checking and reporting for BlockNetFramework registry key values. Each version of the .NET Framework can be blocked by creating a registry key value named BlockNetFramework<version> and setting the value data to a non-zero number. For example, BlockNetFramework472 with a value data of DWORD 1 would suppress the installation of .NET Framework 4.7.2. See the image below:

regedit image

Get the script on the TechNet Gallery: Display a list of all .NET Framework Versions installed on a computer.


Follow me on twitter! @EssentialExch

Get-FrameworkVersion

Get-FrameworkVersion.ps1 displays a list of all .NET Framework versions installed on a computer. While other scripts perform similar functionality, those I found were not well-behaved when they found an unknown version. Thus, the genesis for this version. Operation is simple:

PS C:\scripts> .\Get-FrameworkVersion.ps1
v2.0.50727       2.0.50727.4927   SP2
v3.0             3.0.30729.4926   SP2
v3.5             3.5.30729.4926   SP1
v4
		Client           4.7.02556
		Full             4.7.02556
v4.0
		Client           4.0.0.0

Current .NET Framework version: 4.7.1 on Windows 10 (4.7.02556 = release 461308)
PS C:\scripts>

I have posted the script on the TechNet Gallery: Display a list of all .NET Framework Versions installed on a computer.

The script is small enough that you can easily use it inside an Invoke-Command to execute on another computer. I will add native remote functionality in the next version.


Follow me on twitter! @EssentialExch

Determining the Exchange Version – without using Get-ExchangeServer – Update 2013

This is an update of my post Determining the Exchange Version – without using Get-ExchangeServer, from April 25, 2012. Since then, Exchange 2013 has been released! I've had several requests from people who are not PowerShell scripters to update that function. So here it is. I have repeated the text from the prior below.

If you write lots of Exchange scripts (as I do), and have several different versions of Exchange on which you need to run those scripts (as I do); you soon see the need for being able to determine what version of Exchange a particular server is running – and you may need to do this outside of the Exchange Management Shell.

This may be necessary because of different behaviors that are required with the Exchange Management Shell depending on the version of Exchange. It also may be required because the version of Exchange pre-dates the Exchange Management Shell (i.e., Exchange 2003). As an additional rationale, your script may need to load the Exchange Management Shell and cannot do that properly without knowing the version of Exchange that is being targeted (the process differs between Exchange 2007 and Exchange 2010 and Exchange 2013).

Thus, I've written a couple of functions that I wrap in a script to give me that information. Get-ExchangeServer, the function presented in this post, returns a simple object containing the name of the server examined, plus the version of Exchange that is running on that server. That is, in C pseudo-syntax:

struct result {
string Name;
string Version;
}

If the result of the function is $null, then the version could not be determined and the server targeted either does not exist or is (very likely) not running Exchange. If the server does not exist (or the firewall on the server prevents remote management via WMI) then an error is displayed.

The version information about Exchange is stored in the registry of each Exchange server. The script below shows some techniques for accessing the registry to obtain string (reg_sz) and 32-bit short integer (reg_dword) values while using PowerShell. Note that PowerShell has multiple mechanisms for accessing this information, including the so-called Registry Provider. I personally find using the WMI functions to be a bit easier to handle.

You can include these functions directly into your PowerShell profile, or you can dot-source the function on an as-needed basis.

Without further ado, enjoy!

###
### Get-ExchangeVersion
###
### Return the version of the specified Exchange server
###
### 2013-04-11
###	Updated to support Exchange Server 2013 and E16
###

Set-StrictMode -Version 2.0

$HKCR = 2147483648
$HKCU = 2147483649
$HKLM = 2147483650

function RegRead
{
	Param(
		[string]$computer,
		[int64] $hive,
		[string]$keyName,
		[string]$valueName,
		[ref]   $value,
		[string]$type = 'reg_sz'
	)

	[string]$fn = "RegRead:" ## function name

	try {
		$wmi = [wmiclass]"\\$computer\root\default:StdRegProv"
		if( $wmi -eq $null )
		{
			return 1
		}
	}
	catch {
		$error[0]
		write-error "$fn Could not open WMI access to $computer"
		return 1
	}

	switch ( $type )
	{
		'reg_sz'
			{
				$r = $wmi.GetStringValue( $hive, $keyName, $valueName )
				$value.Value = $r.sValue
			}
		'reg_dword'
			{
				$r = $wmi.GetDWORDValue( $hive, $keyName, $valueName )
				$value.Value = $r.uValue
			}
		default
			{
				write-error "$fn Unsupported type: $type"
			}
	}

	$wmi = $null

	return $r.ReturnValue
}

function Get-ExchangeVersion
{
	Param(
		[string]$computer = '.'
	)

	[string]$fn = "Get-ExchangeVersion:" ## function name

	if( $computer -eq '.' -or [String]::IsNullOrEmpty( $computer ) )
	{
		$computer = $env:ComputerName
	}

	## Exchange E16 (assumption!)
	## HKLM\Software\Microsoft\ExchangeServer\v16\Setup
	## MsiProductMajor (DWORD 16)

	## Exchange 2013
	## HKLM\Software\Microsoft\ExchangeServer\v15\Setup
	## MsiProductMajor (DWORD 15)

	## Exchange 2010
	## HKLM\Software\Microsoft\ExchangeServer\v14\Setup
	## MsiProductMajor (DWORD 14)

	## Exchange 2007
	## HKLM\SOFTWARE\Microsoft\Exchange\Setup
	## MsiProductMajor (DWORD 8)

	## Exchange 2003
	## HKLM\SOFTWARE\Microsoft\Exchange\Setup
	## Services Version (DWORD 65)

	$v = 0

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v16\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 16 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = 'E16'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v15\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 15 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2013'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v14\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 14 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2010'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\Exchange\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 8 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2007'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\Exchange\Setup' 'Services Version' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 65 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2003'
		return $obj
	}

	### almost certainly not an Exchange server

	return $null
}

Please follow me on Twitter, @essentialexch

 

Determining the Exchange Version – without using Get-ExchangeServer

If you write lots of Exchange scripts (as I do), and have several different versions of Exchange on which you need to run those scripts (as I do); you soon see the need for being able to determine what version of Exchange a particular server is running – and you may need to do this outside of the Exchange Management Shell.

This may be necessary because of different behaviors that are required with the Exchange Management Shell depending on the version of Exchange. It also may be required because the version of Exchange pre-dates the Exchange Management Shell (i.e., Exchange 2003). As an additional rationale, your script may need to load the Exchange Management Shell and cannot do that properly without knowing the version of Exchange that is being targeted (the process differs between Exchange 2007 and Exchange 2010).

Thus, I've written a couple of functions that I wrap in a script to give me that information. Get-ExchangeServer, the function, returns a simple object containing the name of the server examined, plus the version of Exchange that is running on that server. That is:

struct result {

string Name;

string Version;

}

If the result of the function is $null, then the version could not be determined and the server targeted either does not exist or is (very likely) not running Exchange. If the server does not exist (or the firewall on the server prevents remote management via WMI) then an error is displayed.

The version information about Exchange is stored in the registry of each Exchange server. The script below shows some techniques for accessing the registry to obtain string (reg_sz) and short integer (reg_dword) values while using PowerShell. Note that PowerShell has multiple mechanisms for accessing this information, including the so-called Registry Provider. I personally find using the WMI functions to be a bit easier to handle.

You can include these functions directly into your PowerShell profile, or you can dot-source the function on an as-needed basis.

Without further ado, enjoy!


###
### Get-ExchangeVersion
###
### Return the version of the specified Exchange server
###

Set-StrictMode -Version 2.0

$HKCR = 2147483648
$HKCU = 2147483649
$HKLM = 2147483650

function RegRead
{
	Param(
		[string]$computer,
		[int64] $hive,
		[string]$keyName,
		[string]$valueName,
		[ref]   $value,
		[string]$type = 'reg_sz'
	)

	try {
		$wmi = [wmiclass]"\\$computer\root\default:StdRegProv"
		if( $wmi -eq $null )
		{
			return 1
		}
	}
	catch {
		##$error[0]
		write-error "Could not open WMI access to $computer"
		return 1
	}

	switch ( $type )
	{
		'reg_sz'
			{
				$r = $wmi.GetStringValue( $hive, $keyName, $valueName )
				$value.Value = $r.sValue
			}
		'reg_dword'
			{
				$r = $wmi.GetDWORDValue( $hive, $keyName, $valueName )
				$value.Value = $r.uValue
			}
		default
			{
				write-error "Unsupported type: $type"
			}
	}

	$wmi = $null

	return $r.ReturnValue
}

function Get-ExchangeVersion
{
	Param(
		[string]$computer = '.'
	)

	if( $computer -eq '.' )
	{
		$computer = $env:ComputerName
	}

	## Exchange vNext (assumption!)
	## HKLM\Software\Microsoft\ExchangeServer\v15\Setup
	## MsiProductMajor (DWORD 15)

	## Exchange 2010
	## HKLM\Software\Microsoft\ExchangeServer\v14\Setup
	## MsiProductMajor (DWORD 14)

	## Exchange 2007
	## HKLM\SOFTWARE\Microsoft\Exchange\Setup
	## MsiProductMajor (DWORD 8)

	## Exchange 2003
	## HKLM\SOFTWARE\Microsoft\Exchange\Setup
	## Services Version (DWORD 65)

	$v = 0

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v15\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 15 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = 'vNext'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v14\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 14 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2010'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\Exchange\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 8 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2007'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\Exchange\Setup' 'Services Version' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 65 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2003'
		return $obj
	}

	### almost certainly not an Exchange server

	return $null
}

 

Exchange 2003 FE Servers vs. Server 2008 Active Directory

I spent eight hours over the last two days figuring out an interesting – but weird – problem. Once I figured out the problem, correcting it was a simple matter.

I’ll just give you the problem description and then the solution. Hopefully it will save you more than a little time in the future.

I have a client (one of several in this particular situation) who has an Exchange 2003 Front-End server located in their DMZ. Yes, that’s right, their DMZ. In the “long-ago time”, this was considered to be a favored practice by Microsoft. That, of course, has changed over the years. Now, in 2011, we do not want any domain member servers located in the DMZ. Why is that? Because to install a domain member in a DMZ basically means that you have to make “swiss cheese” out of your firewall. And, effectively, if a DMZ server is compromised it means that your entire Active Directory can be compromised. The entire list of potentially affected ports is shown in KB 832017, and at the end of the day – it basically says that you have to open everything above port 1024 to make it all work.

With all that being said, if you are using Exchange 2003 with Server 2003 Active Directory (by which I mean an Active Directory domain controller hosted on a Windows Server 2003 server), you could get by with a much more limited set of ports (although it still isn’t small!):

DNS (TCP and UDP 53)
Kerberos (TCP and UDP 88)
RPC Endpoint Mapper (TCP 135)
NetBIOS Name Service (TCP 137)
NetBIOS Datagram Service (TCP 138)
NetBIOS Session Service (TCP 139)
LDAP (TCP and UDP 389)
Directory Services (TCP 445)
LDAP Global Catalog (TCP 3268)
Remote Desktop Protocol (TCP 3389)
Active Directory RPC End-Point (AD-RPC-EP)

You should notice that all of these have defined ports except for the last! By definition, the AD-RPC-EP is a random high port.

However, a somewhat surprising fact is that when you run Active Directory on Windows Server 2003 (in other words, you have a domain controller on Server 2003), the AD-RPC-EP is always either port 1025 or 1026!

Starting with Server 2008, this is no longer true. “Port randomization” ensures that ports are allocated at truly random locations.

So… I have a particular client who, as part of their Exchange 2003 to Exchange 2010 upgrade effort decided that they first wanted to upgrade their Active Directory. That was fine by me – there are no documented restrictions regarding the operating system level of domain controllers to be used by Exchange 2003.

The day we changed the last domain controller from Windows Server 2003 to Windows Server 2008 R2, the Exchange FE server stopped working. Oh, $%&#.

Thankfully, the FE server was only being used for Outlook Web Access (OWA). SMTP was injected into the Exchange environment via a Barracuda cluster directly into the internal back-end server environment.

I’m rather hardheaded. So I wanted to figure this out. And, quite frankly, it took awhile. Eventually, it took an examination using portqry and rpcping (you can find out lots of information about these utilities by using Google or Bing and searching for “portqry site:support.microsoft.com” and “rpcping site:support.microsoft.com”). Comparing the results of those to the “access control lists” from the firewall showed me that the firewall always expected a port to be open at TCP 1025 and/or TCP 1026. Neither of these ports were open on Server 2008 R2!

I went back and investigate some other customers who were running Active Directory on Server 2003. On every single one of their servers, the process lsass.exe had either TCP 1025 or TCP 1026 (or both) open.

A google here, a bing there, and I was led to Active Directory Replication Over Firewalls and KB 224196: Restricting Active Directory replication traffic and client RPC traffic to a specific port.

These led me to understand that on Server 2008 and above AD-RPC-EP could happen at any random port – but that there is a way to specify the port that will be used. YAY!

For the following

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters
Registry value: TCP/IP Port
Value type: REG_DWORD
Value data: (available port)

specifying a “value data” of either 1025 or 1026 ensures that Server 2008 and Server 2008 R2 operate as did Server 2003. After setting this value, it does take a reboot of the affected DC/server for the value to take effect.

Once this was done, my client was happy again! I hope this helps you in your migrations…

Until next time…

As always, if there are items you would like me to talk about, please drop me a line and let me know!


Follow me on twitter! : @EssentialExch