Friday, April 29, 2016

Exporting Messages from Transport Queues

Hi folks,

In this short post I would like to share with you on how do you need to export messages from Exchange 2010 (and later version) to file. Usually it is in eml format (that can be opened by Outlook).

First we need to suspend message and we can achieve it by running command similar to this one:

Get-Message SERVER01\Submission\123456789 |Suspend-Message

After a message has been suspended we export it to eml format by running the following command:

Get-Message SERVER01\Submission\123456789 |Export-Message |AssembleMessage -path D:\ML\123456789.eml

If we have more than one message in the queue we can run the following command against each of them:

Get-Message -Queue Server01\Submission |Suspend-Message

After this we can run the second command against each message that we suspended. Of course identity of each message will vary depending on what it is in real life.

Enjoy!

Friday, April 15, 2016

Reporting AD Sites Used By Exchange

Hi folks,

Last year I was already publishing post about retrieving AD information for Exchange server such as global catalog.

Here are some more codes on retrieving it.

If you're requested to retrieve information about sites which are used by Exchange servers you can use this code (Get-ADSite cmdlet won't return you this information):

Get-ExchangeServer |select Site,Name |sort Site |Export-Csv D:\Scripts\ADSites_W_ExchOnPrem.csv

It will provide information about AD sites and which Exchange servers are installed there. Please note that you will receive the same AD site multiple times because this info is queried from Exchange server.

If you need to retrieve information about AD site links utilized by Exchange let's look into the following scenario. Let's imagine our site name is Atlanta. Since AD site can have more than one link and you may not know all the names you can use wild card sign (*) to find all the possible links that site is using. The code will be something like that:

Get-AdSiteLink *Atlanta* |select Name,AdCost,ExchangeCost,@{N="Sites";E={$_.Sites}} |Export-Csv D:\Scripts\ADSiteLinks.csv -Append

Links with the sites that contain Exchange servers (can be retrieved by the first code) will be those that are used by Exchange to determine route.

Enjoy

Script for Bulk Modification of Remote IP Ranges of Receive Connectors

Hi folks,

I decided to capitalize on this previous post of mine to consider for automatic updating of more than one receive connectors for the Exchange environment. While that post was of good use for small Exchange environment, there are a lot of big Exchange deployments which can benefit from it.

As you well aware that receive connectors are managed on the per-server basis and therefore in order for all the environment to be able to receive emails from one IP address every receive connector should be attended. Manually tweaking each of them may be time consuming for the lazy boys like me.

Let us again imagine that we are in professionally designed environment where names of receive connectors are consistent. Let us imagine that every transport server (mailbox server in 2013/16 environment) is identically configured with a receive connector named "From Internet". To automatically change this receive connector on every server we need to collect information about each of these connectors into a variable. After information is collected we will use play for attribute named RemoteIPRanges to add or remove IP addresses. For addition it will be something like $recCon.RemoteIPRanges +="192.168.0.1" and for removal $recCon.RemoteIPRanges -="192.168.0.1".

Since we are modifying a large number of receive connectors a single error in the code can be fatal and break mail routing. Therefore I have added some visibility to the script by using Write-Host command. In my case it will report which receive connector my script is dealing with and also outputs updated list of IP addresses in the white list thus proving that execution was successful. And it does so for every single receive connector.

The code for adding IP addresses to white list of multiple receive connectors will look as follows:


$recCons= Get-ReceiveConnector | Where {$_.Identity -like "*From Internet*"} |sort Identity

ForEach ($recCon in $recCons) {
               Write-Host "Adding 192.168.0.1 to Remote IP Ranges of " $recCon.Identity
$recCon.RemoteIPRanges +="192.168.0.1"
       Set-ReceiveConnector $recCon -RemoteIPRanges $recCon.RemoteIPRanges
Write-Host "Below is the updated white list for " $recCon.Identity
                Get-ReceiveConnector $recCon |select -ExpandProperty remoteIpranges |select expression |sort Expression
}

And script for removal of the IP address will be something like below:

$recCons= Get-ReceiveConnector | Where {$_.Identity -like "*From Internet*"} |sort Identity

ForEach ($recCon in $recCons) {
               Write-Host "Removing 192.168.0.1 from Remote IP Ranges of " $recCon.Identity
$recCon.RemoteIPRanges -="192.168.0.1"
       Set-ReceiveConnector $recCon -RemoteIPRanges $recCon.RemoteIPRanges
Write-Host "Below is the updated white list for " $recCon.Identity
                Get-ReceiveConnector $recCon |select -ExpandProperty remoteIpranges |select expression |sort Expression
}

Now let's slightly complicate our task and imagine we need to add or remove more than 1 IP address into the remote IP ranges of the receive connector named "From Internet". This article offers a good way of dealing with this problem. To achieve this we specify IP addresses in quotes and separated by comma (,). Our variable for IP addresses will be something like:

$recCon.RemoteIPRanges +="192.168.1.201","192.168..1.202","192.168.1.203"

This will result into the below code for addition of IP addresses


$recCons= Get-ReceiveConnector | Where {$_.Identity -like "*From Internet*"} |sort Identity

ForEach ($recCon in $recCons) {
              Write-Host "Adding new IP Addresses to Remote IP Ranges of " $recCon.Identity
$recCon.RemoteIPRanges +="192.168.1.201","192.168..1.202","192.168.1.203"
      Set-ReceiveConnector $recCon -RemoteIPRanges $recCon.RemoteIPRanges
Write-Host "The updated lis of IP Addresses in Remote IP Ranges of " $recCon.Identity
                Get-ReceiveConnector $recCon |select -ExpandProperty remoteIpranges |select expression |sort Expression
}

When attempting to adopt the same approach for removing of multiple records I got the following error:



And this article came to my help. As a workaround for each address you to update RemoteIpRanges array by configuring variable like this (separately for each IP Address:
$recCon.RemoteIPRanges -="192.168.5.201"
$recCon.RemoteIPRanges -="192.168.5.202"
$recCon.RemoteIPRanges -="192.168.5.203"

As the result my script started looking as below:

$recCons= Get-ReceiveConnector | Where {$_.Identity -like "*From Internet*"} |sort Identity

ForEach ($recCon in $recCons) {Write-Host "Removing new IP Addresses from Remote IP Ranges of " $recCon.Identity
     $recCon.RemoteIPRanges -="192.168.5.201"
                        $recCon.RemoteIPRanges -="192.168.5.202"
                        $recCon.RemoteIPRanges -="192.168.5.203"
                        Set-ReceiveConnector $recCon -RemoteIPRanges $recCon.RemoteIPRanges
    Write-Host "The updated lis of IP Addresses in Remote IP Ranges of " $recCon.Identity
                        Get-ReceiveConnector $recCon |select -ExpandProperty remoteIpranges |select expression |sort Expression
}

This resulted in the successful script execution.

PowerShell is indeed power shell.

Enjoy.



Thursday, April 14, 2016

Adding/Removing IP Addresses To/From Receive Connector's White List

Hi folks,

I would love to share with you code for adding and removing IP address to receive connectors. In the receive connector there is an attribute called RemoteIPRanges. It is a multi-value attribute that contains list of hosts that can send emails to to an Exchange server.

Let's imagine that we need to add IP address of 192.168.164.39 to the remote IP ranges by running  below command all the IP addresses in white list will be overwritten by this only IP address and mail flow will be broken.

Set-ReceiveConnector "EXCH01\Internet Mail" -RemoteIPRanges 192.168.164.39

To do it from PowerShell we can use the code as below:

$Server = $(Get-WmiObject Win32_Computersystem).name
$RecvConn = Get-ReceiveConnector "$Server\Internet Mail"
$RecvConn.RemoteIPRanges += "192.168.164.39"
Set-ReceiveConnector $RecvConn -RemoteIPRanges $RecvConn.RemoteIPRanges

When you want to remove a single IP address you will need to replace in the code $RecvConn.RemoteIPRanges +=  with $RecvConn.RemoteIPRanges -=. As you can easily guess plus is used for addition while minus for removal. Thefore the code for removing the IP address of 192.168.35.169 from receive connector will be something like this one:

$Server = $(Get-WmiObject Win32_Computersystem).name
$RecvConn = Get-ReceiveConnector "$Server\Internet Mail"
$RecvConn.RemoteIPRanges -= "192.168.35.169"
Set-ReceiveConnector $RecvConn -RemoteIPRanges $RecvConn.RemoteIPRanges

After either removal or adding IP address to IP address ranges don't forget to check if your receive connector has been successfully updated. For this purpose you can run the following command (provided that you run one of the previous codes you can use $RecvConn variable do identify your connector):

Get-ReceiveConnector $RecvConn |select -ExpandProperty RemoteIpRanges |select Expression |sort Expression

In the returned list you should see new IP address if you added it or the removed IP address should disappear

Enjoy!

Friday, April 8, 2016

Reporting Receive Connectors in Multi-Site Environment

Hi folks,

In this short post I would love to share with you a script which allowed me to retrieve list of receive connectors.

In my scenario I needed to retrieved list of receive connectors which are located in one of 2 locations. Let's say they are located in San Francisco and New York. I needed to retrieve receive connectors only in San Francisco. If you run Get-ReceiveConnector command you will retrieve all receive connectors in all servers.

I chose to use identity parameter for my filtering. As you know identity of receive connector will be something like Server\Connector Name. Let's imagine that we need front end receive connectors for client connections in San Francisco only and exclude any receive connectors in New York, and also that server names in New York start with NY. As the result I have got the following code (which also drops them to:

Get-ReceiveConnector |Where-Object {$_.Identity -like "*Client Front*" -and $_.Identity -notlike "NY*"} |select Identity,Enabled,@{N="Bindings";E={$_.Bindings}} |Export-Csv c:\temp\SanFrancisco-ReceiveConnectors.csv


Enjoy!

Wednesday, April 6, 2016

SCOM Test Mailboxes and Recovery Databases

Hi Folks,

 As you well know Exchange Monitoring Management Pack can run synthetic transactions to help you measure the performance of monitored objects in your Exchange organization. It does it by running Test-OwaConnectivity, Test-ActiveSyncConnectivity, and Test-WebServicesConnectivity against Exchange and monitoring CAS protocols performance.

For synthetic transactions test mailboxes are used. And MS provides new-TestCasConnectivityUser.ps1 script out of box which allows creation of these mailboxes.

When attempting to create test mailboxes I got an error with the following text:

CreateTestUser : Mailbox could not be created. Verify that OU (Users) exists and that password meets complexity requirements.

I have checked my environment and Users container (default one in domain) was present in the AD environment and password that I used was long enough. However issues still persisted.

There are some good posts on how to have this problem solved. This post advises to use -OU parameter which didn't work for me. This post was suggesting to replace Users value of the $OrganizationalUnit variable to something like contoso.com/Users or DC=Users,DC=contoso,DC=com. Finally, there were articles like this one  which suggests to remove -OrganizationalUnit parameter from the New-Mailbox command which will force mailbox creation in the default Users container. In fact, once in Exchange 2010 environment the last method worked for me perfectly.

Unfortunately none of these approaches, including the last one, helped me. After some of Google-ing I ran into this his article. Following its instruction I found that the problem is with this bit of the code in the script:

new-Mailbox -Name:$UserName -Alias:$UserName -UserPrincipalName:$UserPrincipalName -SamAccountName:$SamAccountName -Password:$SecurePassword -Database:$mailboxDatabaseName -ErrorVariable err -ErrorAction SilentlyContinue

It keeps us blind without actually showing what caused mailbox creation to fail. Therefore, as the article author suggested I removed the 2 parameters -ErrorVariable err -ErrorAction SilentlyContinue and the code became like:

new-Mailbox -Name:$UserName -Alias:$UserName -UserPrincipalName:$UserPrincipalName -SamAccountName:$SamAccountName -Password:$SecurePassword -Database:$mailboxDatabaseName  -OrganizationalUnit:$OrganizationalUnit

By the way, I decided not to touch the original script but rather moved it (new-TestCasConnectivityUser.ps1) and its strings file (new-TestCasConnectivityUser.strings.psd1) to a separate folder (the 2 should be together or script won't run).

Script execution showed me that script attempted to create mailbox in the recovery database:


The root cause of the problem was in this section of the script:


It populates variable named $mailboxDatabaseName with the name of the last database. In my case it was a recovery database.

So I wanted to ensure is that my test mailbox is not provisioned in the recovery DB. You can do 3 things:
- create recovery DB after you create test mailbox
- remove recovery DB and then recreate it
- or do it as the author of the article has suggested, namely giving static value to the $mailboxDatabaseName variable

There's something universal that allows us to bypass recovery databases. When configuring $mailboxDatabaseName variable we need to add the following filter {$_.Recovery -ne "True"}. As the result Get-MailboxDatabase command returns only databases which are non-recovery. As the result I have got this section like below:

$mailboxDatabaseName = $null;
    get-MailboxDatabase -server $mailboxServer |Where-Object {$_.Recovery -ne "True"}| foreach {$mailboxDatabaseName = $_.Guid.ToString()}

After saving and running script my test mailbox was successfully created.

I hope if you ever run in this situation this article will help.

Enjoy!