If you are in process of upgrading from Exchange 2003 to Exchange 2010, you must have read “Upgrade from Exchange 2003 Transport” article on Technet which spells out the details of a requirement – “minor link state updates must be suppressed to make sure that message looping doesn’t occur when a route is recalculated”.

I will not go into details of the requirement, however, if you read the details in the “Suppress Link State Updates” article, you know that the task can be daunting, especially if you have multiple Exchange 2003 servers, possibly in geographically dispersed locations.

Well, sweat no more. I have written a script that will help you get it done easily. Here’s the script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#############################################################################
# Suppress-Linkstate.ps1
# Using static text file or Get-ExchangeServer, this script will configure
# selected Exchange 2003 servers to suppress Link State to aid in upgrade to
# Exchange Server 2010 environment.
#
# Use this script in accordance to the following Technet article
# http://technet.microsoft.com/en-us/library/aa996728.aspx
#
# Created by
# Bhargav Shukla
# https://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.
#############################################################################
 
function Suppress-Linkstate
{
# Create empty results file or overwrite existing file
"Server Name,SuppressStateChanges,ServiceStopState,ServiceStartState" | Out-File .\results.csv -Encoding ASCII
 
	foreach ($Server in $Servers)
	{
		# Do Nothing if $_ is null
		if ($Server -ne $null)
		{
		# Set server to connect to
		#$Server = "$_"
 
		# Read SuppressStateChanges from servers
 
			# Set Registry Key variables
			$REG_KEY = "System\\CurrentControlSet\\Services\\RESvc\\Parameters"
			$VALUE = "SuppressStateChanges"
 
			# 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)
 
			# Set SuppressStateChanges to 1 if key exists
			if ($regKey -ne $null)
			{
			$regKey.Setvalue('SuppressStateChanges', '1', 'Dword')
 
			# Write changes to registry without closing it
			$regKey.Flush()
 
			# Read and Store SuppressStateChanges value in variable
			$SuppressStateChanges = $regkey.getvalue($VALUE)
 
			# Close the Reg Key
			$regKey.Close()
			}
 
		# Restart SMTP service, the Microsoft Exchange Routing Engine service, and the Microsoft Exchange MTA Stacks 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 = 'smtpsvc')","(name = 'resvc')","(name = 'msexchangemta')"
 
 
			$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") {
							$ServiceStopState = "Service $($Service.Name) stopped";
 
							# Store result string in a variable
							$result = "$Server,$SuppressStateChanges,$ServiceStopState,$ServiceStartState"
 
							# Write results to file
							$result | Out-File .\results.csv -Encoding ASCII -Append
 
							$ServiceStopState = $null
 
							break :Checking;
						} Else {
							If (((Get-Date) - $Then).seconds -ge $TimeOut){
								$ServiceStopState = "Service $($Service.Name) stop TIMEOUT";
 
								# Store result string in a variable
								$result = "$Server,$SuppressStateChanges,$ServiceStopState,$ServiceStartState"
 
								# Write results to file
								$result | Out-File .\results.csv -Encoding ASCII -Append
 
								$ServiceStopState = $null
 
								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") {
							$ServiceStartState = "Service $($Service.Name) started";
 
							# Store result string in a variable
							$result = "$Server,$SuppressStateChanges,$ServiceStopState,$ServiceStartState"
 
							# Write results to file
							$result | Out-File .\results.csv -Encoding ASCII -Append
 
							$ServiceStartState = $null
 
							break :Checking;
						} Else {
							If (((Get-Date) - $Then).seconds -ge $TimeOut){
								$ServiceStartState = "Service $($Service.Name) start TIMEOUT";
 
								# Store result string in a variable
								$result = "$Server,$SuppressStateChanges,$ServiceStopState,$ServiceStartState"
 
								# Write results to file
								$result | Out-File .\results.csv -Encoding ASCII -Append
 
								$ServiceStartState = $null
 
								break :Checking;
							}
						}
					} While ($True)
				}
			}
		}
	}
}
 
 
#Add Exchange 2010 snapin if not loaded
# Uncomment following four (4) lines if using dynamic list mentioned below
If ((Get-PSSnapin | where {$_.Name -match "E2010"}) -eq $null)
{
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
}
 
# Uncomment next line if you want to dynamically get Exchange 2003 servers using Exchange 2010 Management Shell
$Servers = (get-exchangeserver | Where-Object {$_.AdminDisplayVersion -like '*6.5*'} | ForEach-Object {$_.Name})
# Comment line above and Uncomment next line if you want to provide list of servers in a text file (one server per line)
# You can't have both of the abovementioned lines uncommented. Please use one of your choice.
# $Servers = Get-Content .\servers.txt
 
$Servers | Suppress-Linkstate

Few important pointers:

  • The script can use one of the two methods, please comment and uncomment necessary lines as documented in script

     – Dynamically retrieve list of Exchange 2003 servers in your environment using Get-ExchangeServer cmdlet from an Exchange 2010 server

     – Use list of Exchange 2003 servers provided in servers.txt file (one server per line)

  • If you choose to use dynamic method, you  must have Exchange server 2010 admin tools installed on the machine where the script is run from
  • The output will be store in results.csv file
  • Timeout value in line 62 works fine in my lab tests, it may not work for you. Please adjust the value as necessary

You can download the script from here: Suppress-Linkstate.ps1

I am sure there are ways to optimize and improve this script. I would love to know how I can make the script better and more useful. Please feel free to drop me a line using contact form. I will be happy to hear your feedback and improve script. You can also use comments option on this post to leave your feedback.