Archive for category Exchange 2010

Script to suppress Link State Updates

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
# 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.
#############################################################################
 
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.

Print Friendly
Share

Tags: , , ,

Why can’t I remove a Role Group?

When you try to remove a role group using Remove-RoleGroup cmdlet and you get an error “You don’t have sufficient permissions. This operation can only be performed by a manager of the group.” You have verified that you are member of Organization Management group and should have full permissions to all cmdlets. And you wonder why is the Remove-RoleGroup not working for you!

Well, let’s look at the the error. As it says”…This operation can only be performed by a manager of the group…”. Let’s look at the Role Group:

Get-RoleGroup “Role Group| fl ManagedBy
 
 
Managed By: {Domain/OU/User}

When you look at the output, you will notice that your admin user is not one of the managers of the role group. By default, Remove-RoleGroup will not allow you to remove the role group if you are not a manager of the group. If the group does not have any assigned managers, you will be able to remove the group without any issues.

So how do you remove a role group you are not a manger of and have sufficient permissions (are member of Organization Management group)?

You need to use “BypassSecurityGroupManagerCheck” switch:

Remove-RoleGroup “Role Group” –BypassSecurityGroupmanagerCheck –Confirm:$false

If you use BypassSecurityGroupManagerCheck switch, you must be a member of Organization Management role group or be assigned the Role Management role. Details on TechNet.

Neat isn’t it?

Print Friendly
Share

Tags: ,

PowerShell Variables and scopes

I was helping someone with a profile script. The script is supposed to connect to a remote Exchange 2010 server using PowerShell v2.0 when it is launched.

The script had a function which is called upon when profile is loaded. The function looked like the following:

function connect-remotely()
 
{
      if ($server)
      {
            $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$server/PowerShell/ -Authentication Kerberos
            Import-PSSession $Session
            write-warning 'Do not forget to run Remove-PSSession $Session'
      }
      else
      {
            $server = read-host "Server name"
            connect-remotely $server
      }
}

The $session variable in this instance gets created and populated within the function. The problem, however, is not visible until a bit later. When we were done with our remote session, we tried to remove session using Remove-PSSession $session but encountered an error which indicated that $session has null value.

Why did that happen? That’s where I needed to understand how scopes work. The PowerShell session is created when we launch PowerShell. When the function is executed, a child scope is created. The $session variable is created within child scope. All is fine until after the function completes execution and exits. At this time, $session variable value becomes null since the scope in which it was created is no longer applicable.

But wait, don’t we need the object stored in that variable to remove session successfully? We sure do. So how do we address this?

This can be accomplished using one of the two ways:

1. Define the variable as global. This will make the variable available in all scopes including the one created by function and the parent scopes. Here’s how you can do it:

$Global:Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$server/PowerShell/ -Authentication Kerberos

2. Second option (I like it better) is to define scope. I used New-Variable as describe below when creating $session within function and scoping it to value of 1:

New-Variable -Name Session -Value (New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$server/PowerShell/ -Authentication Kerberos) -Scope 1

When you scope any variable, value 0 means the scope in which it was created. 1 means parent scope, 2 is grand parent and so on. By defining scope of 1, I made the variable available to parent scope of the function which is where I was running Remove-PSSession. As anyone can guess, both methods allowed me to successfully remove session as the variable $session stayed behind populated instead of becoming null.

Hopefully this will help you understand how scope can help in certain situations. For better understanding of scopes, head out to TechNet as it contains lot more information about scopes which is not in scope of this post… get it? ;)

Print Friendly
Share

Tags:

Upgrading Exchange 2007 MCTS / MCITP Certification to Exchange 2010 MCTS / MCITP

If you are like me who had previously earned MCTS or MCITP in Exchange 2007, you can now upgrade your certification to MCTS / MCITP Exchange 2010.

The certifications are as below:

Microsoft Certified Technology Specialist (MCTS): Microsoft Exchange Server 2010, Configuration

Microsoft Certified IT Professional (MCITP): Enterprise Messaging Administrator 2010

To earn your MCTS in Exchange 2010, you will need to pass exam 70-662. The MCTS certification is for Messaging Generalist who is responsible for maintenance and administration of Exchange 2010 servers in an enterprise environment. Don’t let the words get in your way, this exam also counts towards your MCITP certification. You can find skills measured and other details here.

To earn your MCITP in Exchange 2010, you will need to pass exam 70-663. The MCITP certification is for those responsible for the Exchange Messaging environment in an enterprise environment. If you are not a senior administrator or technical lead responsible for Exchange Messaging environment, passing the exam will still earn you MCITP which can help towards fulfilling your aspirations of becoming one. You can find skills measured and other details here.

I would highly recommend that you are familiar with the product and have some experience with the product before attempting these exams. There are many resources that can help you prepare for exam. I highly recommend making Exchange 2010 Documentation on TechNet your #1. It is constantly updated with current information and contains almost every detail you need to know as an IT Pro about the product. I will also recommend that you read Microsoft Exchange Team Blog. The blog is resource of very valuable information about not only Exchange Server 2010 but previous versions as well.

And for my status, I was lucky enough to attempt the exams when they were in beta and I did achieve passing score in both. Call me certifiable!

Good luck to you all who are planning for these exams.

Print Friendly
Share

Tags: , , , ,

What does New-ExchangeCertificate –confirm do?

Depends who is asking.

Let’s assume a scenario where you are trying to create a new self-signed certificate on Exchange 2007 using a script. You run the command “New-Exchange Certificate – Services “IMAP, POP3, IIS, SMTP” –Confirm:$false”. The script stops at a prompt when it tries to confirm overwrite of existing SMTP certificate (because current self-signed certificate is assigned to that function). Since this is breaking your script, you decide to throw in –force to force override of the prompt.

Now you face another error: “Parameter set cannot be resolved using the specified named parameters.

This is because –Force serves different purpose in Exchange 2007. According to TechNet:

Use this parameter switch to overwrite an existing certificate request file that matches the same file path as specified in this cmdlet. By default, this cmdlet will not overwrite existing files.

Unfortunately, there is no way you can override the dreaded SMTP certificate prompt in Exchange 2007 (that I know of).

Now let’s turn our attention to Exchange 2010. Since New-ExchangeCertificate cmdlet does not directly write to a file, –force serves the purpose you expected in previous scenario. According to TechNet:

The Force switch specifies whether to override the confirmation prompt and set the new self-signed certificate as the default certificate for TLS for internal SMTP communication. By default, this cmdlet requires a confirmation before setting the new certificate as the default certificate for TLS encryption of internal SMTP communication.

So in case you were wondering, there you go.

Print Friendly
Share

Tags: , ,

Webcast Series – Upgrade Exchange 2003 to Exchange 2010 – Part 1

Update: I will not be posting future updates or next parts of the series as lot of work is being done by my friend Robert Gillies in his series of articles on Exchange Team Blog feature “Robert’s Rules of exchange”. Sorry for any inconvenience and I hope you will find Robert’s series of articles helpful.

Ever since Exchange 2010 is RTM, I have been advocating to upgrade directly to Exchange 2010 if it makes sense for your environment. As I discussed with administrators who manage Exchange 2003 environments, I received a very positive response from many who expressed their strong interest in skipping Exchange 2007 and upgrade to Exchange 2010.

Since I have had a lab that was designed for this purpose only, I decided to create a webcast series that will walk through the process of upgrading from Exchange 2003 environment to Exchange 2010 environment.

I am publishing Part 1 of multi part series here. I will be publishing more parts as time permits but no less than once a month. You can download the webcast here. The webcast works best when viewed in 1024×768 or higher resolution.

I also request that you provide your  questions, comments and feedback to feedback@bhargavs.com. It will greatly help me improve future webcasts.

I hope this webcast series will help you learn Exchange 2010 upgrade process and you can apply the learning in real-world. Thanks and enjoy!

Print Friendly
Share

Tags: , , ,

Automate Update Rollup installation during Exchange 2010 setup

If you are about to install Exchange 2010, one of the checks in your checklist should be Update Rollup 1 for Exchange 2010. This would come after you have installed Exchange 2010 on your server. But what if I tell you that you can slipstream RU1 with your Exchange setup?

All you have to do is populate a folder with RU installer and use /UpdatesDir switch with setup.com. The example would be setup.com /role:mailbox /UpdatesDir:”C:\ExchangeSources\RUs”. Once this command successfully completes, you will see that the exchange install is complete and your determined patches from the UpdatesDir are applied to the server in single step! Isn’t that a thing of beauty?

Exchange Server Update Rollups are cumulative so you don’t need to have RU1 installer if you build a new server with RU2 in future. All you need is RU2 installer file in UpdatesDir and exchange source to install from.

There are many other useful switches to automate Exchange 2010 setup. you can find the reference on Technet.

Print Friendly
Share

Tags: , ,

Another Podcast from RunAs Radio

Once Again, I had privilege of talking to Richard and Greg from RunAs radio. This time we went deep discussing how Exchange 2010 High Availability featured have changed from previous versions!

I am sure the you will like it. Download the podcast from http://bit.ly/5o3bSv. I welcome feedback and comments.

Print Friendly
Share

Tags: , ,

How do I check Update Rollup version on Exchange 20xx Server?

Instead of updating my previous post which covers only Exchange 2007, I decided to create a new post which covers both versions.

Now that Update Rollup for Exchange Server 2010 is available, I have updated my previous script to check for Update Rollup versions on both Exchange Server 2007 and Exchange Server 2010. No need to have two versions of script. Just download this one!

Here’s what has changed between versions:

  • Product GUID has changed to AE1D439464EB1B8488741FFA028E291C (Exchange 2010) from 461C2B4266EDEF444B864AD6D9E5B613 (Exchange 2007).
  • Exchange writes installation information to HKLM\SOFTWARE\Microsoft\ExchangeServer\v14\Setup instead of “HKLM\SOFTWARE\Microsoft\Exchange\Setup”

The script below will do the work for you so you don’t need to remember what I just said above. Isn’t that what script is for?

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
# Store header in variable
$headerLine =
@"
 
Server Name,Rollup Update Description,Installed Date,ExSetup File Version
"@
 
# Write header to file
$headerLine | Out-File .\results.csv -Encoding ASCII -Append
 
function getRU()
{
# Set server to connect to
	$Server = "$_".ToUpper()
 
 
# Check if server is running Exchange 2007 or Exchange 2010
 
	$ExchVer = (Get-ExchangeServer $Server | ForEach {$_.AdminDisplayVersion.Major})
 
# Set appropriate base path to read Registry
# Exit function if server is not running Exchange 2007 or Exchange 2010
	if ($ExchVer -eq "8" -or $ExchVer -eq "14")
		{
			switch ($ExchVer)
			{
			 "14"	{
			 			$REG_KEY = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\AE1D439464EB1B8488741FFA028E291C\\Patches"
						$Reg_ExSetup = "SOFTWARE\\Microsoft\\ExchangeServer\\v14\\Setup"
			 		}
			 "8"	{
			 			$REG_KEY = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\461C2B4266EDEF444B864AD6D9E5B613\\Patches"
						$Reg_ExSetup = "SOFTWARE\\Microsoft\\Exchange\\Setup"
			 		}
			}
		}
	else
		{return}
 
# Read Rollup Update information from servers
 
# Set Registry constants
	$VALUE1 = "DisplayName"
	$VALUE2 = "Installed"
	$VALUE3 = "MsiInstallPath"
 
# Open remote registry
	$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Server)
 
# Set regKey for MsiInstallPath
	$regKey= $reg.OpenSubKey($REG_ExSetup)
 
# Get Install Path from Registry and replace : with $
	$installPath = ($regkey.getvalue($VALUE3) | foreach {$_ -replace (":","`$")})
 
# Set ExSetup.exe path
	$binFile = "Bin\ExSetup.exe"
 
# Get ExSetup.exe file version
	$exSetupVer = ((Get-Command "\\$Server\$installPath$binFile").FileVersionInfo | ForEach {$_.FileVersion})
 
# Create an array of patch subkeys
	$regKey= $reg.OpenSubKey($REG_KEY).GetSubKeyNames() | ForEach {"$Reg_Key\\$_"}
 
# Walk through patch subkeys and store Rollup Update Description and Installed Date in array variables
	$dispName = [array] ($regkey | %{$reg.OpenSubKey($_).getvalue($VALUE1)})
	$instDate = [array] ($regkey | %{$reg.OpenSubKey($_).getvalue($VALUE2)})
 
# Loop Through array variables and output to a file
	$countmembers = 0
 
	if ($regkey -ne $null)
	{
		while ($countmembers -lt $dispName.Count)
		{
		$server+","+$dispName[$countmembers]+","+$instDate[$countmembers].substring(0,4)+"/"+$instDate[$countmembers].substring(4,2)+"/"+$instDate[$countmembers].substring(6,2)+","+$exsetupver | Out-File .\results.csv -Encoding ASCII -Append
		$countmembers++
		}
	}
	else
	{
		$server+",No Rollup Updates are installed,,"+$exsetupver | Out-File .\results.csv -Encoding ASCII -Append
	}
}
 
# Get Exchange 2007 servers and write Rollup Updates to results file
$Servers = (Get-ExchangeServer | Where-Object {($_.AdminDisplayVersion -match "8" -OR $_.AdminDisplayVersion -match "14") -AND $_.ServerRole -ne "ProvisionedServer" -and $_.ServerRole -ne "Edge"} | ForEach {$_.Name})
$Servers | ForEach {getRU}

Download – Get-ExchangeUpdateRollups.ps1

Print Friendly
Share

Tags:

New Podcast from RunAs Radio

Recently, I had privilege of talking to Richard and Greg from RunAs radio. We discussed the features of the newly released Exchange 2010 and why businesses still running Exchange 2003 should jump right to the latest version and skip 2007 entirely.

I am sure the listeners will benefit from it. You can download the podcast from http://bit.ly/5fSqbp. Comments welcome.

Print Friendly
Share

Tags: , , , ,