Script to configure static ports on Exchange Server 2010

Posted by in Exchange 2010, PowerShell

This post is depricated. Please use newer version of script and read more details here: http://www.bhargavs.com/index.php/2011/10/21/script-to-configure-static-ports-on-exchange-server-2010-2/

If you are planning to implement or are implementing Exchange Server 2010, you may have already noticed that with new changes introduced in this version we highly recommend that you load balance your CAS servers using hardware load balancer. The client connections are not mediated through RPC Client Access service on CAS and that’s why load balancing CAS is very important.

Now if you know how Outlook clients work with Exchange RPC/MAPI, you may as well be aware that it uses random high ports for connection to the Exchange servers. This may not work well when you try to configure your hardware load balancer device. There are many other reasons why you may decide that you would rather use a static port or two for services that need it. It also makes it easy to configure network devices to work with few ports than a whole upper range of high ports.

To that matter, TechNet article “Load Balancing Requirements of Exchange Protocols” has done a great job documenting how you can configure static ports on CAS and Mailbox servers. Henrik Walther has also provided some more details on TechNet wiki article “Configuring Static RPC Ports on an Exchange 2010 Client Access Server”.

Now only if you had the script to do it all for you so you can enjoy doing other things that may be more important or interesting to you… :)

Well, that’s why I have created this script… just for YOU!

So, how do I run the script?

There are few ways you can run it. One is to just call the script with your server name.

& 'c:\scripts\Set-StaticPorts.ps1' -Server Server1

This will change RPC and Address Book ports on the server you specify.

If you want to change many servers, store your servers in a variable and pipe it. Use whichever method works for you to create the array variable with servers you need. One example is to use text file containing one server per line:

$Servers = Get-Content c:\scripts\servers.txt
$Servers | Foreach {& 'c:\scripts\Set-StaticPorts.ps1' -Server $_}

The script can be run remotely as long as you have proper permissions on the server to change registry and exchange configuration files.

So far, in examples above, we did not specify which ports to use for each service. The script accommodates for this by having default values 7575 and 7576 for RPC Service and Address Book Service respectively. But what if you want to specify your own ports? Run:

& 'c:\scripts\Set-StaticPorts.ps1' -Server Server1 -rpcport "50000" -abport "50001"

This will allow you to define your own ports. Just ensure you are not using well known ports or ports in use by other services in your environment to avoid any conflict or confusion.

The script is noisy as you may notice when you run it. It is intentional. It will prompt for your confirmation when changing ports if they are already changed before. It will ask for confirmation before restarting services. Now you may say, I am running the script and I know what I am doing. Well, let the force be with you. –force parameter I mean!

& 'c:\scripts\Set-StaticPorts.ps1' -Server Server1 -rpcport "50000' -abport "50001" –force $true

And this will silcence prompting. It will still write output for each server to the host. You can redirect it to a file as well!

 

& 'c:\scripts\Set-StaticPorts.ps1' -Server Server1 -rpcport "50000" -abport "50001" –force $true | out-file c:\scripts\results.txt

So, where is this script? You can download it here: Set-StaticPorts.ps1

If you are still reading, here’s the actual script to refer:

# FUTURE, Update to run with -auto to find cas servers and run automatically
# FUTURE, Update to rung with -auto to find PF stores and run on PF host
#############################################################################
# Set-StaticPorts.ps1
# This script will configure static ports for RPC Client Access and
# Address Book Service on Exchange 2010 CAS servers.
#
# Usage syntax:
# Set-StaticPorts [-Server]  [-rpcport]  [-abport]  [-force] 
#
# Usage Examples:
# Set-StaticPorts -Server Server1 -rpcport "50000" -abport "50001" -force $true
# Set-StaticPorts -Server Server1
#
# Use this script in accordance to the following Technet wiki article:
# http://social.technet.microsoft.com/wiki/contents/articles/configuring-static-rpc-ports-on-an-exchange-2010-client-access-server.aspx
#
# Created by
# Bhargav Shukla
# http://blogs.technet.com/bshukla
# http://www.bhargavs.com
#
# DISCLAIMER
# ==========
# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
# RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#############################################################################

# Declare Parameters with defaults
param([string]$server,[string]$rpcport = '7575',[string]$abport = '7576',[bool]$force = $false)

# Filter to set tcp-port, for avoiding redundancy in the code that follows
function tcp-port
{
	if (($regkey.getvalue($VALUE)) -eq $null)
	{
		# Create / Set TCP/IP Port key and value
		$regKey.Setvalue('TCP/IP Port', "$rpcport", 'Dword')

		# Make changes effective immediately
		$regKey.Flush()

		# Read and store current value
		$global:TCPPort = $regkey.getvalue($VALUE)	

		# Close registry key
		$regKey.Close()

		Write-Host -ForegroundColor Green "TCP/IP Port for RPC Client Access is set to $TCPPort on server $server."
	}
	else
	{
		if (($regkey.getvalue($VALUE)) -ne $rpcport)
		{
			$rpccurrport = $regkey.getvalue($VALUE)
			If (($force) -or (Read-Host "Change the RPC Client Service port from $rpccurrport to $rpcport ? [Y/N]") -eq "Y")
			{
				# Create / Set TCP/IP Port key and value
				$regKey.Setvalue('TCP/IP Port', "$rpcport", 'Dword')

				# Make changes effective immediately
				$regKey.Flush()

				# Read and store current value
				$global:TCPPort = $regkey.getvalue($VALUE)	

				# Close registry key
				$regKey.Close()

				Write-Host -ForegroundColor Green "TCP/IP Port for RPC Client Access is set to $TCPPort on server $server."
			}
			else
			{
				Write-Host -ForegroundColor Green "No changes to RPC Client Service port are made on server $server."
			}
		}
		else
		{
			Write-Host -ForegroundColor Green "No changes to RPC Client Service port are necessary on server $server."
		}
	}
}

function get-installpath
{
	# Set Exchange base key and value to read
	$Reg_ExSetup = "SOFTWARE\\Microsoft\\ExchangeServer\\v14\\Setup"
	$VALUE = "MsiInstallPath"

	# Set regKey for MsiInstallPath
	$regKey= $reg.OpenSubKey($REG_ExSetup)

	# Get Install Path from Registry and replace : with $
	$installPath = ($regkey.getvalue($VALUE) | foreach {$_ -replace (":","`$")})

	# Set Address Book Service config file path
	$global:ABFile = "\\$Server\$installPath"+"Bin\microsoft.exchange.addressbook.service.exe.config"

	# Close registry key
	$regKey.Close()
}

function restart-services
{
	# Restart Microsoft Exchange RPC Client Access and Microsoft Exchange Address Book service
	#### You must specify a timeout (in seconds) or the script could potentially never end
	$TimeOut = 30

	#### This will stop a single service on all servers in sequence
	$ServiceFilters = "(name = 'msexchangerpc')","(name = 'msexchangeab')"

	$Locator = new-object -com "WbemScripting.SWbemLocator"
	$WMI = $Locator.ConnectServer($Server, "root\cimv2")
	# Stop Service and check for timeout or sucessful stop
		$ServiceFilters | %{
			$ThisFilter = $_
			(Get-WmiObject -Class Win32_Service -ComputerName $Server -filter "$ThisFilter AND state='running'") | %{
				$Service = $_
				$Refresher = new-object -comobject "WbemScripting.SWbemRefresher"
				$FreshObject = $Refresher.Add($WMI,$Service.__RELPATH)
				$Refresher.Refresh()
				$Then = Get-Date
				:Checking Do {
					$Service.StopService() | out-null
					$Refresher.Refresh()
					if (($FreshObject.Object.properties_ | ?{$_.name -eq "state"}).value -eq "Stopped")
					{
						Write-Warning "Service $($Service.Name) is stopped on server $Server."
						break :Checking;
					} Else {
						If (((Get-Date) - $Then).seconds -ge $TimeOut)
						{
							Write-Warning "Service $($Service.Name) timed out while trying to stop on server $Server. Please restart service manually."
							break :Checking;
						}
					}
				} While ($True)
			}
		}

	# Start Service and check for timeout or sucessful start
		$ServiceFilters | %{
			$ThisFilter = $_
			(Get-WmiObject -Class Win32_Service -ComputerName $Server -filter "$ThisFilter AND state='Stopped'") | %{
				$Service = $_
				$Refresher = new-object -comobject "WbemScripting.SWbemRefresher"
				$FreshObject = $Refresher.Add($WMI,$Service.__RELPATH)
				$Refresher.Refresh()
				$Then = Get-Date
				:Checking Do {
					$Service.StartService() | out-null
					$Refresher.Refresh()
					if (($FreshObject.Object.properties_ | ?{$_.name -eq "state"}).value -eq "running")
					{
						Write-Host "Service $($Service.Name) started successfully on server $Server"
						break :Checking;
					} Else {
						If (((Get-Date) - $Then).seconds -ge $TimeOut)
						{
							Write-Warning "Service $($Service.Name) timed out while trying to start on server $Server. Please restart service manually."
							break :Checking;
						}
					}
				} While ($True)
			}
		}
}

If ($server)
{
	# Set Registry Key variables
	$REG_BASE = "System\\CurrentControlSet\\Services\\MSExchangeRPC"
	$REG_KEY = "System\\CurrentControlSet\\Services\\MSExchangeRPC\\ParametersSystem"
	$VALUE = 'TCP/IP Port'

	# Open remote registry
	$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Server)

	# Open the targeted remote registry key/subkey as read/write
	$regKey = $reg.OpenSubKey($REG_KEY,$true)

	# Create/Modify Key and Values as necessary
	if ($regKey)
	{
	tcp-port
	}
	else
	{
	$regBase = $reg.OpenSubKey($REG_BASE,$true)
	$regBase.CreateSubKey('ParametersSystem')
	$regBase.Close()
	# Open the targeted remote registry key/subkey as read/write
	$regKey = $reg.OpenSubKey($REG_KEY,$true)
	tcp-port
	}

	# Get location of Address Book Service configuration file
	get-installpath

	# Verify the file exists, exit if it doesn't
	if (Test-Path $ABFile -ErrorAction SilentlyContinue)
	{
		# Backup file before editing
		$currentDate = (get-date).tostring("MM_dd_yyyy-hh_mm_s")
		$ABbackup = $ABFile + ".$currentDate"
		$xml = [xml](get-content $ABFile)
		$xml.Save($ABbackup)

		# Edit RpcTcpPort if 0, ask user for approval if not 0
		$root = $xml.get_DocumentElement();
		ForEach ($item in $root.appSettings.add)
		{
		if (($item.key -eq "RpcTcpPort") -and ($item.value -eq "0"))
			{
				$item.value="$abport"
				Write-Host -ForegroundColor Green "TCP/IP Port for Address Book Service is set to $ABPort on server $server."
			}
			else
			{
			If (($item.key -eq "RpcTcpPort") -and ($item.value -ne "$abport"))
				{
				if (($force) -or (Read-Host "Change port from $($item.value) to $abport on server $server ? [Y/N]") -eq "Y")
					{
						$item.value="$abport"
						Write-Host -ForegroundColor Green "TCP/IP Port for Address Book Service is set to $ABPort on server $server."
					}
				else
					{
						Write-Host -ForegroundColor Green "No changes are made to TCP/IP Port for Address Book Service on server $server."
					}
				}
			}
		}
		$xml.Save($ABFile)
	}
	else
	{
	Write-Host -ForegroundColor Red -BackgroundColor Black "Address Book Service configuration file does not exist for server $server. Please verify file and update manually."
	}

	# Ask for and restart services if requested
	If (($force) -or (Read-Host "Restart services MSExchangeRPC and MSExchangeAB? [Y/N]") -eq "Y")
		{
			restart-services
		}
		else
		{
			Write-Warning "Please restart the Microsoft Exchange RPC Client Access (MSExchangeRPC) service for changes to take effect."
		}

	# Reminder for MAilbox Servers
	Write-Warning "Please do not forget to change ports on Mailbox servers for Public Folder Access."
}
else
{
Write-Host -ForegroundColor Red -BackgroundColor Black "Server Name is null. Please provide Server where static ports need to be set."
Write-Host -ForegroundColor Red -BackgroundColor Black "The syntax is 'Set-StaticPorts.ps1 `"servername`" `"1234`" `"1235`"'."
Write-Host -ForegroundColor Red -BackgroundColor Black "Please replace values of `"1234`" and `"1235`" with valid port numbers for RPC and Address Book Services respectively."
}

This is a cross post from http://blogs.technet.com/bshukla