Sunday, October 8, 2017

Exchange/Skype For Business UM Integration With Selective Trust

My Exchange 2016 environment resides in a Resource Forest with a Selective Trust configured to ensure security between our US and Rest-of-World (ROW) forests.

Our Skype For Business resides in the Accounts Forest, because management didn't want multiple SFB environments in multiple Resources Forests.

This causes some problems when configuring Dial Plans with Unified Messaging because the SFB servers need to be able to authenticate against Exchange in order to configure Auto Attendants and Subscriber Access.

When running the OCSUmUtil in the Accounts Forest with a Selective Trust, the Dial Plans will come up empty. This is because the SFB servers are blocked from authenticating on the Resource Forest and can't read the config.

To allow authentication, the following groups from the Accounts Forest need to be set as "Allowed to Authenticate" on the Exchange servers and the Domain Controllers in the Resource Forest:




To easily add those groups and keep a clean Active Directory I suggest following my
previous post on creating a Selective Trust Security Group in the Accounts Forest.

And then follow
my other post to set the Selective Trust auth permissions.

If you just want to hurry and add those to allowed to auth, do the following:

1. Log onto a domain controller in your Resource Forest

2. Open Active Directory Users and Computers (ADUC)

3. Click View

4. Select Advanced Features

5. Browse to the OU where the Exchange Server(s) you are trying to authenticate to

6. Right-click the Exchange server objects, then select Properties, then the Security tab

7. Add the RTCComponentUniversalServices; RTCUniversalServerAdmins; RTCUniversalUserAdmins groups

8. Grant Allowed to authenticate rights

9. Click Apply, then OK.

10. Browse to the Domain Controllers OU.

11. Browse to the OU where the DC's you are trying to authenticate to

12. Right-click the DC objects, then select Properties, then the Security tab

13. Add the RTCComponentUniversalServices; RTCUniversalServerAdmins; RTCUniversalUserAdmins groups

14. Grant Allowed to authenticate rights

15. Click Apply, then OK

Now give it some time for replication.

For good measure, re-run the ExchUCUtil.ps1 on the Exchange server to make sure the Dial Plans will be configured correctly. (You can run that as many times as needed, it won't cause any harm)

Then, on the SFB server refresh your OCSUmUtil and it will now display your Dial Plans that are set in your Resource Forest.

Happy UMing :)

Sunday, October 1, 2017

Exchange Get All Duplicate Recipient SMTP Addresses

In my organization we have two Exchange 2016 Resource Forests (eu.domain.com and us.domain.com) and an Accounts Forest (domain.org) that both Exchange forests share.

We use MIM (Microsoft Identity Manager) to control our DirSync between all three forests.

We have about 15,000 users total, and a few thousand more service/admin/functional accounts.

The issue is, tons of those non-user accounts are set with the same proxy addresses and email addresses as the user accounts.

For instance if Joe Schmo is an IT admin and he has joe.schmo@domain.com for his UserMailbox Primary SMTP, his admin.schmo account also has joe.schmo@domain.com, which syncs over to the Exchange forest, causing tons of Event ID 9217 warnings and also causes email bounces because Exchange can't find the correct recipient.

In Exchange there's no one-liner to find duplicate SMTP addresses, nor is there in Active Directory...you can only do a query to find them one by one. This is a giant problem if you have an environment like me, where there's a couple hundred duplicate recipients!

So, I wrote a quick script that will run through your Exchange environment and spit out a TXT file with the displaynames and SMTP addresses that are duplicated.

Copy the following into Notepad and save it as Get-Dupe-Recipients.ps1 to somewhere like C:\Scripts:

$dupes = @{}
Get-recipient -resultsize unlimited |
  Select PrimarySmtpAddress,displayName |
   foreach {$dupes[$_.PrimarySmtpAddress] += @($_.DisplayName)

$dupes.keys |
 foreach {
         if (($dupes[$_]).count -gt 1) {

What the script does is uses a hash-table to find any SMTP address that is greater than than 1 for any recipient.

Once you have the .ps1 saved, fire up the Exchange Management Shell (EMS) and cd to the location of the .ps1 like so:

cd C:\Scripts

Then, run the following:

Get-Dupe-Recpients.ps1 | Out-File C:\Temp\RecipDupes.txt

**Note** You can change the Out-File location to where ever you want.

Now, you have list of all duplicate recipients...go clean 'em up!

Saturday, September 30, 2017

Exchange Veeam Backup Error Code 1935 Cannot Connect to Administrative Share

I use Veeam to backup my Exchange 2016 environment because (most importantly) it works with an IP-less DAG, and it's relatively quick and easy to do restores.

The issue is: it doesn't tie into Windows as well as something like DPM, so there's a bit of permission tuning you have to do; especially if you have a Resource Forest with a Selective Trust like I do.

We recently created a new dedicated backup account, but this account is located in the Accounts Forest, which means it has to authenticate over to the Resource Forest where Exchange lives.

Because of this, Veeam started throwing the following error:

Failed to prepare guest for hot backup. Error: Failed to connect to guest agent. Errors: 'Cannot connect to the host's administrative share. Host: [EXCH1.exchangeitup.com]. Account: [exchangeitup\exchbackup2]. Win32 error: The computer you are signing into is protected by an authentication firewall. The specified account is not allowed to authenticate to the computer. Code: 1935 Cannot connect to the host's administrative share. Host: []. Account: [exchangeitup\exchbackup2].

Deciphering the error:

The backup account doesn't have "Allowed to authenticate" rights on the actual Exchange server(s).

The Fix:

Follow my previous post to create a security group to allow Selective Trust auth:


After you have that set, add that backup account to your new group.

Or a messier way, messier because I don't like adding single users for permissions (use groups, your fellow IT admins will thank you later) you can just add auth rights directly:

1. Log onto a domain controller in the forest where your Exchange servers are homed
2. Open Active Directory Users and Computers (ADUC)
3. Click View 
4. Select Advanced Features
5. Browse to the OU where the Exchange Server(s) you are trying to authenticate to
6. Right-click then select Properties, then the Security tab
7. Add the backup user
8. Grant Allowed to authenticate rights
9. Click Apply, then OK.

Now your backups should run without auth errors

Saturday, September 9, 2017

Exchange Database Failovers and MSMQ Event ID 2250

My databases had been automatically switching over from one of my three Exchange 2016 CU4 DAG nodes, several times a day. I would move them back and they would fail back over almost instantly.

I started checking the Event Logs, and there were tons of Event ID 2250 and Event ID 2252 in the Application Log that coincided with the failover timestamps in the Exchange/High Availability/Operational Logs. This only happened on the one Mailbox Server though...the other two servers were running like champs.

The full Event IDs are:

MSMQ Event ID: 2250

Message Queuing will not be able to accept messages temporarily because system paged pool is low. During this period, machine quota will be set to 0. No manual intervention is required at this stage. Once memory utilization has normalized, Message Queuing will automatically resume accepting messages.

MSMQ Event ID: 2252

Message Queuing is resuming accepting messages because the system memory usage has normalized.

**Note** The 2252 event would happen directly after the databases moved off of this server.

Googling those events turned up nothing that really helped. As you can see the 2250 states that the page pool was exhausted, which was not the case, and my pagefile is set to preferred architecture (RAM + 10MB in my case 32,778MB).

Since it was only this one server, it had to be some sort of communication error.

So I did the normal Test-ReplicationHealth, Get-MailboxDatabaseCopyStatus and I noticed that PowerShell was throwing tons of errors like "MBX1 does not exist on DC1" and WinRM errors every time I would run a cmdlet.

That led me check the Domain Controllers and sure enough DCDIAG from Exchange-to-DC and DC-to-DC came back nasty with errors for the DC holding the FSMO roles.

The Cause:

Another domain admin was monkeying around with the firewall and GPOs on the DC's causing all kinds of network issues.

DO NOT turn off the Windows Firewall on production servers, and check your GPO scopes before enabling them!!!!!

The Fix:

1. Take away admin rights from everyone else :)

2. Reboot the DC and check DCDIAG to ensure it was clean...it is now.

3. Reboot the crippled Mailbox server
    - The errors were gone before the reboot, but just to make sure, I bounced it anyway.

**Note** Rebooting the FMSO DC will result in mailbox database dismounts. This is because the FMSO DC holds the AD configuration container for the databases.They should remount cleanly after the DC is back online, but you should do this during off hours if you can, since it will affect client connections to Exchange.

Now your databases should stay put according to their activation preference and those MSMQ events will be gone.

Saturday, August 19, 2017

Exchange External Forward 550 5.7.54 Unable To Relay In Non-Accepted Domain

I recently had this problem where an Accepted Domain in my Exchange environment would get a bounce when mailing a Shared Mailbox that was set with an external forward. In case you have the same setup as me, I've found a fix.

Consider this scenario: you have two Exchange Resource Forest us.domain.com and eu.domain.com.
The eu.domain.com is set as an Internal Relay Accepted Domain in the us.domain.com Exchange environment.
The us.domain.com has a Shared Mailbox, which forwards to an external email address with the ForwardingSmtpAddress switch like so:
Get-Mailbox "Shared Mailbox Name" |fl *forw*
DeliverToMailboxAndForward : True
ForwardingAddress          :
ForwardingSmtpAddress      : smtp:externalemailaddress@externaldomain.net

When users from the eu.domain.com Resource Forest send a message to the Shared Mailbox in the US, they get the following error:
The email to the following recipient(s) could not be delivered:
The remote mail server told: 550 5.7.54 SMTP; Unable to relay recipient in non-accepted domain

All other external senders (people from outside the company) can send and messages get forwarded successfully.
Setting the forward by using the ForwardingAddress switch and creating a MailContact causes all senders from both the other forest and external to fail; so you must keep the ForwardingSmtpAddress set.
A brief overview of ForwardingSmtpAddress vs ForwardingAddress:
This is a RecipientIdParameter value which has a higher priority than ForwardingSmtpAddress; meaning if you set the parameter ForwardingAddress, other forwarding settings will be overritten. This setting does not require the Set-RemoteDomain -AutoForwardEnabled, it does require an external MailContact in Exchange.
ForwardingSmtpAddress :
This is a msExchGenericForwardingAddress AD attribute. It has lower a priority than ForwardingAddress and is not accessible in in the EAC; you must use PowerShell to set it. You must also use the Set-RemoteDomain -AutoForwardEnabled $True cmdlet to allow forwarding, but it does not require a MailContact in Exchange.

The Fix:

We'll need to create a dedicated Send Connector to the domain for our external forward.

In the EAC, navigate to Mail Flow, Send Connectors, +

In the New Send Connector window, give it a name like "External Forward" and click Next

Create Send Connector

Leave MX record selected and click Next

Send Connector to MX

Click the + and under the FQDN type the domain name for the external contact, click Save, and Next

Send Connector Domain

For the Source Server, click the + and select your Edge server if you have one, or your Mailbox servers (all of them) and click Ok, then Finish

Send Connector Source Server

Enable Verbose Logging on the Connector:

You'll want full logging on the connector so you can check the SMTPSend protocol logs later to verify successful sending.

In the Exchange Management Shell (EMS), run the following:

Set-SendConnector "External Forward" -ProtocolLoggingLevel Verbose


If the external domain requires it, you'll need to enable forced TLS, else messages will be dropped.

In the EMS run the following:

Get-SendConnector "External Forward" | Set-SendConnector -RequireTLS $true

Now test! Have someone from the other Resource Forest send to the Shared Mailbox and have an external sender send a message the Shared Mailbox and verify that it forwards by using the protocol logs.

**Note** Depending on what source server you used (Edge or Mailbox) that's where you'd check the SMTPSend protocol logs.

Sunday, August 6, 2017

Exchange SpamTitan 421 4.3.2 Maximum Connections Exceeded Limit

I run a SpamTitan Private Cloud Antispam Gateway in my Exchange environment, and its awesome...probably one of the best spam filters I've run in a long time. And their support is top-notch!
And if you happen to run a ITAR/DFARS/DoD compliant Exchange environment, this solution will work for you as all data is housed on the AWS private cloud in the US.

One thing I started noticing was we had quite a few incoming messages being deferred when flowing from the gateway to my Edge server. It was pretty sporadic, where if I had messages from one external sender to multiple internal recipients, one recipient would receive it and the others wouldn't.

In the SpamTitan console, go to Reporting > History and you'll see messages with Delivery Status of Deferred and Delivery Response with the following:

host [Your Exchange external IP] refused to talk to me: 421 4.3.2 The maximum number of concurrent server connections has exceeded a per-source limit, closing transmission channel (EdgeServer.domain.com)

In my case, I had tons of these going back months. The message will list your Exchange external IP and the server that dropped the connection (this could be your Edge server or your Mailbox servers if they are internet facing).

What was happening was, my Edge server receive connector was still set to default for Max Inbound Connection Per Source, which is 20.

The SpamTitan uses 60 connections according to their support. So, we need to bump up the limit on the Receive Connector.

Fire up the Exchange Management Shell (EMS) on whichever server is internet facing - the Edge in my environment.

Run the following to verify the current settings:

Get-ReceiveConnector | fl *maxin*

You'll get the output of:

MaxInboundConnection                                  : 5000
MaxInboundConnectionPerSource                  : 20
MaxInboundConnectionPercentagePerSource : 2

As you can see, 20 MaxInboundConnectionPerSource is too low, and the SpamTitan filter will defer those messages until connections become available...or worse case, bounce those message if the time limit expires. That's bad because your users won't receive messages, and they won't even know they were supposed to.

We'll bump up the limit to 100, which should suffice for SpamTitan, and give us a little wiggle room without overloading the Exchange server(s).

Run the following:

Get-ReceiveConnector | Set-ReceiveConnector -MaxInboundConnectionPerSource 100

**Note** Some receive connectors on Mailbox servers are set to unlimited, so you'll wanna specify the connector name in the above cmdlet if you're not running this on an Edge server since it will generally only have one receive connector.

Now, you can verify the settings by running the first cmdlet again:

Get-ReceiveConnector | fl *maxin*
You'll get the output of:
MaxInboundConnection                                    : 5000
MaxInboundConnectionPerSource                   : 100
MaxInboundConnectionPercentagePerSource : 2

You're all set! Now new messages won't be deferred.
Be warned: those messages that were being queued as deferred, will start flowing in...so you'll get reports from users that they're receiving messages from days ago :)

**Note** I do not work for SpamTitan, nor did I receive any endorsement from them on this post.

Saturday, August 5, 2017

Exchange Get Mailbox Sizes By Mailbox Type

This post is really just for my own records because I use these all the time; my higher-ups like to know who has giant mailboxes.
And since my Exchange environment is in a Resource Forest, I have every kind of mailbox imaginable - Linked, User, Room, Archives...

So I have these PowerShell cmdlets at the ready to quickly grab that info when I need to send it to the bosses.

Fire up the Exchange Management Shell (EMS) and run any of the following, depending on what info you need.

Shared Mailboxes:

Get-Mailbox -RecipientTypeDetails shared | Get-MailboxStatistics | ft displayname,totalitemsize,itemcount

--To Export a CSV:

Get-Mailbox -RecipientTypeDetails shared | Get-MailboxStatistics | select displayname,totalitemsize,itemcount | Export-Csv C:\Temp\sharedsize.csv -NoTypeInformation

Room Mailboxes:

Get-Mailbox -RecipientTypeDetails roommailbox | Get-MailboxStatistics | ft displayname,totalitemsize,itemcount

--To Export a CSV:

Get-Mailbox -RecipientTypeDetails roommailbox | Get-MailboxStatistics | select displayname,totalitemsize,itemcount | Export-Csv C:\Temp\roommailbox size.csv -NoTypeInformation

Equipment Mailboxes:

Get-Mailbox -RecipientTypeDetails equipment | Get-MailboxStatistics | ft displayname,totalitemsize,itemcount

--To Export a CSV:

Get-Mailbox -RecipientTypeDetails equipment | Get-MailboxStatistics | select displayname,totalitemsize,itemcount | Export-Csv C:\Temp\equipment size.csv -NoTypeInformation

User Mailboxes:

Get-Mailbox -RecipientTypeDetails usermailbox | Get-MailboxStatistics | ft displayname,totalitemsize,itemcount

--To Export a CSV:

Get-Mailbox -RecipientTypeDetails usermailbox | Get-MailboxStatistics | select displayname,totalitemsize,itemcount | Export-Csv C:\Temp\usermailbox size.csv -NoTypeInformation

Linked Mailboxes:

Get-Mailbox -RecipientTypeDetails linkedmailbox | Get-MailboxStatistics | ft displayname,totalitemsize,itemcount

--To Export a CSV:

Get-Mailbox -RecipientTypeDetails linkedmailbox | Get-MailboxStatistics | select displayname,totalitemsize,itemcount | Export-Csv C:\Temp\linkedmailbox size.csv -NoTypeInformation

Archive Mailboxes:

Get-Mailbox | Get-MailboxStatistics -Archive | ft displayname,totalitemsize

--To Export a CSV:

Get-Mailbox | Get-MailboxStatistics -Archive | select displayname,totalitemsize,itemcount | Export-Csv C:\Temp\Archive size.csv -NoTypeInformation

Happy reporting!

Sunday, July 23, 2017

Exchange Cleaning Up Meetings From Terminated Employee

You have a user who has left the company and they have a bunch of meetings scheduled on Room Mailboxes; this is probably something every exchange admin has to deal with at least once.
If you have a small organization or a limited number of rooms, those meetings can be taking up valuable timeslots that other users could be using.
Why there's not an automated mechanism to remove those meetings when you disable a mailbox? I dunno :(

A lot of times, you might have also granted Full Access to that user's mailbox for someone that has taken over their responsibilities, and that person gets annoying alerts for meetings that no longer need to be scheduled.

I'll show you how to delete the meetings from the Room Mailboxes in one shot, and how to remove all meetings from the user's mailbox calendar by using PowerShell.

Deleting Meetings From the User's Calendar:

First, we'll get a count of how many meetings the user has. This will be useful to ensure that the cleanup cmdlet works later.

Fire up the Exchange Management Shell (EMS) and run the following:

Search-Mailbox -identity disableduseraccountname -SearchQuery kind:meetings -EstimateResultOnly | Out-File C:\Temp\DeletedUserMeetings.txt

**Note** Change disableduseraccountname to name of the user's mailbox, and change the TXT file path.

The above cmdlet will spit out a TXT file with all the meetings that the departed user had scheduled.

The reason I pipe it out to a TXT file, is because there might be tons of meetings...I've seen a user with 6000 meetings scheduled. They thought they'd be with the company until the year 2045 :)

Next, we'll delete all those meetings, by running:

Search-Mailbox -identity disableduseraccountname -SearchQuery kind:meetings -DeleteContent

Once that cmdlet completes, you can run the first one with the -EstimateResultOnly switch to verify all meetings are gone.

Deleting Meetings From the Room Mailboxes:

Now, we'll clean up all meetings scheduled by the terminated user from every room mailbox, in bulk.

Again, we're gonna check and see how many meetings exist for this user, by running:

Get-Mailbox -RecipientTypeDetails roommailbox | Search-Mailbox -searchquery "kind:meetings from:disableduseraccountname" -EstimateResultOnly | Out-File C:\Temp\DeletedUserRoomMailboxMeetings.txt

**Note** Change disableduseraccountname to the mailbox name and the file path.

The cmdlet will search all Room Mailboxes for any meeting scheduled by the terminated user and output a TXT file.

Now, we'll delete all those meetings from the Room Mailboxes:

Get-Mailbox -RecipientTypeDetails roommailbox | Search-Mailbox -searchquery "kind:meetings from:disableduseraccountname" -deletecontent

This cmdlet will search those Room Mailboxes, deleting any meeting scheduled by the supplied user.

You can run the estimate results cmdlet again to verify they were deleted, or open up one of the Room Mailboxes in Outlook and see if any meeting still exists on the calendar.

Saturday, July 22, 2017

Exchange Find AD Users With TargetAddress Set

I recently wrapped up a Lotus Notes to Exchange migration and during co-existence we needed to have the TargetAddress attribute set on Exchange mailboxes so mail would forward back to Notes until their mailboxes were moved over.
The issue I had was after migrating the last of the Shared Mailboxes, some users complained that the mailboxes weren't receiving mail.
What happened was after we migrated, we overlooked a few of those that still had TargetAddress attributes set. So I needed to find all mailboxes that still had them.
But the catch is, we have two Resource Forests that are synced with MIM, so I have MailUsers from another domain that do have TargetAddresses, and I don't want to touch those.
With PowerShell, it's real quick to find those Users that match my specific domain.
First, what is a TargetAddress?
The TargetAddress attribute is an Active Directory User property that forwards mail to a mailbox located somewhere other than your Exchange. This can be external, another Exchange environment, or in my case a Notes system.
To find all users that have the TargetAddress set, for a specific domain and export to a TXT file, run the following cmdlet in the Exchange Management Shell or the Active Directory PowerShell:
Get-aduser -filter {targetAddress -like "*.domain.com"} -properties * | Select-Object Name, targetaddress | Out-File C:\Temp\TargetAddresses.txt
**Note** Change "*.domain.com" to your domain name and change the out-file path.

Now you can go remove those in bulk by following my previous post here.

Saturday, July 1, 2017

Exchange Set Retention Policy Scheduled Task

My organization uses the same mailbox creation provisioning script for multiple Exchange environments through our ServiceNow application and we can't set the Retention Policy during mailbox creation because it would affect those other environments.

So I set up a scheduled task to grab all UserMailboxes without any policy applied, and set our custom policy.

Exchange on-prem doesn't have a the option to run Set-RetentionPolicy -IsDefault $true like Office365 does, so you either have to set the retention policy during mailbox provisioning or manually later on.

So, what we're gonna do is create a Task on our Exchange Management Tools server; you can set it directly on an Exchange server if you choose.

The task will run a PowerShell cmdlet that finds all UserMailboxes with no Retention Policy applied (newly created mailboxes) and it will then set our organization's Retention Policy.

Create a Service Account:

First, you'll want to create a Service Account in your domain, which will be used to run the scheduled task. It's best practice to use service accounts rather than your own account to run scheduled tasks, so if you ever leave your position and they deactivate your account, it won't break the task!

In your domain, create a new user called something like exchscriptaccount and set a super-strong password.

This account will need to be a member of the Recipient Management Role Group, otherwise it won't have permissions to make changes to mailboxes.

Next, add the newly created user to the Local Administrators Group on your Exchange Management Tools server or Exchange server if your running it from there. The scheduled task will need local admin rights to run PowerShell things, and since you have a super strong password, it's not an issue.

Creating The Task:

Create the scheduled task on the Exchange Management Server (or one of your Exchange Servers):

Open the Task Scheduler Control Panel, click Action > Create Task...

On the General tab:

Give it a name like Set Retention Policy

Click "Change User or Group..." hit "Locations" and switch to your domain, then search for your exchscriptaccount service account.

Check the box for "Run with highest privileges"

On the Triggers Tab:

Click "New..."

Set it for how often you need it to run. I run mine Daily at 12AM - no specific reason, but you do want it to run Daily.

On the Actions Tab:

Set the "Action" dropdown to "Start a program"

Under Program/Script, copy/paste the following:


In the "Add arguments" field, copy/paste the following:

-NonInteractive -WindowStyle Hidden -command ". 'C:\Program Files\Microsoft\Exchange Server\V15\bin\RemoteExchange.ps1'; Connect-ExchangeServer -auto; Get-Mailbox -Filter {retentionpolicy -eq $null -and recipienttypedetails -eq 'usermailbox' } | Set-Mailbox -RetentionPolicy 'My Retention Policy'"

**Note** Change 'My Retention Policy' to the name of your policy. You can also change recipienttypedetails -eq 'usermailbox' to other types like LinkedMailbox if you have those.

In the Settings Tab:

Checkmark the following boxes:

- Allow task to be run on demand

- Stop the task if it runs longer than: 1 hour (if it runs longer than an hour, you got something wrong!)

- If the running task does not end when requested, force it to stop

Click OK when you have everything set.

Testing the Task:

In the main task window, right-click your new "Set Retention Policy" task, and click Run.

When it finishes running, you should have a (0x1) Last Run Result.

Check the properties on a newly created mailbox that you know didn't have the Retention Policy set, and it should now have your policy applied.

Now, Exchange will do the boring job of applying the policy for you :)

Sunday, June 4, 2017

Exchange Set Retention Policy In Bulk For Specific Mailbox Type

We recently rolled out a new Retention Policy in my Exchange 2016 environment but I only wanted it to apply to Linked and User Mailboxes; not Shared or Resource Mailboxes.
We'll set a different policy for those. So I needed a way to apply this new Policy to a certain mailbox type. We'll get this done quickly with PowerShell!
Generally, when you apply a Retention Policy in bulk you would run Get-Mailbox | Set-Mailbox without any filters, and that would apply it to all Mailboxes in the organization.
We don't want that, so we'll use a filter to grab the Mailbox Type.
Fire up the Exchange Management Shell and run the following cmdlet:
Get-Mailbox -Filter {(RecipientTypeDetails -eq 'UserMailbox')} | Set-Mailbox –RetentionPolicy "UserMailbox Retention Policy"
The above cmdlet will apply your policy named "UserMailbox Retention Policy" to User Mailboxes only.
Since this is a Resource Forest, I also have Linked Mailboxes in my environment. So we'll need to apply the Policy to those as well:
Get-Mailbox -Filter {(RecipientTypeDetails -eq 'LinkedMailbox')} | Set-Mailbox –RetentionPolicy "UserMailbox Retention Policy"

This cmdlet will filter only Linked Mailboxes and apply the "UserMailbox Retention Policy"

**Note** Change "UserMailbox Retention Policy" to the name of your policy.

If you have multiple policies for different mailbox types, change the RecipientTypeDetails -eq 'mailboxtype' to one of the following:






For instance, setting a Room Mailbox Policy would look like so:

Get-Mailbox -Filter {(RecipientTypeDetails -eq 'RoomMailbox')} | Set-Mailbox –RetentionPolicy "RoomMailbox Retention Policy"

Now your different mailbox types will have the proper policies applied!

Saturday, June 3, 2017

Exchange Export List of Mailboxes Created After a Certain Date

In my organization, we have a billing structure for each site, where they charge back resources like mailboxes to the company. This is done monthly, so I needed a way to get a list of mailboxes to send to site managers.

We'll use our old buddy PowerShell to export a CSV of mailboxes that were created after a certain date.

Fire up the Exchange Management Shell (EMS) and run the following:

Get-Mailbox -ResultSize unlimited | where {$_.whenmailboxcreated -gt (get-date "4/18/2017")} | select displayname,whenmailboxcreated | Export-CSV C:\Temp\Mailboxes-4-18-17.csv

What the cmdlet does is grabs all mailboxes created after 18APR17 with "-gt" (greater than) operator and then exports the CSV with the mailbox display names and when they were created.

**Note** Change the date to how far you need to go back and change "C:\Temp\Mailboxes-4-18-17.csv" to the path and filename of your choosing.

Now you have a when were these mailboxes created report to send out!

Saturday, May 27, 2017

Exchange Messages Missing From The MessageTrackingLog

In my Exchange 2016 environment, we run an Edge server, which is where I generally do message tracking; this gives a better picture of message flow, since all external mail goes in or out of the Edge.

If you don't run an Edge server, you'll need to do message tracking on your Mailbox Servers.

The issue,is, just running the Get-MessageTrackingLog on one Mailbox server, searches only on that server, which means some messages that you know went out, are missing from the log.

So, we need a way to search the log on every server we have. PowerShell makes it easy!

Fire up an Exchange Management Shell on one of your Mailbox Servers, or on your Exchange Management Tools machine and run the following:

$Servers = Get-ExchangeServer;  $Servers | where {$_.isHubTransportServer -eq $true -or $_.isMailboxServer -eq $true} | Get-MessageTrackingLog -recipients email@address.com -start 05/23/2017

**Note** Change email@address.com to the email addy you're working with and change the -start date

Now, your message tracking searches will show all messages not just the ones local to a server.

Exchange Count the Number of Messages In The MessageTrackingLog

I had to diagnose a mailbox forwarding to external issue recently, and while I could tell that messages were going out by using Message Tracking on the Edge server, the user that had the forward wanted a count of how many messages were being sent.

So this is quick post on using PowerShell to get a count of messages by sender in the MessageTrackingLog.

Fire up the Exchange Management Shell on the Edge server if you have one, or on one of your Mailbox Servers.

And run the following commands:

$count = get-messagetrackinglog -Recipients "email@address.com" -Start 5/23/2017

$count | Group-Object -Property Sender | Select-Object name,count | sort

**Note** Change "email@address.com" and -start date

You can also do -sender instead of -recipient in the first variable, like so:

$count = get-messagetrackinglog -Sender "email@address.com" -Start 5/23/2017

And if you want to narrow down the results further, you can also add an -EventID like so:

$count = get-messagetrackinglog -Recipients "email@address.com" -EventID SEND -Start 5/23/2017

Happy message counting!

Sunday, May 21, 2017

Exchange/AD Export List of Users With Empty Email Address Fields

In my Exchange 2016 environment, I run a Resource Forest, which means we have to use MIM to sync users from the Accounts Forest. The way we have it configured, the attribute that triggers a sync is the Email Address field. If that field is empty, the account won't get created in the Resource Forest.

Not every account needs an email address, or sometimes the provisioning script might not populate the email address.

My bosses wanted an "empty email address" report, so I needed a way to export a list of users with empty email address field, and their respective OU's.
Since this is an Accounts Forest (with no Exchange) we'll have to use Active Directory PowerShell cmdlets...which aren't as easy to use to grab recipient information.

So, here's a quick one-liner that will grab all ADUsers with no email address populated; the full path of the Organizational Unit they live in; and export that list to a CSV file.

Fire up Active Directory Module for Windows PowerShell and run the following cmdlet:

Get-ADUser -Filter {EmailAddress -notlike "*"} -Properties EmailAddress | Select-Object Name,@{n='OU';e={$_.canonicalname -replace "/$($_.cn)",""}} | Export-Csv "C:\Temp\EmptyEmailAddresses.csv"

**Note** Change the "C:\Temp\EmptyEmailAddresses.csv" path to wherever want to save the csv.

Your csv output will look like so:

Name OU
Stacey Branham exchangeitup.com/Mailboxes
User1 exchangeitup.com/NonMailUsers
User2 exchangeitup.com/NonMailUsers
User3 exchangeitup.com/DisabledUsers
User4 exchangeitup.com/DisabledUsers

Happy reporting :)

Sunday, May 7, 2017

Exchange Count Total Number Of Items In All Mailboxes

After completing the user mailbox migration from Lotus Notes to my Exchange 2016 environment, our higher-ups wanted a "how much email did we move?" report.
While it does seem kind of arbitrary, they wanted a full count to go along with their return on investment report for the board.

We used the Quest migration tool to move the mailbox items, and while it is a pretty decent tool, it didn't really give a full picture of items moved, without digging through each migration batch log - which would take forever.

So I created two quick scripts to count the number of all items in each mailbox in the Exchange organization, and then present the total.

Get Item Count By Database

The first script will count items in each Mailbox Database, which can be useful for a more granular breakdown.

You can download the Get-TotalItemCountInDBs.ps1 file on my gDrive.

Or copy and paste the following text into Notepad, and save as a .ps1 file:

 ##Change "DBNAME" to the Database you're working with
$Mailboxes = Get-MailboxDatabase "DBNAME" | Get-Mailbox

 $MailboxTotalItemCount = 0
 foreach ($Mailbox in $Mailboxes)
 $MailboxStats = Get-MailboxStatistics -Identity $Mailbox
 $MailboxItemCount = $MailboxStats.ItemCount
 $MailboxTotalItemCount = $MailboxTotalItemCount + $MailboxItemCount
 Write-Host "Total Item Count:      $MailboxTotalItemCount"

**Note** Since this is just a quick script, I didn't have to time to make it pretty and prompt for the DB name, so you'll need to replace the "DBNAME" with the database in the ps1 itself, before running it.

When the script is done running, you'll get an output like so - this is for one of my databases:

[PS] C:\Users\stacey\scripts>.\Get-TotalItemCountInDBs.ps1 
Total Item Count:      2983548

Get Item Count In All Databases

The second script will crawl through every database, and present the total count for all mailboxes on all databases.

You can download the Get-TotalItemCount.ps1 on my gDrive

Or, once again, copy the text below and save as a .ps1:

 $Mailboxes = Get-MailboxDatabase | Get-Mailbox
 $MailboxTotalItemCount = 0
 foreach ($Mailbox in $Mailboxes)
 $MailboxStats = Get-MailboxStatistics -Identity $Mailbox
 $MailboxItemCount = $MailboxStats.ItemCount
 $MailboxTotalItemCount = $MailboxTotalItemCount + $MailboxItemCount
 Write-Host "Total Item Count:      $mailboxTotalItemCount"

**Note** You don't have to specify any database in this script, just run it as is.

When this script is done (and yes it will take a while) you'll get the results like so:

[PS] C:\Users\stacey\scripts>.\Get-TotalItemCount.ps1
Total Item Count:      15797733

Now, you'll have a number to present to your "handlers" showing that: yes, we moved over 15 million items (in my case) over to Exchange; and yes that was overkill since most users won't ever even know how to find most of those items :)