Home » Posts tagged 'exchange'

Tag Archives: exchange

Advertisements

Copying Receive Connectors Hither and Yon

If you have done a number of Exchange migrations, or have a large number of servers to migrate in a single migration, I am sure you have run into the pain of replicating the receive connectors to the new server. Lots of settings to copy down and move over plus there is the headache of explicit permissions granted on the connector in the case of relays or other special use connectors. That can waste a lot of time that you would much rather spend on the finale of Sherlock season 3. Let’s see if we can simplify that today with this script for Copy-ReceiveConnector. You call the script as follows:

Copy-Receive Connector -SourceConnector "EXCHANGE\Alternate Receive Connector" -DestinationServer NEWEXCHANGE -DomainController dc01

This will create a new receive connector on the destination server with all of the settings specified on the old receive connector. It will then loop through all of the non-inherited permissions on the connector and copy those over. You can also specify a new name for the connector via -Name. Onto the code.

<# .SYNOPSIS Copy-ReceiveConnector - Copies a receive connector from a source server to a  destination server .DESCRIPTION Takes the source receive connector a creates a copy on the destination server with values populated from the source receive connector. .PARAMETER SourceConnector Identity of the source receive connector .PARAMETER DestinationServer Server name of the destination Exchange server .PARAMETER DomainController Target domain controller for setting the configuration .PARAMETER Name Optional new name for the connector .EXAMPLE Copy-Receive Connector -SourceConnector "EXCHANGE\Alternate Receive Connector"  -DestinationServer NEWEXCHANGE -DomainController dc01 #>
[CmdletBinding()]
param(
[Parameter(Mandatory=$True)][string]$SourceConnector,
[Parameter(Mandatory=$True)][string]$DestinationServer,
[Parameter(Mandatory=$True)][string]$DomainController,
[Parameter(Mandatory=$False)][string]$Name
)
Import-Module ActiveDirectory
# Get the values for the old connector
$Source = Get-ReceiveConnector -Identity $SourceConnector
# Update the name if specified
if($Name)
{
 $Source.Name = $Name
}
# Custom permission group is not allowed in Exchange 2013 so we need to remove it
# Nothing to be concerned about since the ACEs are explicitly copied over.
$TempArray = @($Source.PermissionGroups) -split ", " | Select-String -Pattern "Custom" -NotMatch
$TempString = "$($TempArray)"
$Source.PermissionGroups = $TempString.Replace(" ", ", ")
# Copy all the values over to create the new connector on the 2013 server
New-ReceiveConnector -Bindings $Source.Bindings -Server $DestinationServer -DomainController $DomainController -Name $Source.Name -RemoteIPRanges $Source.RemoteIPRanges -AdvertiseClientSettings $Source.AdvertiseClientSettings -AuthMechanism $Source.AuthMechanism -Banner $Source.Banner -BinaryMimeEnabled $Source.BinaryMimeEnabled -ChunkingEnabled $Source.ChunkingEnabled -Comment $Source.Comment -ConnectionInactivityTimeout $Source.ConnectionInactivityTimeout -ConnectionTimeout $Source.ConnectionTimeout -DefaultDomain $Source.DefaultDomain -DeliveryStatusNotificationEnabled $Source.DeliveryStatusNotificationEnabled -DomainSecureEnabled $Source.DomainSecureEnabled -EightBitMimeEnabled $Source.EightBitMimeEnabled -EnableAuthGSSAPI $Source.EnableAuthGSSAPI -Enabled $Source.Enabled -EnhancedStatusCodesEnabled $Source.EnhancedStatusCodesEnabled -ExtendedProtectionPolicy $Source.ExtendedProtectionPolicy -Fqdn $Source.Fqdn -LongAddressesEnabled $Source.LongAddressesEnabled -MaxAcknowledgementDelay $Source.MaxAcknowledgementDelay -MaxHeaderSize $Source.MaxHeaderSize -MaxHopCount $Source.MaxHopCount -MaxInboundConnection $Source.MaxInboundConnection -MaxInboundConnectionPercentagePerSource $Source.MaxInboundConnectionPercentagePerSource -MaxInboundConnectionPerSource $Source.MaxInboundConnectionPerSource -MaxLocalHopCount $Source.MaxLocalHopCount -MaxLogonFailures $Source.MaxLogonFailures -MaxMessageSize $Source.MaxMessageSize -MaxProtocolErrors $Source.MaxProtocolErrors -MaxRecipientsPerMessage $Source.MaxRecipientsPerMessage -MessageRateLimit $Source.MessageRateLimit -MessageRateSource $Source.MessageRateSource -PermissionGroups $Source.PermissionGroups -PipeliningEnabled $Source.PipeliningEnabled -ProtocolLoggingLevel $Source.ProtocolLoggingLevel -RequireEHLODomain $Source.RequireEHLODomain -RequireTLS $Source.RequireTLS -ServiceDiscoveryFqdn $Source.ServiceDiscoveryFqdn -SizeEnabled $Source.SizeEnabled -SuppressXAnonymousTls $Source.SuppressXAnonymousTls -TarpitInterval $Source.TarpitInterval -TlsDomainCapabilities $Source.TlsDomainCapabilities -TransportRole $Source.TransportRole
# Next we need to copy over all of the explicity created permissions
$ConnectorPermissions = Get-ReceiveConnector -Identity $SourceConnector | Get-ADPermission | where {$_.IsInherited -eq $false}
$ConnectorPermissions | foreach {
 Get-ReceiveConnector "$($DestinationServer)\$($Source.Name)" | Add-ADPermission -DomainController $DomainController -User $_.User -Deny:$_.Deny -AccessRights $_.AccessRights -ExtendedRights $_.ExtendedRights
}

And as a bonus here’s a script for just copying over the permissions configured on a connector, in case you wanted to roll your own connector but didn’t want to spend the time on redefining all of the permissions. Usage is not quite the same as above as you are just specifying a source and destination connector.

Copy-ReceiveConnectorPermissions -SourceConnector "EXCHANGE\Alternate Receive Connector" -DestinationConnector "NEWEXCHANGE\New Receive Connector"
<# .SYNOPSIS Copy-ReceiveConnectorPermissions - Copies the permissions from the source  connector to the destination connector .DESCRIPTION Takes the source receive connector, retrieves all of the explicitly defined  permissions, then applies them to the destination receive connector .PARAMETER SourceConnector Identity of the source receive connector .PARAMETER DestinationConnector Identity of the destination receive connector .EXAMPLE Copy-Receive Connector -SourceConnector "EXCHANGE\Alternate Receive Connector"  -DestinationConnector "NEWEXCHANGE\New Receive Connector" #>
[CmdletBinding()]
param(
[Parameter(Mandatory=$True)][string]$SourceConnector,
[Parameter(Mandatory=$True)][string]$DestinationConnector
)
Import-Module ActiveDirectory
# We need to copy over all of the explicity created permissions
$ConnectorPermissions = Get-ReceiveConnector -Identity $SourceConnector | Get-ADPermission | where {$_.IsInherited -eq $false}
$ConnectorPermissions | foreach {
 Get-ReceiveConnector "$($DestinationConnector)" -DomainController $DomainController | Add-ADPermission -User $_.User -Deny:$_.Deny -AccessRights $_.AccessRights -ExtendedRights $_.ExtendedRights
}
Advertisements

A Mass Contact Conversion Experience

This may save you a fair bit of trouble in the future. I was working on a client with an Exchange 2007 server that was overrun with contacts. That’s not necessarily a problem in and of itself, but these contacts had served their purpose and now needed to be turned into mail enabled users. They already had a number of duplicates between AD users and contacts and it was causing a lot of trouble in their SharePoint hierarchy. Fortunately PowerShell can step in and save the day! Or would you really enjoy manually copying over all of the contact’s profile data to an AD user? Not my idea of fun.

Here’s the code. You’ll want to tweak it to your purposes of course. I wanted some clear on the screen results as it progressed and also have an HTML report that I can reference afterwards. This marked my first venture in LINQ with moderate success. I will have to revisit things later to get it working exactly how I want.

<#
.SYNOPSIS
Reads in a CSV of contacts or mail enabled users and moves the contacts to mail enabled users or updates
the current mail enabled users

.DESCRIPTION
Convert-CSVContactsToUsers reads in the CSV specified in the command line. This CSV contains a list
of users that either currently have a contact which needs to be converted to a mail enabled
user, have both a contact and a user that is not mail enabled which needs the info from the contact
copied over to the user which is then mail enabled, or have neither a contact or a user and need
to have a mail enabled user created for them.

.PARAMETER FilePath
The path and filename to the CSV containing the required users.

.PARAMETER DomainController
The NetBIOS or FQDN for the target DC to use.

.PARAMETER ResultsFile
The filename to save the HTML formatted results to.

.EXAMPLE
Convert-CSVContactstoUsers -FilePath .\UserList.csv -DomainController dc01 -ResultsFile .\Results.html

.NOTES
Require Server 2008 R2 and should be run from the EMS.
.NET 3.5 and above required for HTML output.
Appropriate domain and Exchange rights are required.

Revision History
v1.0 - Initial release
v1.1 - Added X500 address, moved status report
#>

[CmdletBinding()]
param(
[Parameter(Mandatory=$True)][string]$FilePath,
[Parameter(Mandatory=$True)][string]$DomainController,
[Parameter(Mandatory=$False)][string]$ResultsFile
)

Import-Module ActiveDirectory

#$ErrorActionPreference= 'silentlycontinue'
$CSVList = Import-CSV -Path "$($FilePath)"
$Password = ConvertTo-SecureString -String "expPassword!!" -AsPlainText -Force

# Create a custom table for storing the results
$ResultsTable = New-Object System.Data.DataTable "Conversion Results"
$column01 = New-Object System.Data.DataColumn User
$column02 = New-Object System.Data.DataColumn Result

$ResultsTable.columns.add($column01)
$ResultsTable.columns.add($column02)

# Loop through each CSV entry, check for object existence
# then process the object based on existence and type
foreach($TargetUser in $CSVList)
{
# Check for existence of any objects
$ADContact = Get-ADObject -LdapFilter "(&(CN=$($TargetUser.Name))(ObjectClass=contact))" -SearchScope Subtree -Properties * -Server $DomainController
$ADUser = Get-ADObject -LdapFilter "(&(CN=$($TargetUser.Name))(ObjectClass=user))" -SearchScope Subtree -Properties * -Server $DomainController
$ResultsRow = $ResultsTable.NewRow()
$ResultsRow.User = $TargetUser.Name
[string]$Status = $null

# If both contact and user exist, copy the info from the contact
# into the user's properties, remove the contact, then mail enable the user
if($ADContact -and $ADUser)
{
# First copy over any of the current profile details

if($ADContact.directReports)
{
$ADUser.directReports = $ADContact.directReports
}
if($ADContact.homePhone)
{
$ADUser.homePhone = $ADContact.homePhone
}
if($ADContact.facsimileTelephoneNumber)
{
$ADUser.facsimileTelephoneNumber = $ADContact.facsimileTelephoneNumber
}
if($ADContact.l)
{
$ADUser.l = $ADContact.l
}
if($ADContact.manager)
{
$ADUser.manager = $ADContact.manager
}
if($ADContact.mobile)
{
$ADUser.mobile = $ADContact.mobile
}
if($ADContact.physicalDeliveryOfficeName)
{
$ADUser.physicalDeliveryOfficeName = $ADContact.physicalDeliveryOfficeName
}
if($ADContact.postalCode)
{
$ADUser.postalCode = $ADContact.postalCode
}
if($ADContact.sn)
{
$ADUser.sn = $ADContact.sn
}
if($ADContact.st)
{
$ADUser.st = $ADContact.st
}
if($ADContact.streetAddress)
{
$ADUser.streetAddress = $ADContact.streetAddress
}
if($ADContact.telephoneNumber)
{
$ADUser.telephoneNumber = $ADContact.telephoneNumber
}
if($ADContact.title)
{
$ADUser.title = $ADContact.title
}
if($ADContact.department)
{
$ADUser.department = $ADContact.department
}
if($ADContact.Description)
{
$ADUser.Description = $ADContact.Description
}
if($ADContact.c)
{
$ADUser.c = $ADContact.c
}
if($ADContact.co)
{
$ADUser.co = $ADContact.co
}
if($ADContact.countryCode)
{
$ADUser.countryCode = $ADContact.countryCode
}
if($ADContact.info)
{
$ADUser.info = $ADContact.info
}
if($ADContact.initials)
{
$ADUser.initials = $ADContact.initials
}
if($ADContact.ipPhone)
{
$ADUser.ipPhone = $ADContact.ipPhone
}
if($ADContact.pager)
{
$ADUser.pager = $ADContact.pager
}
if($ADContact.wWWHomePage)
{
$ADUser.wWWHomePage = $ADContact.wWWHomePage
}
if($ADContact.postOfficeBox)
{
$ADUser.postOfficeBox = $ADContact.postOfficeBox
}
if($ADContact.company)
{
$ADUser.company = $ADContact.company
}

# Update the user with the current info
Set-ADObject -Instance $ADUser -Server $DomainController

# Loop through the groups and add the user to them
foreach($ADGroup in $ADContact.memberOf)
{
Add-ADGroupMember -Identity "$($ADGroup)" -Members $ADUser -Server $DomainController
}

# Next, remove the contact
Remove-MailContact -Identity $ADContact.DistinguishedName -Confirm:$false -DomainController $DomainController

# Enable the current user, then copy over the remaining attributes
Enable-MailUser -Identity $ADUser.DistinguishedName -ExternalEmailAddress $ADContact.mail -Alias $ADContact.mailNickname -DomainController $DomainController | Out-Null

# Add the X500 address if the contact was stamped with a legacyExchangeDN
if($ADContact.legacyExchangeDN)
{
$X500User = Get-MailUser -Identity $ADUser.DistinguishedName -DomainController $DomainController
$X500User.EmailAddresses += [Microsoft.Exchange.Data.CustomProxyAddress]("X500:$($ADContact.legacyExchangeDN)")

Set-MailUser -Identity $X500User -EmailAddresses $X500User.EmailAddresses -DomainController $DomainController
}

Write-Host "$($TargetUser.Name) converted."
$Status = "Success"
}
elseif($ADContact)
{
# First remove the contact
Remove-MailContact -Identity $ADContact.DistinguishedName -Confirm:$false -DomainController $DomainController

# Create the mail enabled user
New-MailUser -Name "$($TargetUser.Name)" -ExternalEmailAddress $ADContact.mail -Alias $ADContact.mailNickname -UserPrincipalName "$($ADContact.mailNickname)@domain.local" -Password $Password -ResetPasswordOnNextLogon:$true -DomainController $DomainController | Out-Null

# Then copy in the attributes
$ADUser = Get-ADObject -LdapFilter "(&(CN=$($TargetUser.Name))(ObjectClass=user))" -SearchScope Subtree -Properties * -Server $DomainController
$ADUser.directReports = $ADContact.directReports
$ADUser.homePhone = $ADContact.homePhone
$ADUser.facsimileTelephoneNumber = $ADContact.facsimileTelephoneNumber
$ADUser.l = $ADContact.l
$ADUser.manager = $ADContact.manager
$ADUser.mobile = $ADContact.mobile
$ADUser.physicalDeliveryOfficeName = $ADContact.physicalDeliveryOfficeName
$ADUser.postalCode = $ADContact.postalCode
$ADUser.sn = $ADContact.sn
$ADUser.st = $ADContact.st
$ADUser.streetAddress = $ADContact.streetAddress
$ADUser.telephoneNumber = $ADContact.telephoneNumber
$ADUser.title = $ADContact.title
$ADUser.department = $ADContact.department
$ADUser.Description = $ADContact.Description
$ADUser.c = $ADContact.c
$ADUser.co = $ADContact.co
$ADUser.countryCode = $ADContact.countryCode
$ADUser.info = $ADContact.info
$ADUser.initials = $ADContact.initials
$ADUser.ipPhone = $ADContact.ipPhone
$ADUser.pager = $ADContact.pager
$ADUser.wWWHomePage = $ADContact.wWWHomePage
$ADUser.postOfficeBox = $ADContact.postOfficeBox
$ADUser.company = $ADContact.company
# Copying over the X500 address if it exists
if($ADContact.legacyExchangeDN)
{
$X500User = Get-MailUser -Identity $ADUser.DistinguishedName -DomainController $DomainController
$X500User.EmailAddresses += [Microsoft.Exchange.Data.CustomProxyAddress]("X500:$($ADContact.legacyExchangeDN)")

Set-MailUser -Identity $X500User -EmailAddresses $X500User.EmailAddresses -DomainController $DomainController
}

Set-ADObject -Instance $ADUser -Server $DomainController

# Loop through the groups and add the user to them
foreach($ADGroup in $ADContact.memberOf)
{
Add-ADGroupMember -Identity "$($ADGroup)" -Members $ADUser -Server $DomainController
}

Write-Host -ForegroundColor Yellow "$($TargetUser.Name) created."
$Status = "Success"
}
elseif($ADUser)
{
# Only a user is found
Write-Host -ForegroundColor Cyan "$($TargetUser.Name) already exists."
$Status = "Exists"
}
else
{
Write-Host -ForegroundColor Magenta "$($TargetUser.Name) not found!"
$Status = "Failed"
}

# Update the results
$ResultsRow.Result = $Status

# Clear the variables to prevent false positives on the next loop
$ADContact = $null
$ADUser = $null
$Status = $null

# Update the results table
$ResultsTable.Rows.Add($ResultsRow)
}

# Check if HTML results should be written out
if($ResultsFile)
{
# Build the style sheet for the page
$Style = "<style>"
$Style = $Style + "body{font-family: `"Century Gothic`"; font-size: 10pt;}"
$Style = $Style + "table{border-width: 1px; border-style: solid; border-color black; border-collapse: collapse; }"
$Style = $Style + "th{border-width: 1px; border-style: solid; border-color: black; background-color: #CBFEFF; }"
$Style = $Style + "td{border-width: 1px; border-style: solid; border-color: black; text-align: center}"
$Style = $Style + "</style>"

# LINQ will be used for easier custom formatting
Add-Type -AssemblyName System.Xml.Linq

# Convert the desired columns into HTML and convert it to XML
$xml = [System.Xml.Linq.XDocument]::Parse("$($ResultsTable | Select User,Result | ConvertTo-Html -Head $Style)")

# Define the namespace
if($Namespace = $xml.Root.Attribute("xmlns").Value)
{
$Namespace = "{{{0}}}" -f $Namespace
}

#
# $xml.Descendants().Value is not returning the values for some reason here
# Have to resort to alternate index discovery
#
#$wsIndex = [Array]::IndexOf($xml.Descendants("${Namespace}th").Value, "Result")
[Array]$xmlArray = $xml.Descendants("${Namespace}th")
for($i=0;$i -le $xmlArray.length-1;$i++)
{
#We're color coding the Results column
if($xmlArray[$i] -match "Result")
{
$wsIndex = $i
}
}

# Loop through each row and assign a text color and background color to the result
foreach($row in $xml.Descendants("${Namespace}tr"))
{

switch(@($row.Descendants("${Namespace}td"))[$wsIndex])
{
{"Success" -eq $_.Value} {$_.SetAttributeValue("style", "color: #006600; background-color: #F1FEFE;"); continue}
{"Failed" -eq $_.Value} {$_.SetAttributeValue("style", "color: #660000; background-color: #F1FEFE;"); continue }
{"Exists" -eq $_.Value} {$_.SetAttributevalue("style", "color: #4D6600; background-color: #F1FEFE;"); continue }
}
}

# Save to the desired file
$xml.Save("$pwd/$($ResultsFile)")
}

Use safely!

Easy Ways to find your Mail Enabled Public Folders

It looks like some of you are wanting to easily find your mail enabled public folders. Definitely something good to know, especially when you are planning out a migration or are being thrown into a new and probably fragile environment. The documentation is never up to date of course so you have to dig it out yourself. Here comes PowerShell to rescue you from manually slogging through it all!

$Servers = Get-PublicFolderDatabase
foreach($TargetServer in $Servers)
{
    Get-PublicFolder -Recurse -Server $TargetServer.Server | where {$_.MailEnabled -eq $true}
}

This will grab all of the mail enabled public folders from all of the servers in your organization. But in case you need to dig in further, say for instance if you are needing to dig into the AD objects for fixing things like missing homeMDB entries or other woes. Or even just plain documentation. This one liner will do it for you.

Get-ADObject -SearchBase ( "CN=Microsoft Exchange System Objects," + (Get-ADRootDSE).rootDomainNamingContext) -SearchScope OneLevel -Filter { (mail -like "*") -and (ObjectClass -eq "publicFolder")} | select Name

I hope this helps out a few of you out there.

5 Extremely Useful Tools to Simplify your Microsoft Exchange Life

Here’s what I find myself using in my day to day life in working with Exchange. If it weren’t for these then troubleshooting and automation would be a lot more difficult and I would find myself throwing my life away. Why waste time when you could be watching E3 game trailers instead?

  1. PowerShell! This one definitely has to take the first place spot in mention. Not that I have any particular order to how I’m listing everything. If it wasn’t for PowerShell then management of even a single Exchange server would be much more tiresome. Just look at mass creating a number of new users. HR sends you an Excel sheet with all of their details, you save the pertinent bits out to a CSV, then just run it through a little PowerShell script
    $NewUsers = Import-Csv -Path C:\Import\UserList.csv
    foreach($NewUser in $NewUsers)
    {
                    New-Mailbox -Name $NewUser.Name -Password $NewUser.Password -UserPrincipalName $NewUser.UPN -Alias $NewUser.Alias -Database $NewUser.TargetDatabase -DisplayName ($NewUser.FirstName + " " +$NewUser.LastName) -FirstName $NewUser.FirstName -LastName $NewUser.LastName -OrganizationalUnit $NewUser.OU -Phone $NewUser.Phone -ResetPasswordOnNextLogon:$true
    }

    Tada, creating new users has been simplified from hours of manual labor to a few minutes of CSV formatting and scripted importing.

  1. mxtoolbox.com This is a site that gets used often in troubleshooting. I can quickly check on the MX records for any domain including my own, run through a list of BLs to see if my domain is listed, and very importantly run diagnostics on my mail server’s external facing connectors to see what errors may come up. This is where I turn if I don’t have a way to telnet in from the outside. There are a number of other useful tools there as well though they don’t receive as much use as the BL and diagnostics.
  2. testexchangeconnectivity.com A very important site if you are running Exchange migrations. The ActiveSync and Outlook Anywhere tests help greatly for verifying all of your autodiscover functionality and your other CAS services. The Lync tests are great as well if you have that as part of your organization. It is so much easier than having to call up some external user and have them test such and such functionality, again.
  3. migrationwiz.com Speaking of Exchange migrations, I’ve found this particular service to be extremely useful when working with migrations that are not Exchange to Exchange. MigrationWiz will login to the target service and transfer mailboxes to your Exchange server. It is as simple as that. No annoying process of exporting and import PSTs. Furthermore since incremental transfers are available you can spread out your migration without needing to do an overnight cutover.
  4. telnet and nslookup Since these are small I figured I could combine them into one entry. These are also some extremely vital tools in your Microsoft Exchange swiss army knife. I’ve already talked about the usefulness of telnet in previous posts so I won’t bore you all over again. Nslookup is a fantastic way to quickly verify records. Such as comparing your internal DNS records to your external records. Run nslookup autodiscover.contoso.com and nslookup autodiscover.contoso.com 8.8.8.8. The first will check what it is resolving to internally while the second will query Google’s public DNS for the external autodiscover record. MX record lookups are simple as well, nslookup –type=mx contoso.com 8.8.8.8.

Any favorite tools that you find yourself using over and over for your Exchange servers or migrations? Please let me know in the comments.

The Welcome News of the Death of SBS

SBSIntoTheNight

SBS wander off into the black abyss

Somehow I missed this bit of news last year. The Death of the Small Business Server. Possibly because I haven’t had to deal with SBS in a serious manner for a while now. Anyhow from the point of view of an Exchange administrator it is welcome news to see that SBS 2011 is the end of the line for the SBS. I have always found SBS to be a pain to work with. The migration wizards were prone to breaking in mysterious ways. The POP3 connector was horrid to troubleshoot and would still choke on a seemingly normal email all too easily. The most annoying thing was being forced to use the wizards for most things you do, and to suffer the consequences if someone else did not use a wizard. For small businesses there don’t seem to be any genuine alternatives unfortunately. Several Linux based alternatives are presented over at The VAR Guy but I don’t see a full Linux solution being a comfortable route, at least for most of the small businesses I’ve worked with in the past. There are a few odd ones out that embrace any alternatives to Microsoft of course. Windows Server 2012 Essentials and Zimbra Collaberation Server virtual appliance may be a better compromise for MSPs that are reluctant to touch a full Linux alternative for an on-site Exchange substitute. For me I’ll just be happy that I won’t have to worry about Exchange 2013 shoehorned into an SBS.

452 4.3.1 Insufficient System Resources – Continued Telnet Training

This is a problem that crops up fairly often if you have a lot of disparate Exchange servers out there without a solid monitoring solution in place. Very common for MSPs. Oh, and actually have somebody paying attention to those monitoring alerts. Nobody likes paying attention to monitoring alerts. There are reams of rules dedicated to keeping them out of sight in Outlook clients around the world. But that makes for an entirely separate topic/rant. The symptoms of this problem are that you’ll be getting reports from the end users that they don’t seem to be receiving any email, or at least any external email. But oddly enough sending out email is working just fine.

This is the point where a quick telnet test will focus you in on what is going on really fast. Continuing with what you learned from the post on Essential Exchange Troubleshooting – Send Email via Telnet you will want to telnet into the server from outside the organization. You may immediately get a response of:

452 4.3.1 Insufficient System Resources

But more likely you’ll receive a typical SMTP banner such as

220 myserver.contoso.com Microsoft ESMTP MAIL Service ready at Mon, 27 May 2013 08:19:44 -0700

If so then I recommend that you continue through with sending in an email via telnet. The next likely place that you’ll encounter this error is when you issue the RCPT TO: command to which you receive a response of

452 4.3.1 Insufficient System Resources

The fix for this is fairly simple. Check your Exchange server for low disk space usage on the partition where your queues reside, which will most likely be the partition with your Exchange installation. I find that most often what has eaten all of your space, in cases of single server Exchange 2007/2010 installations, is the IIS log files. When setting up your Exchange server it is a good idea to make sure that you have an archiving/recycling policy in place for your IIS logs to keep them from swallowing the entire partition over time. BES installations have the same problem as well with log files swallowing the drive.

The key phrase that you’ll want to keep in mind with this is “back pressure.” In a later post I’ll delve into this term.

More to the topic on hand, here’s an extra PowerShell fix for you to keep those IIS log files under control. It can also be easily customized for BES logs or other logging happy programs. Or even just keeping your temp files cleaned up regularly. You’ll want to set it to run as a scheduled task on a daily, weekly or monthly basis depending upon your organizations policies.

# CleanIISLogs.ps1
# Find and remove files older than $days
# Set $LogPath to where the IIS logs you want to recycle are kept
# 

$days = 31
$LogPath = C:\inetpub\logs\LogFiles\W3SVC1
# Find the target date
$startdate = Get-Date
$startdate = $startdate.AddDays(-$days)

# Clean the directory of log files older than the target date
Get-ChildItem -Path "$($LogPath)" -Recurse | where {$_.LastWriteTime -lt $startdate} | Remove-Item -Confirm:$false

Is this post helpful to you or is there something you would like me to go into greater detail on? Please let me know, thanks.

The Number One Easy Way to Setup a Failed Migration

It surprises me how much I run across this one but then again I have been guilty of it as well.

Eater of backups

I eat backups! Garr!

There is a very important first step that I find skipped over and forgotten quite often when it comes to running an Exchange migration. Or really any other kind of migration. Have you taken a system state backup of AD yet? No? Then you’re just spinning the bottle and hoping it doesn’t end up with you getting cozy with Microsoft’s support hoping that they can fixed your screwed up Active Directory.

Don’t make the mistake of assuming backups are working

I made this mistake once upon a time. It was from one of the first Exchange migrations I was running. I didn’t feel like being bothered to take a backup of AD as the server was a really slow server. I was confident that the nightly backups had taken care of everything anyhow. Though I didn’t bother to validate this. So I went directly into running the migration and everything was going smoothly at first. Everything looked like it was running great. But then part of the way through I found that AD replication had broken and that it possibly had been that way for a while. It would have been easy to roll back to an AD backup, correct the problem and then retrace my steps but unfortunately that wasn’t an option. Because I hadn’t taken a backup. The nightly backups hadn’t worked in several months either. That lead into a call with Microsoft later on and then having to spend even more hours fixing things manually via ADSIedit when they couldn’t figure it out.

I don’t want to be the one cleaning up after you

It is a very simple step to take at the very beginning. Just grab a backup before you run your first setup.com /PrepareAD. While you’re at it, why don’t you test the backup of your current mail server and make sure that it is working ok as well. Trust me on this. You don’t want to be the guy to explain to your boss that the data is gone as your only valid backup is from 3 years ago. Your backups are working, right? You might want to double check on that just to be sure. I recommend a mock restore for that extra bit of assurance.

Essential Exchange Troubleshooting – Send Email via Telnet

One of the best tools available for troubleshooting mail flow issues is right at your fingertips. It is quick, simple, and only requires a little training to use effectively. I am always surprised at how very few Exchange administrators seem to use it. You can see some of this in action in my previous NDR troubleshooting post. So let’s delve into some of the basics of how to use telnet for troubleshooting your mail flow issues.

First off, it is a great way to see if your SMTP service is even available. If you cannot connect to it via telnet then you immediately know that you need to check on the health of your services and if that is ok then you most likely have a firewall or other networking issue. So to execute this basic check pop open a command prompt and run

telnet myserver.contoso.com 25

Substituting your server address and if you are troubleshooting an alternate port change the 25 to whatever port you are troubleshooting. The majority of the time it will be port 25 though. If you receive a successful connection you should be greeted by your mail server’s banner, probably something along the lines of below

220 myserver.contoso.com Microsoft ESMTP MAIL Service ready at Mon, 27 May 2013 08:19:44 -0700

This is also a good time to check whether you are seeing the correct server address in your banner. If you are seeing the internal address for myserver.contoso.local you will want to update this.

At this point you need respond with a HELO or EHLO command to start the SMTP conversation. What is the difference between them? HELO is for SMTP while EHLO is for eSMTP. In the context of sending an e-mail via telnet it won’t matter which you use but it mail be useful to use EHLO to see what verbs are being offered, especially if you are suspecting that there mail be a problem with eSMTP.

EHLO mail.alternatecontoso.com

You should receive a response similar to what is below

250-myserver.contoso.com Hello [4.3.2.1]
250-SIZE
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-STARTTLS
250-AUTH NTLM LOGIN
250-8BITMIME
250-BINARYMIME
250 CHUNKING

If you have seen all of the above then so far so good. Your routing is good (assuming you aren’t being routed to the wrong SMTP server, but if you are and you don’t know it then you have bigger problems), your firewall configuration is correct and your hub transport is listening. Also note from the verbs sent above you can see that this service supports TLS and authentication as you can see in the STARTTLS and AUTH NTLM LOGIN verbs.

Now we want to start sending an email to someone on this server. Most likely your postmaster account since you are just testing your mail flow.

MAIL FROM: someone@alternatecontoso.com

You should receive a Sender OK response. If not then you’ll know that you need to look into sender permissions.

250 2.1.0 Sender OK

We need to specify who we are sending to

RCPT TO: postmaster@contoso.com

Here you should receive a Recipient OK response. This is the part where the conversation is most likely to break down and you will get an error code that you can start working with.

250 2.1.5 Recipient OK

So far so good, now we can send the actual email. Start off with the DATA command

DATA

And the server will be ready to receive your input. You can get as fancy or as simple as you like here but once you are done with the message use a . to end the mail input

354 Start mail input; end with <CRLF>.<CRLF>
.
250 2.6.0 <53ea1be2-3d1a-4856-8bdf-3c576c14cfc0@mail.contoso.com> [InternalId=47975] Queued mail for delivery

Assuming everything is still going well you should see either a queued mail for delivery or a spam rejection, depending upon how strict your spam filter is. You also may get an error message that would be worth researching as well. If everything is still going well you can close out the conversation.

QUIT
221 2.0.0 Service closing transmission channel

This is all for sending an email similar to how an anonymous server on the Internet would send an e-mail. There are a few variations that you will want to be aware of as well though. The first is for testing relaying. When you get to the part where you input the recipient you will input a remote server recipient.

RCPT TO: foreignemail@externalserver.com

If you are NOT wanting the server to relay the expected response would be

550 5.7.1 Unable to relay

This is a good thing as you do not want open relays sitting around. But on the other hand if this is an internal connector that is supposed to be relaying then you could have a permissions problem on your hand.

The other method that you would normally use telnet testing for is authentication. This is a bit more complex. After your HELO/EHLO command you will issue

AUTH LOGIN

To which if basic authentication is supported you will receive a response of

334 VXNlcm5hbWU6

This gibberish is actually a base64 encoded response that says “Username:”. The expected response to this is a base64 encoded username. An online utility I recommend is at this site which is pretty simple for encoding/decoding base64 message. So translate the username you are attempting to use into base64 and respond with that. I responded with “logon” encoded.

bG9nb24=

You should receive a response of

334 UGFzc3dvcmQ6

Which translates to “Password.” So now you need to response with the account’s password encoded in base64. My response was “My simple password”

TXkgc2ltcGxlIHBhc3N3b3Jk

You should now receive an Authentication successful message

235 Authentication succeeded

And you can continue with the rest of your steps of sending an email.

Was this post helpful? Do you have any topics you would be interested in seeing me cover in a later blog post? Just leave your suggestion in the comments below or shoot me an email.

OWA Login – Your Account has been Disabled

While this may not be a common issue, or at least I certainly hope it is not a common issue for you, it can be a bit vexing to figure out what is going on. You have a user with a recently restored account that is attempting to login to OWA and they are receiving an error similar to the following:

Your account has been disabled.

Copy error details to clipboard

Show details

Request

Url: https://mail.contoso.com:443/owa/

User host address: 1.2.3.4

User: Jane Doe

EX Address: /o=first organization/ou=exchange administrative group
(fydibohf23spdlt)/cn=recipients/cn=jane doe96d

SMTP Address: jdoe@contoso.com

OWA version: 14.2.318.2

The steps leading up to this error are most likely as follows.

  1. A user’s account was deleted and their mailbox removed recently. Possibly by accident or possibly by company politics.
  2. The user’s account is recreated as opposed to restored (which means a new SID and all the fun that goes along with that) and their mailbox is reattached to the account.
  3. The user now attempts to login with their “new” account into their old mailbox.
  4. Angry calls to your help desk now ensue.

Most likely your first thought was to do an iisreset but in this case you would be wrong. Here is how you clear this issue up swiftly and easily. Open up the EMS and run:

Clean-MailboxDatabase –Identity <Database Name>

This kicks off a scan of AD that updates the status of disconnected mailboxes in the targeted database. Alternatively you could also just tell the user to wait until Exchange runs its maintenance cycle on the database but that answer definitely won’t win you any friends. Now why does this need to be done? As you’ve probably suspected it is due to cached AD information of the disconnected mailboxes. For more info take a look at KB2682047.

Problems Installing Exchange 2010 Service Pack 2 on SBS 2011

Now these problems that occur are very likely originating from an already rather screwed up installation of SBS 2011. I was not involved in the original setup of this particular server but I do know that there had been a large number of problems originally encountered. In this instance the task was to get Exchange 2010 SP2 installed. There are several hoops that you may have to jump through to get this installed, here I will recount what I was required to do.

Firstly you need to make sure that you have closed any instance of the SBS Console. Otherwise you’ll get a failure in the prerequisites. Also initially you’ll need to stop the Windows SBS Manager service though if you can get the install to progress to the point of working on the installed roles rather than the organization that will no longer be a requirement. Once you’re past those prerequisites in theory your installation should go smoothly. But if that is not the case then read on.
The next problem you may encounter is any error in the Hub Transport Role. From the event logs you’ll find this error:

 Event ID 1002 MSExchangeSetup
 Exchange Server component Hub Transport Role failed.
 Error: Error:
 The following error was generated when "$error.Clear();
 if (get-service MSExchangeServiceHost* | where {$_.name -eq "MSExchangeServiceHost"})
 {
 restart-service MSExchangeServiceHost
 }
 " was run: "Service 'Microsoft Exchange Service Host (MSExchangeServiceHost)' cannot be started due to the following error: Cannot start service MSExchangeServiceHost on computer '.'.".
Service 'Microsoft Exchange Service Host (MSExchangeServiceHost)' cannot be started due to the following error: Cannot start service MSExchangeServiceHost on computer '.'.
Cannot start service MSExchangeServiceHost on computer '.'.
The service cannot be started, either because it is disabled or because it has no enabled devices associated with it

Checking your services you’ll also find all of the Exchange services disabled. Service packs and update rollups usually disable the services to prevent them from starting up unexpectedly while the update is being installed, but in this case for some reason SP2 is jinxing itself by not allowing itself to start a couple of necessary services for it to be able to continue. The easiest way to get around this, though not necessarily the safest, is to make sure that at this point all the Exchange services are set to Manual or Automatic. When you see setup get down to the point of setting up the Hub Transport Role then watch your services and wait for them all to be set to disabled. Once they are pop open a Powershell prompt and run:

Get-Service | where {$_.DisplayName –match “Microsoft Exchange”} | Set-Service –StartupType Manual

Now setup will be able to continue with starting the services that it requires for continuing setup. Which may lead to your next problem, it will fail on generating a new self-signed certificate for the Exchange Transport service. You’ll find this error in the event logs:

Event ID 1002 MSExchangeSetup
 Exchange Server component Hub Transport Role failed.
 Error: Error:
 The following error was generated when "$error.Clear();
 Write-ExchangeSetupLog -Info "Creating SBS certificate";
$thumbprint = [Microsoft.Win32.Registry]::GetValue("HKEY_LOCAL_MACHINE\Software\Microsoft\SmallBusinessServer\Networking", "LeafCertThumbPrint", $null);
if (![System.String]::IsNullOrEmpty($thumbprint))
 {
 Write-ExchangeSetupLog -Info "Enabling certificate with thumbprint: $thumbprint for SMTP service";
 Enable-ExchangeCertificate -Thumbprint $thumbprint -Services SMTP;
Write-ExchangeSetupLog -Info "Removing default Exchange Certificate";
 Get-ExchangeCertificate | where {$_.FriendlyName.ToString() -eq "Microsoft Exchange"} | Remove-ExchangeCertificate;
Write-ExchangeSetupLog -Info "Checking if default Exchange Certificate is removed";
 $certs = Get-ExchangeCertificate | where {$_.FriendlyName.ToString() -eq "Microsoft Exchange"};
 if ($certs)
 {
 Write-ExchangeSetupLog -Error "Failed to remove existing exchange certificate"
 }
 }
 else
 {
 Write-ExchangeSetupLog -Warning "Cannot find the SBS certificate";
 }
 " was run: "The internal transport certificate cannot be removed because that would cause the Microsoft Exchange Transport service to stop. To replace the internal transport certificate, create a new certificate. The new certificate will automatically become the internal transport certificate. You can then remove the existing certificate.".
The internal transport certificate cannot be removed because that would cause the Microsoft Exchange Transport service to stop. To replace the internal transport certificate, create a new certificate. The new certificate will automatically become the internal transport certificate. You can then remove the existing certificate.
Error:
 The following error was generated when "$error.Clear();
 Write-ExchangeSetupLog -Info "Creating SBS certificate";
$thumbprint = [Microsoft.Win32.Registry]::GetValue("HKEY_LOCAL_MACHINE\Software\Microsoft\SmallBusinessServer\Networking", "LeafCertThumbPrint", $null);
if (![System.String]::IsNullOrEmpty($thumbprint))
 {
 Write-ExchangeSetupLog -Info "Enabling certificate with thumbprint: $thumbprint for SMTP service";
 Enable-ExchangeCertificate -Thumbprint $thumbprint -Services SMTP;
Write-ExchangeSetupLog -Info "Removing default Exchange Certificate";
 Get-ExchangeCertificate | where {$_.FriendlyName.ToString() -eq "Microsoft Exchange"} | Remove-ExchangeCertificate;
Write-ExchangeSetupLog -Info "Checking if default Exchange Certificate is removed";
 $certs = Get-ExchangeCertificate | where {$_.FriendlyName.ToString() -eq "Microsoft Exchange"};
 if ($certs)
 {
 Write-ExchangeSetupLog -Error "Failed to remove existing exchange certificate"
 }
 }
 else
 {
 Write-ExchangeSetupLog -Warning "Cannot find the SBS certificate";
 }
 " was run: "Failed to remove existing exchange certificate".
Failed to remove existing exchange certificate

This is a very verbose yet also very helpful error. Chances are you’ll most likely encounter this if you are not using the default self-signed certificates but have installed a third party certificate. Though I didn’t check in this case, reviewing the commands being run it may be choking on a third party certificate that has a friendly name of Microsoft Exchange. To fix this one first make sure you have a copy of your third party certificate available and if you don’t then export a copy as you’ll be in need of it later. Once you have that available then run through the SBS Set up your Internet address wizard. This will generate you another self-signed certificate and replace the third party certificate you have in place. It will also remove the third party certificates from your certificate store, which is why you need to make sure you have a copy of the certificate available. Once you have done this re-run setup and you’ll be able to finish your installation of SP2. Don’t forget to put the third party certificate back in place and also it would be a good idea to run ExBPA to make sure you are still in compliance. You’ll also want to make sure that all of your Exchange services are set back to their appropriate startup values as you may be left with all the services set to disabled.

%d bloggers like this: