Monitor message queues & alert

EMS:


[string]$smtpserver = <HTServer>
[string]$recipient = <e-mail of recipient for alerts>

[string]$hostserver = $env:computername

[string]$sender = $hostname + "@" + ($recipient -split ("@"))[1]
write-host -foregroundcolor Cyan "This script will monitor message queues and create an alert if the queue message count threshold is exceeded"
$threshold = read-host "Specify a value for the threshold"
write-host -foregroundcolor Cyan "The timeout between messages is configurable"
$timeout = read-host "Specify a timeout in seconds"
$mins = $timeout/60
$HTS = (Get-ExchangeServer | ?{$_.IsHubTransportServer -eq "True"})
while($true){foreach ($HT in $HTS){Get-queue -server $HT | ?{$_.MessageCount -gt $threshold -and $_.DeliveryType -ne "ShadowRedundancy"}
$Queue = Get-queue -server $HT | ?{$_.MessageCount -gt $threshold -and $_.DeliveryType -ne "ShadowRedundancy"}
if ($Queue.MessageCount -gt $threshold -and $Queue.DeliveryType -ne "ShadowRedundancy"){write-host -foregroundcolor yellow "Sent mail"; $MSGQueue = $Queue | out-string; Send-MailMessage -To $recipient -From $sender -Subject "Queues alert - threshold=$threshold, timeout to next alert $mins minute(s)" -body "A message queue has triggered an alert: $MSGQueue" -SmtpServer $smtpserver; sleep $timeout}}; write-host -foregroundcolor green "Script running... Timeout="$timeout" second(s), Queue message threshold= "$threshold; sleep 5}

Show Queues

EMS one liner:

$HTS = (Get-ExchangeServer | ?{$_.IsHubTransportServer -eq "True"}); while ($true){foreach ($HT in $HTS){Get-queue -server $HT | ?{$_.MessageCount -gt "0" -and $_.DeliveryType -ne "ShadowRedundancy"}}; write-host -foregroundcolor green "Next..."; sleep 15}

Database Size Limit in GB script

EMS to set the database size limit in the registry

$DBsize = "200"
$loc = get-location
[string]$server = $env:computername

$var1 = "HKLM:\System\CurrentControlSet\services\MSExchangeIS\"

$var2 = "\"
$var3 = "Database Size Limit In GB"
$var4 = “*Database Size Limit In GB*”
$var5 = "\Logstate"

$regkeys = (Get-ChildItem -path $var1$server)


Set-location -path REGISTRY::


foreach ($regkey in $regkeys){

$key = (Get-ItemProperty -path $regkey); if ($key -notlike $var4){New-ItemProperty -path $regkey -name $var3 -value $DBsize -PropertyTYpe "DWord"}
}

Remove-itemProperty $var1$server$Var5 -name $var3 -confirm:$false


set-location -path $loc 


Method not allowed

Method not allowed

Whilst trying to perform DAG tasks in an Exchange 2010 environment (suspending replication, moving an active database copy), I was getting some warnings (in yellow) as follows:

WARNING: The cmdlet extension agent with the index 0 has thrown an exception in OnComplete(). The exception is:
System.Net.WebException: The request failed with HTTP status 405: Method Not Allowed.
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at Microsoft.Exchange.SoapWebClient.CustomSoapHttpClientProtocol.<>c__DisplayClass4.b__3()
at Microsoft.Exchange.SoapWebClient.HttpAuthenticator.NetworkServiceHttpAuthenticator.AuthenticateAndExecute[T](SoapHttpClientProtocol client, AuthenticateAndExecuteHandler`1 handler)
at Microsoft.Exchange.SoapWebClient.SoapHttpClientAuthenticator.AuthenticateAndExecute[T](SoapHttpClientProtocolclient, AuthenticateAndExecuteHandler`1 handler)
at Microsoft.Exchange.SoapWebClient.EWS.ExchangeServiceBinding.FindFolder(FindFolderType FindFolder1)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.EwsMailer.GetAdminAuditLogsFolder(ADUser adUser)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.EwsMailer..ctor(OrganizationId organizationId, ADUser adUser, ExchangePrincipal principal)
at Microsoft.Exchange.ProvisioningAgent.MailboxLoggerFactory.Create(OrganizationId organizationId, ADUser mailbox, ExchangePrincipal principal)
at Microsoft.Exchange.ProvisioningAgent.AdminLogAgentClassFactory.ConfigWrapper.get_MailboxLogger()
at Microsoft.Exchange.ProvisioningAgent.AdminLogProvisioningHandler.OnComplete(Boolean succeeded, Exception e)
at Microsoft.Exchange.Provisioning.ProvisioningLayer.OnComplete(Task task, Boolean succeeded, Exception exception)


They were just warnings, but I still needed to get to the bottom of them as they indicate a problem.

The crux of this is the “The request failed with HTTP status 405: Method Not Allowed” – implying to me an authentication problem. I was suspecting that the hardware load balancers were at fault. I understood that the action that I was doing would have result in Autodiscover being updated (or attempted updating). I examined the authentication types on the Autodiscover virtual directory and tested direct connectivity by tricking my client with a hosts file entry. This ruled out the load balancers as I still got the error. Nothing was amiss with the vdirs or authentication. This was also not limited to one CAS server, thus reducing the likelihood of it being an IIS that simply needed recycling. I had deliberately set the external url on the Autodiscover virtual directory when I installed Exchange 2010 into the environment so that the existing clients didn’t get any impact from an untested service. That testing has been successfully completed, so it is now safe to expose clients to Autodiscover and that is what fixed it.

In my case the above warning message actually meant The external url of the Autodiscover Virtual Directory is blank”. Obvious really, eh? I populated the external url for all the new Exchange 2010 CAS servers and that’s the end of the warnings.


Relay Connectors & multi-valued properties

I’ve been working with Receive connectors, specifically relay connectors.

101 Relay Connectors – A basic Receive Connector can be used to allow hosts to relay internally. By adding an AD Permission, you can allow hosts to relay externally. So why not create two relay connectors?

New-ReceiveConnector -Server -RemoteIPRanges 1.2.3.4 -Name "Allowed to Relay Internally HTS_Server" -AuthMechanism Tls, BasicAuth -PermissionGroups AnonymousUsers -MaxMessageSize 20MB -Bindings 0.0.0.0:25,:::25

New-ReceiveConnector -Server -RemoteIPRanges 6.7.8.9 -Name "Allowed to Relay Externally HTS_Server" -AuthMechanism Tls, BasicAuth -PermissionGroups AnonymousUsers -MaxMessageSize 20MB -Bindings 0.0.0.0:25,:::25

Get-ReceiveConnector | ?{$_.name -like "Allowed to Relay Externally*"} | Add-ADPermission -User "NT AUTHORITY\ANONYMOUS LOGON" -ExtendedRights "ms-Exch-SMTP-Accept-Any-Recipient"


So now we have Relay Connectors, but adding IP Addresses to them is not such a simple task. When you use Set-ReceiveConnector –RemoteIPRanges the existing values get overwritten.

I tried to apply the instruction given in article http://technet.microsoft.com/en-gb/library/bb684908(v=exchg.150).aspx as follows:

Set-ReceiveConnector <RelayConnector> –RemoteIPRanges @{Add=”1.2.3.4”}

It should have worked. But it didn’t :-(

So I tried this instead: http://exchangepedia.com/2007/02/how-to-update-multi-valued-attributes-in-powershell.html as follows:

$var= Get-ReceiveConnector <RelayConnector>; $var.RemoteIPRanges +=”1.2.3.4”; $var | Set-ReceiveConnector "RelayConnector"

And even:

$var= Get-ReceiveConnector <RelayConnector>; Get-Content .\IPs.txt | foreach {$rc.RemoteIPRanges += "$_"}; $var | Set-ReceiveConnector

Still no joy :-(

I don't want to have to do this in EMC. I may have a load of addresses to add. And the trouble with listing them is that the buffer size of the Exchange Management Shell causes the field to be truncated. Even after using Out-String. So what’s the quickest and easiest way to append IP addresses to a receive connector?
By using the ‘View Exchange Management Shell Command Log’ (http://technet.microsoft.com/en-gb/library/ee332355.aspx). If you add one IP address (e.g. 1.2.3.4) then you’ll see the command that was run to add it, listing all the existing values. Copy, paste, massage in notepad and use in EMS to add the additional IP addresses or ranges. E.g.:

Set-ReceiveConnector -Identity 'SERVER01\Allowed to Relay Internally SERVER01' -RemoteIPRanges '1.2.3.4', '2.3.4.5'

And then to list multiple IP addresses or address ranges in a Receive Connector's RemoteIPRanges property:

(Get-ReceiveConnector "RelayConnector").RemoteIPRanges | ft Lowerbound,Upperbound,RangeFormat -AutoSize


Powershell remoting

I always configure my new Exchange servers for remote management and Powershell remoting but also run commands on each server. Sometimes these are commands that I want to run in a disconnected session but the default for MaxShellsPerUser is set to 5, which can cause issues with running remote scripts. The value is by design as per this article (http://blogs.msdn.com/b/powershell/archive/2010/05/03/configuring-wsman-limits.aspx) but if you know what you're doing (and you should do if you're trying to overcome this problem) then raising the value to 25 is not harmful. Unfortunately I couldn't do this remotely (by design to prevent malicious reconfiguration), but it's a simple one liner on each server:

$l = Get-Location; cd WSMan:\localhost\Shell; Set-Item .\MaxShellsPerUser 25; dir; Set-Location $l

If I break that down - assigning the current location to a variable just allows us to return to the same place afterwards; change to WSMan path; modify the value; show some output; return to the original location

Now I can run remote commands without exceeding the MaxShells limit, such as this one for restarting the Exchange Transport service on multiple servers:

$s = new-pssession -computername SERVER1, SERVER2, SERVER3
invoke-command -session $s {$h = Restart-Service MSExchangeTransport}
exit-pssession


More info on Powershell remoting can be found at:
http://technet.microsoft.com/en-us/library/dd819505.aspx

Exchange 2010 bits and pieces

Here are a few bits and pieces for optimizing an Exchange 2010 deployment:

Moving the Transport Database and Queue (e.g. to a fast disk):
cd $exscripts; .\Move-TransportDatabase.ps1 -queueDatabasePath "T:\TransportRoles\Data\Queue\DB" -queueDatabaseLoggingPath "T:\TransportRoles\Data\Queue\Logs"

Note: Do not create the folders! You can create the root folders but NOT the host folders (e.g. In above I mean do NOT create 'DB' or 'Logs' folders)

Configure the Cluster Account environment variables (Local System on Windows 2008 R2) to a dedicated volume:
Set-ItemProperty -path "REGISTRY::\HKEY_USERS\S-1-5-18\Environment" -name TMP -value T:\Temp; Set-ItemProperty -path "REGISTRY::\HKEY_USERS\S-1-5-18\Environment" -name TEMP -value T:\Temp

Notice that the above is using the Set-ItemProperty with the registry and specifically with HKEY_USERS. That wasn’t easy to work out how to target!

Configure Cluster parameters:
Import-module FailoverClusters; (Get-Cluster).SameSubnetThreshold=10; (Get-Cluster).SameSubnetDelay=1000

Configure ClusterLog size:
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" -Name ClusterLogSize -Value 32 -Type DWord



Create mailboxes with EMS

I couldn't find my script for setting up test mailboxes for an Exchange 2010 environment that I am working on, so I had to re-write it. Sometimes it's just quicker to go back to the drawing board!

My scenario is that I have 30 databases and I want to create one mailbox per database. I also want to keep these at the end of the GAL so as not to upset existing users, so I'm naming them 'zztestuser' with a number appended.

So that I can run this quickly without having to input anything, I am first converting a plain text password to a secure string and using it throughout the script. Once it is defined as a variable I can re-use it again and again. So the first two lines do that bit and then it's all quite repetitive. I used Excel with the Concatenate function to put it all together, but the fields in Excel have their limitations, so a mix of Excel and Notepad with the 'replace' feature work well.

Here is my script for creating 30 new mailboxes with one in each database. Copy the code, modify as appropriate and save to a ps1.


$PlainPassword = "P@ssw0rd"
$SecurePassword = $PlainPassword | ConvertTo-SecureString -AsPlainText –Force
New-Mailbox -Name 'zztestuser01' -Alias 'zztestuser01' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser01@domain.com' -SamAccountName 'testuser01' -FirstName 'zztestuser' -Initials '' -LastName '01' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB01'
New-Mailbox -Name 'zztestuser02' -Alias 'zztestuser02' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser02@domain.com' -SamAccountName 'testuser02' -FirstName 'zztestuser' -Initials '' -LastName '02' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB02'
New-Mailbox -Name 'zztestuser03' -Alias 'zztestuser03' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser03@domain.com' -SamAccountName 'testuser03' -FirstName 'zztestuser' -Initials '' -LastName '03' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB03'
New-Mailbox -Name 'zztestuser04' -Alias 'zztestuser04' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser04@domain.com' -SamAccountName 'testuser04' -FirstName 'zztestuser' -Initials '' -LastName '04' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB04'
New-Mailbox -Name 'zztestuser05' -Alias 'zztestuser05' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser05@domain.com' -SamAccountName 'testuser05' -FirstName 'zztestuser' -Initials '' -LastName '05' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB05'
New-Mailbox -Name 'zztestuser06' -Alias 'zztestuser06' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser06@domain.com' -SamAccountName 'testuser06' -FirstName 'zztestuser' -Initials '' -LastName '06' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB06'
New-Mailbox -Name 'zztestuser07' -Alias 'zztestuser07' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser07@domain.com' -SamAccountName 'testuser07' -FirstName 'zztestuser' -Initials '' -LastName '07' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB07'
New-Mailbox -Name 'zztestuser08' -Alias 'zztestuser08' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser08@domain.com' -SamAccountName 'testuser08' -FirstName 'zztestuser' -Initials '' -LastName '08' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB08'
New-Mailbox -Name 'zztestuser09' -Alias 'zztestuser09' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser09@domain.com' -SamAccountName 'testuser09' -FirstName 'zztestuser' -Initials '' -LastName '09' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB09'
New-Mailbox -Name 'zztestuser10' -Alias 'zztestuser10' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser10@domain.com' -SamAccountName 'testuser10' -FirstName 'zztestuser' -Initials '' -LastName '10' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB10'
New-Mailbox -Name 'zztestuser11' -Alias 'zztestuser11' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser11@domain.com' -SamAccountName 'testuser11' -FirstName 'zztestuser' -Initials '' -LastName '11' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB11'
New-Mailbox -Name 'zztestuser12' -Alias 'zztestuser12' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser12@domain.com' -SamAccountName 'testuser12' -FirstName 'zztestuser' -Initials '' -LastName '12' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB12'
New-Mailbox -Name 'zztestuser13' -Alias 'zztestuser13' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser13@domain.com' -SamAccountName 'testuser13' -FirstName 'zztestuser' -Initials '' -LastName '13' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB13'
New-Mailbox -Name 'zztestuser14' -Alias 'zztestuser14' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser14@domain.com' -SamAccountName 'testuser14' -FirstName 'zztestuser' -Initials '' -LastName '14' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB14'
New-Mailbox -Name 'zztestuser15' -Alias 'zztestuser15' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser15@domain.com' -SamAccountName 'testuser15' -FirstName 'zztestuser' -Initials '' -LastName '15' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB15'
New-Mailbox -Name 'zztestuser16' -Alias 'zztestuser16' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser16@domain.com' -SamAccountName 'testuser16' -FirstName 'zztestuser' -Initials '' -LastName '16' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB16'
New-Mailbox -Name 'zztestuser17' -Alias 'zztestuser17' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser17@domain.com' -SamAccountName 'testuser17' -FirstName 'zztestuser' -Initials '' -LastName '17' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB17'
New-Mailbox -Name 'zztestuser18' -Alias 'zztestuser18' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser18@domain.com' -SamAccountName 'testuser18' -FirstName 'zztestuser' -Initials '' -LastName '18' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB18'
New-Mailbox -Name 'zztestuser19' -Alias 'zztestuser19' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser19@domain.com' -SamAccountName 'testuser19' -FirstName 'zztestuser' -Initials '' -LastName '19' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB19'
New-Mailbox -Name 'zztestuser20' -Alias 'zztestuser20' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser20@domain.com' -SamAccountName 'testuser20' -FirstName 'zztestuser' -Initials '' -LastName '20' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB20'
New-Mailbox -Name 'zztestuser21' -Alias 'zztestuser21' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser21@domain.com' -SamAccountName 'testuser21' -FirstName 'zztestuser' -Initials '' -LastName '21' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB21'
New-Mailbox -Name 'zztestuser22' -Alias 'zztestuser22' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser22@domain.com' -SamAccountName 'testuser22' -FirstName 'zztestuser' -Initials '' -LastName '22' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB22'
New-Mailbox -Name 'zztestuser23' -Alias 'zztestuser23' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser23@domain.com' -SamAccountName 'testuser23' -FirstName 'zztestuser' -Initials '' -LastName '23' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB23'
New-Mailbox -Name 'zztestuser24' -Alias 'zztestuser24' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser24@domain.com' -SamAccountName 'testuser24' -FirstName 'zztestuser' -Initials '' -LastName '24' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB24'
New-Mailbox -Name 'zztestuser25' -Alias 'zztestuser25' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser25@domain.com' -SamAccountName 'testuser25' -FirstName 'zztestuser' -Initials '' -LastName '25' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB25'
New-Mailbox -Name 'zztestuser26' -Alias 'zztestuser26' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser26@domain.com' -SamAccountName 'testuser26' -FirstName 'zztestuser' -Initials '' -LastName '26' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB26'
New-Mailbox -Name 'zztestuser27' -Alias 'zztestuser27' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser27@domain.com' -SamAccountName 'testuser27' -FirstName 'zztestuser' -Initials '' -LastName '27' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB27'
New-Mailbox -Name 'zztestuser28' -Alias 'zztestuser28' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser28@domain.com' -SamAccountName 'testuser28' -FirstName 'zztestuser' -Initials '' -LastName '28' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB28'
New-Mailbox -Name 'zztestuser29' -Alias 'zztestuser29' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser29@domain.com' -SamAccountName 'testuser29' -FirstName 'zztestuser' -Initials '' -LastName '29' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB29'
New-Mailbox -Name 'zztestuser30' -Alias 'zztestuser30' -OrganizationalUnit 'domain.com/Users' -UserPrincipalName 'zztestuser30@domain.com' -SamAccountName 'testuser30' -FirstName 'zztestuser' -Initials '' -LastName '30' -Password $SecurePassword -ResetPasswordOnNextLogon $false -Database 'DB30'


Exchange and Adobe iFilters

*N.B. this is an old article and may no longer be relevant*

(Note: This article refers to Exchange 2007 and Exchange 2010)

The full set of IFilters installed with Exchange out of the box include Microsoft Office documents (e.g. doc/docx, xls/xlsx, ppt/pptx), Email message formats (e.g. msg, eml), HTML and text formats. These are used on Hub Transport servers (when filtering attachments using Transport Rules) and Mailbox servers (for indexing attachments).
Third Party iFilters can be added and as per http://blogs.technet.com/b/exchange/archive/2009/05/11/3407435.aspx, Adobe PDF is one of the most common.
Adobe provide the iFilters as a downloadable, installable package: http://www.adobe.com/support/downloads/detail.jsp?ftpID=4025
which also needs registering. The following article summarizes how to do that: http:// marksmith.netrends.com/Lists/Posts/Post.aspx?List=d0ef1a62%2D8e97%2D484a%2D9053%2D7acda05534cb&ID=93&Web=2dee96c1%2D5fed%2D439e%2Db530%2D37626608d03e. However, it doesn’t quite go far enough. There are some steps to do afterwards and two of them involve modifying registry permissions.

I decided that these were two steps that could be automated by Powershell, so set about doing that. The method used is straight off of Technet; that is, using get-acl to ingest the current permissions and then set-acl to add to them. The code for setting Read access for the Network Service on the two relevant keys for the Adobe iFilters is as follows:

$rule = New-Object System.Security.AccessControl.RegistryAccessRule ("NT AUTHORITY\NETWORK SERVICE","ReadKey","Allow")
$acl1 = Get-Acl "HKLM:\SOFTWARE\Microsoft\ExchangeServer\V14\MSSearch\CLSID\{E8978DA6-047F-4E3D-9C78-CDBE46041603}"
$acl1.SetAccessRule($rule)
$acl1 |Set-Acl -Path "HKLM:\SOFTWARE\Microsoft\ExchangeServer\V14\MSSearch\CLSID\{E8978DA6-047F-4E3D-9C78-CDBE46041603}"
$acl2 = Get-Acl "HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\MSSearch\Filters\.pdf"
$acl2.SetAccessRule($rule)
$acl2 |Set-Acl -Path "HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\MSSearch\Filters\.pdf"


You can save the above to a script and run it after registering the Adobe iFilters or you can incorporate it into one script (enhancing the one by Mark E. Smith) so that the full script looks like this:



# save as Register-Adobe-PDF-filter.ps1
#
# Download installation from: http://www.adobe.com/support/downloads/detail.jsp?ftpID=4025

# Refer to: http://blogs.technet.com/b/exchange/archive/2009/05/11/3407435.aspx

# Adobe iFilter Directory Path
$iFilterDirName = "C:\Program Files\Adobe\Adobe PDF IFilter 9 for 64-bit platforms\bin"

# Get the original path environment variable
$original = (Get-ItemProperty "HKLM:SYSTEM\CurrentControlSet\Control\Session Manager\Environment" Path).Path
# Add the ifilter path
Set-ItemProperty "HKLM:SYSTEM\CurrentControlSet\Control\Session Manager\Environment" Path -value ( $original + ";" + $iFilterDirName )

$CLSIDKey = "HKLM:\SOFTWARE\Microsoft\ExchangeServer\V14\MSSearch\CLSID"
$FiltersKey = "HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\MSSearch\Filters"

# Filter DLL Locations
$pdfFilterLocation = “PDFFilter.dll"

# Filter GUIDs
$PDFGuid ="{E8978DA6-047F-4E3D-9C78-CDBE46041603}"

# Create CLSIDs
Write-Host "Creating CLSIDs..."

New-Item -Path $CLSIDKey -Name $PDFGuid -Value $pdfFilterLocation -Type String

# Set Threading model
Write-Host "Setting threading model..."

New-ItemProperty -Path "$CLSIDKey\$PDFGuid" -Name "ThreadingModel" -Value "Both" -Type String

# Set Flags
Write-Host "Setting Flags..."
New-ItemProperty -Path "$CLSIDKey\$PDFGuid" -Name "Flags" -Value "1" -Type Dword

# Create Filter Entries
Write-Host "Creating Filter Entries..."

# These are the entries for commonly exchange formats
New-Item -Path $FiltersKey -Name ".pdf" -Value $PDFGuid -Type String

$rule = New-Object System.Security.AccessControl.RegistryAccessRule ("NT AUTHORITY\NETWORK SERVICE","ReadKey","Allow")
$acl1 = Get-Acl "HKLM:\SOFTWARE\Microsoft\ExchangeServer\V14\MSSearch\CLSID\{E8978DA6-047F-4E3D-9C78-CDBE46041603}"
$acl1.SetAccessRule($rule)
$acl1 |Set-Acl -Path "HKLM:\SOFTWARE\Microsoft\ExchangeServer\V14\MSSearch\CLSID\{E8978DA6-047F-4E3D-9C78-CDBE46041603}"
$acl2 = Get-Acl "HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\MSSearch\Filters\.pdf"
$acl2.SetAccessRule($rule)
$acl2 |Set-Acl -Path "HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\MSSearch\Filters\.pdf"

Write-Host -foregroundcolor Green "The NETWORK SERVICE has been granted read access to the following registry keys:`n$CLSIDKey\$PDFGuid`n$FiltersKey\.pdf"
Write-Host " "
Write-Host -foregroundcolor Yellow "Next, reboot the Exchange Server
- after which you need to rebuild the search indexes using the precanned script:"
Write-Host " "
Write-Host -foregroundcolor White "cd $exscripts; .\ResetSearchIndex.ps1 –Force –All"
Write-Host " "
Write-Host -foregroundcolor Green "Then wait for the indexes to be rebuilt before initiating a search.
Repeat this process on each Mailbox Server and Hub Transport in the organization"
Write-Host " "
Write-Host -foregroundcolor Yellow "Note: The permissions on the keys above for NETWORK SERVICE are required on the Hub Transport role (or multi-roled server that hosts the Hub role) so that transport rules can do PDF attachment filtering"




Windows Disk Timeouts (Ex2010)

*N.B. this is an old article and may no longer be relevant*

As per the following article, MS make recommendations regarding storage for the Exchange 2010 MBX role:

http://technet.microsoft.com/en-us/library/ee832792(v=exchg.141).aspx

This post pertains specifically to the recommendation for the disk timeout value. By default this is set at 60 seconds. For DAS or SAN this should be modified to 20 seconds. I have created a one liner to do this:

Set-ItemProperty -path HKLM:\SYSTEM\CurrentControlSet\services\Disk -name TimeOutValue -value 20

More info on the topic can be found here:
http://blogs.msdn.com/b/san/archive/2011/09/01/the-windows-disk-timeout-value-understanding-why-this-should-be-set-to-a-small-value.aspx

Note that the information is omitted from the Exchange 2013 version of the Technet article:

http://technet.microsoft.com/en-us/library/ee832792.aspx

Don't ask me why. I can't see that it wouldn't still be relevant. If you're looking at this in reference to Exchange 2013, read the blog and make up your own mind. Personally, I would still set it to 20 seconds in the absence of specific advice.

Wow. Is it only five to four? I've got another five minutes of work. Time for a coffee...