Archive for October, 2009

Script: Update-MobileNumber.ps1 – Automatically Updating the Global Address List with Mobile Numbers from Exchange ActiveSync

October 13th, 2009 2 comments


In some organizations, the Global Address List is used extensively as a phone list and corporate directory. When that’s the case, keeping the information current can be time-consuming. Users don’t always notify you of changes, and Help Desk staff have better things to do than updating stuff like that. There are applications like fellow Jim McBee’s awesome web-based Directory Update, which provides a simple interface for users to update GAL info. But that still requires that the user take the time to update the info. Here, we’ll automate the process of updating the GAL with a new mobile number when a user syncs a new ActiveSync device for the first time.

When a user synchronizes a device, information about the device is stored in Active Directory. The info can be viewed using the Get-ActiveSyncDeviceStatistics.

Get-ActiveSyncDeviceStatistics -mailbox dbingham
FirstSyncTime         : 10/2/2009 5:45:54 PM
LastPolicyUpdateTime  : 10/2/2009 5:46:38 PM
LastSyncAttemptTime   : 10/13/2009 4:46:38 PM
LastSuccessSync       : 10/13/2009 4:46:38 PM
DeviceType            : PocketPC
DeviceID              : AF053AA9D0FE3D37C5A2AC3C77ACB9F8
DeviceUserAgent       :
DeviceWipeSentTime    :
DeviceWipeRequestTime :
DeviceWipeAckTime     :
LastPingHeartbeat     :
RecoveryPassword      : ********
DeviceModel           : RAPH800
DeviceIMEI            : 0x80046B09
DeviceFriendlyName    : Pocket_PC
DeviceOS              : Windows CE 5.2.19965
DeviceOSLanguage      : English
DevicePhoneNumber     : 5865311234
Identity              :\AirSync-PocketPC-AF053AA9D0FE3D37C5A2AC3C77ACB9F8

We see that the next-to-last field contains the device’s phone number*. So, we’ll use some code that will accomplish the following tasks:

  • Get a list of all user mailboxes
  • Get ActiveSync data for all devices that:
    • have a phone number
    • have synced for the first time in the last 24 hours
  • filter out any old devices still listed (in case a user has had more than one EAS device)
  • format the number in a human friendly version (hyphenate)
  • Update the user’s AD account with the number

That can be accomplished using the following code:

$mailboxes = @(Get-Mailbox | ? {$_.RecipientType -eq 'UserMailbox'})
ForEach ($mailbox in $mailboxes){
  $devices = @(Get-ActiveSyncDeviceStatistics -mailbox $mailbox.Alias | Where-Object {($_.DevicePhoneNumber -ne '') -and ($_.FirstSyncTime -gt (Get-Date).addhours(-24))}) | Sort-Object LastSuccessSync -descending | Select-Object -first 1
ForEach ($device in $devices){
   $NumberLength = $device.DevicePhoneNumber.length
   if ($NumberLength -eq 10) {$DeviceNumber = $device.DevicePhoneNumber.SubString(0,3)+"-"+$device.DevicePhoneNumber.SubString(3,3)+"-"+$device.DevicePhoneNumber.SubString(6,4)}
   if ($NumberLength -eq 11) {$DeviceNumber = $device.DevicePhoneNumber.SubString(1,3)+"-"+$device.DevicePhoneNumber.SubString(4,3)+"-"+$device.DevicePhoneNumber.SubString(7,4)}
   Set-User $mailbox.Alias -MobilePhone $DeviceNumber

Copy that code to notepad and save it in your scripts folder as Update-MobileNumber.ps1. Then we just run the script via a scheduled task every 24 hours. If you don’t run it every 24 hours, make sure you adjust the (Get-Date).addhours(-24) line accordingly.

* – Most devices have the number stored there. Some devices, like the Apple iPhone, unfortunately don’t.


Execution Policy: Third-party PowerShell scripts may require that the PowerShell Execution Policy be set to either AllSigned, RemoteSigned, or Unrestricted. The default is Restricted, which prevents scripts – even code signed scripts – from running. For more information about setting your Execution Policy, see Using the Set-ExecutionPolicy Cmdlet.


I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.


Running PowerShell Scripts via Scheduled Tasks

October 7th, 2009 No comments

The robust capabilities of PowerShell and the Exchange Management Shell allow us to streamline and automate many system tasks. By using scheduled tasks, we can now run tasks on a recurring schedule, thus reducing our manual workload, as well as providing capabilities that were not previously available.

Generally, any PowerShell task can be automated, as long as it doesn’t require manual intervention. For this example, we’ll run a script called New-UserWelcome.ps1. This script, as will be documented in a future blog post, sends a ‘welcome’ email to all new mailboxes. This blog post assumes that the script to be scheduled is error free. We’ll plan to run the script every 4 hours.

What the PowerShell script does might dictate what server you run it from. In this example, since the script will send email, we’ll run it from an Exchange 2007 hub transport server.

On a hub transport server running on Windows 2003 server, we go to Control Panel>Scheduled Tasks. Once there, right click and chose New Scheduled Task. Give the task a name and press enter. Now right click on the task and choose Properties.

In the Run field, we include the path to the PowerShell executable, as well as the path to the Exchange console file (which contains the Exchange cmdlets), and the actual script we want to run. An example would be:

c:\windows\system32\windowspowershell\v1.0\powershell.exe -psconsolefile "c:\Program Files\Microsoft\Exchange Server\bin\exshell.psc1" -command "New-UserWelcome.ps1"

We also place the path to our scripts folder in the Start In field. This is because our example script, New-UserWelcome.ps1, is in the scripts folder:

"c:\program files\microsoft\exchange server\scripts"

In the Run As field, enter the name of an Exchange admin account, and click Set Password to enter the password for that account.

On the Schedule tab, set the Schedule Task to be Daily, Every 1 day, and click the Advanced button. Check Repeat Task, and set it for Every 4 hours for a duration of 24 hours. Click Ok twice.

Now we can test the scheduled task by right clicking on it and choosing Run.

In Windows 2008, the GUI is a tad different. Go to Administrative Tools>Task Scheduler. In the Action pane on the far right, click Create Basic Task. Give the task a name, and click next. Click Daily, and Next. Choose Recure every 1 day, and Next. Choose Start a program, and Next. Enter the path to PowerShell, the path to the Exchange console file, and the path to the scipe in the Program/Script field. For this example, we’ll use:

c:\windows\system32\windowspowershell\v1.0\powershell.exe -psconsolefile "c:\Program Files\Microsoft\Exchange Server\bin\exshell.psc1" -command "New-UserWelcome.ps1"

In the Start In field, add the path to the \scripts folder than holds your PowerShell script, such as

"c:\program files\microsoft\exchange server\scripts"

And then click Next. When presented with the popup about arguments in the text box, click Yes. On the Summary page, click the box for Open the Properties dialog for this task when I click Finish. Then click Finish.

When the properties dialog box opens, click the triggers tab, click edit, click the Repeat task every 4 hours (you can type in the pulldown box) for a duration of 1 day. Then click Ok twice.

Once that’s done, click Run in the lower part of the Action pane to test.

As you can see, it’s not terribly complex to run scheduled PowerShell scripts. By doing so, we can automate many mundane tasks, and even add functionality that is not already there.

We will build upon this article in future articles.

Exchange 2007 Prerequisite XML Files Included in Service Pack 2 Download

October 6th, 2009 No comments

Microsoft has included the handy Windows Server 2008 XML files in the Exchange Server 2007 Service Pack 2 distribution. These files, previously available via the product group’s blog site, makes it quick and painless to install all necessary prerequisites for Exchange on Windows 2008 servers.

Found in the \scripts folder, the files include

  1. exchange-all.xml (all roles)
  2. exchange-base.xml (all prerequisites for any Exchange server)
  3. exchange-cas.xml (client access server)
  4. exchange-edge (edge transport server)
  5. exchange-hub (hub transport server)
  6. exchange-mbx (mailbox server)
  7. exchange-typical (single box – Hub/CAS/Mbx)
  8. exchage-um (unified messaging)

What is not in the files, it appears, are the prerequisites for a clustered mailbox server (which was included in the original zip file) and Network Load Balancing for Client Access Servers. Both files can be created or the options can be added to the existing files.

How to Remove 169.254.x.x Addresses From Message Headers of Messages Coming From CCR Clusters

October 1st, 2009 2 comments

Recently, a client had a user who received an NDR they had not seen before. The NDR included, among other info, the following text:

#5.0.0 smtp; 5.1.0 – Unknown address error 550-‘Error: ACL header_checks_bogon Message contains a Received: header containing a forbidden “bogon” IP address from an unassigned Class A/B network. See, IP = “[169.254.]”‘ (delivery attempts: 0)> #SMTP#

Note the partial IP address, 169.254.. You’ll no doubt recognize that as the first two octets of the Automatic Private IP Addressing (APIPA) address range. That’s not something we generally see being used in an enterprise messaging environment, and the receiving side was right for wanting to bounce messages with that in the headers. Further testing confirmed that messages coming from any mailbox on any of the CCR implementations did include that address in the message headers. All of the clusters were built on Windows 2008. An example is:

Received: from ([]) by
([]) with mapi; Thu, 17 Sep 2009 17:53:24 -0700

That’s not good. When we investigated, we checked the network adapters on both nodes of the clusters. Each node had multiple NICs, including those for the public network, those for the private (heartbeat) network, and those for a dedicated log shipping network. All of the IP addresses were as built, and not APIPA addresses. However, from an active node, when we would ping the same box by name, we would get the APIPA address. The plot thickens!

When doing an IPConfig from the active node, we saw this adapter pop up:

Ethernet adapter Local Area Connection* 8:

Connection-specific DNS Suffix  . :
IPv4 Address. . . . . . . . . . . :
Subnet Mask . . . . . . . . . . . :
Default Gateway . . . . . . . . . :

This is the Microsoft Cluster Virtual Adapter that is present in cluster implementations on Windows Server 2008. Simple enough, we’ll just move it further down the bind list, right? Nope. The Microsoft Cluster Virtual Adapter does not show in the Adapters and Bindings property page – so we can’t adjust that. Time to roll up the sleeves.

The solution? Editing the hosts file. By placing an entry in the hosts file of each CCR node, the problem went away. Only an entry for the local host is required and should point to the address assigned to the public NIC for itself. An example on one we used: emx21

Once this was completed on each node, the problem stopped, and headers indicate the proper address now.

Received: from ([]) by
 ([]) with mapi; Thu, 1 Oct 2009 09:16:02 -0700

If you’ve deployed Exchange 2007 on CCR, check the message headers coming from mailboxes on those clusters. A 2 minute fix is all you’ll need.