Pulling Client Information from Paymo

October 17th, 2010

We’ve recently started using Paymo to track our time and send invoices.  So far I’ve been fairly impressed, it seems to do a good job.

We charge some of our customers a set amount per month that includes up to x hours of work during the month.  It would be convenient for them to have a way to check to see how much time we’ve spent during the month (so they can keep track of how much time they have “left” for the month).  Now that our time tracking is done in the cloud, I realized it would be possible to create a page that they could view this information on.

To keep things simple, I will just send our customers a link to view the monthly time for their project.  I was a little worried about security though – the script receives the project ID number from the querystring.  To keep someone from guessing the project ID for another customer’s project, I added a SHA1 hash onto the querystring.  When I send a link out to a customer, I generate a SHA1 hash of the project ID concatenated with a secret key.  The script then concatenates the project ID that its received with the secret key, and checks that with the hash that is sent to it through the querystring.  The actual link a customer would receive looks kind of like this: http://www.example.com/paymo-time.php?pid=12345&hashsig=aBigLongSHA1Hash

The trickiest part was probably getting the XPath parts right.  The Paymo API sends XML data back after you make the request, so I use XPath (through PHP’s SimpleXML) to pull out the relevant parts.  Basically, this script:

  1. Verifies that the hash is valid
  2. Authenticates to Paymo
  3. Pulls the list of billable tasks for the given project
  4. Pulls the amount of time spent on each of those tasks in the current calendar month
  5. Adds the time together and displays the total for the whole project

 

<?php
$project_id = $_GET['pid'];
$hashsig = $_GET['hashsig'];

$salt = "RandomStringOfCharacters";
if (sha1($salt . $project_id) != $hashsig){
    die("Invalid Authentication Token");
}

$api_key = "–MyPaymoAPIKey–";
$api_username = "–MyPaymoUsername–";
$api_password = "–MyPaymoPassword–";
$auth_token;
$start = date("Y-m-d H:i:s", strtotime(date(‘m’).’/01/’.date(‘Y’).’ 00:00:00′));
$end = date("Y-m-d H:i:s", strtotime(‘-1 second’,strtotime(‘+1 month’,strtotime(date(‘m’).’/01/’.date(‘Y’).’ 00:00:00′))));
$auth_token = paymoAuth($api_key,$api_username,$api_password);

$a1 = array(
    ‘api_key’ => $api_key,
    ‘auth_token’ => $auth_token,
    ‘include_task_lists’ => 1,
    ‘include_tasks’ => 1);
$tasklist = paymoAPI("paymo.projects.getList", $a1);
$project_name = $tasklist->xpath("/response/projects/project[@id=" . $project_id . "]/@name");
$project_name = $project_name[0];
$tasks = $tasklist->xpath("/response/projects/project[@id=" . $project_id . "]/task_lists/task_list/tasks/task[@billable=\"1\"]/@id");
$time = 0;
foreach ($tasks as $task)
{
    $a2 = array(
        ‘api_key’ => $api_key,
        ‘auth_token’ => $auth_token,
        ‘task_id’ => $task[0],
        ’start’ => $start,
        ‘end’ => $end);
    $taskinfo = paymoAPI("paymo.entries.getTrackedTimeByTask", $a2);
    $tasktime = $taskinfo->xpath("/response/time/text()");
    $tasktime = $tasktime[0];
    $time += $tasktime;
}
print "<h1>Monthly Time for " . $project_name . "</h1><br/>";
print "<b>" . date("F Y") . ": </b>";
$time = mktime(0, 0, $time);
print date("H:i", $time) . " (HH:MM)";

function paymoAuth($api_key, $api_username, $api_password)
{
    $args = array ( ‘api_key’ => $api_key, ‘username’ => $api_username, ‘password’ => $api_password );
    $auth_token = paymoAPI("paymo.auth.login", $args)->xpath(‘/response/token/text()’);
    return $auth_token[0];
}
function paymoAPI($method, $args)
{
    $base = "
https://api.paymo.biz/service/" . $method . "?";
    $qs = "";
    foreach ($args as $key => $value){
        $qs .= "$key=" . urlencode($value) . "&";
    }
    $url = $base . $qs;
    $result = file_get_contents($url);
    $xml_result = new SimpleXMLElement($result);
    return $xml_result;

}

?>

Cloud Computing in Africa

September 29th, 2010

Cloud computing has quickly taken off in the US, and is in large part changing the way organizations here use technology.  What issues come up when the cloud computing model is used in less developed parts of the world?

Services such as Amazon AWS and Rackspace Cloud offer great computing and storage services at flexible prices.  In addition, they allow users to take advantage of first class datacenters and connectivity.  The reliability and ease of use that these services offer make them attractive to many organizations, including non-profits and university sponsored partnerships with locations spread across the world.

However, the limited connectivity options available in many parts of Africa (particularly where I visited in Malawi and Moshi, Tanzania) make utilizing cloud computing services difficult.  Satellite based connections are typically quite reliable (as long as you have power).  Their high latency and somewhat limited speeds however, effectively make them a long narrow road between you and your resources in the cloud.  Local Internet service providers may provider faster speeds and slightly lower latency, but in my experience aren’t yet as reliable as they need to be.  This will change in the future.  More upstream providers will be available soon (see EASSy) and existing providers like SEACOM are becoming more reliable as the kinks are worked out.

For organizations in regions with limited connectivity, cloud computing is best used in situations where:

  1. The service is used mostly by people offsite, in the US, EU, etc, or
  2. The service uses little bandwidth, and will work with high-latency or unreliably connectivity

In the first situation, these services might include websites targeted at people in the US / EU (such as websites to encourage donations, raise awareness, or provide information to volunteers who will be traveling onsite).  If data collected on the ground will need to be analyzed, or accessed by staff here in the US, cloud storage might be a viable option as well.  Cloud computing systems like EC2 make it easy and inexpensive to temporarily use large amounts of computing resources to analyze data. 

Everyday examples for the second situation are a little harder to come by.  Cloud storage services can be effective for offsite backups, if the amount of data can be easily copied in bulk overnight.  On a satellite connection with 512 kbps of upstream bandwidth, around 2.5 GB of data could be uploaded in a 12 hour period.  That is likely large enough to back up a single day’s worth of data.  It’s the initial full backup of everything, and a full restore from scratch that would pose the biggest issues.

If you’re in a situation like this and need help deciding what options are best for your site with limited connectivity, I’d love to help.  We can also help tweak your network to make sure it allows your cloud computing service to perform optimally.  Comment, give me a call, email me, or message me on Twitter if I can help in any way.

My Experiences with DD-WRT

September 15th, 2010

I recently set up DD-WRT on my router at my apartment.  While it is quite a bit cheaper to buy a consumer class device and load DD-WRT on it, compared to purchasing a traditional business class device, there were several aspects of DD-WRT that I was disappointed with.  I’m not sure I can really recommend using DD-WRT in a business environment unless your needs are relatively simple.

Hardware

I am using DD-WRT on a Netgear WNR3500L.  I specifically bought this device to use with DD-WRT after researching what devices will work best with DD-WRT.  I was specifically looking for something that had enough memory to be able to use OpenVPN, and that could support multiple SSID’s / VLAN’s.  802.11n support wasn’t crucial for me, but it was a “nice-to-have”.  The WNR3500L does support all of those. 

I had no real problem flashing the firmware to install DD-WRT.  If I recall correctly, there was an interim firmware that has to be used first before you can upload the full version of DD-WRT.  As long as you follow the directions / tutorials from the DD-WRT site you should have too much trouble.

Site to Site VPN

My most important requirement was to set up a site-to-site VPN to another LAN that hosts my servers.  I use OpenVPN for a road warrior style VPN for when I’m working remotely, so I intended to use this as well for the site to site VPN.

The endpoint on my other LAN is a CentOS box.  Unfortunately, DD-WRT makes it difficult to use a custom OpenVPN config on the device.  A lot of things, like TLS Auth or having multiple remote endpoints (for reliability) aren’t really possible to do. 

Furthermore, the OpenVPN software is hard-coded to NAT the connection.  This means that the remote network you are connecting to cannot see your local network – only IP address on the VPN interface on the DD-WRT router.  This was a ridiculously bad decision on the part of the DD-WRT developers / maintainers.  In almost no conceivable circumstance would you want to NAT a site-to-site VPN connection.  It does this by calling the route-up.sh script in the /tmp/openvpncl directory. 

After hours of frustration, I eventually found a solution that works.  I added a startup command that disables the NAT’ing by running the route-down script, and then restarts OpenVPN without running the route-up script.

( sleep 20 ; killall openvpn ; /tmp/openvpncl/route-down.sh ; openvpn –config /tmp/openvpncl/openvpn.conf –daemon ) &

This works, but its still ugly in my opinion.  Due to the way DD-WRT works its not possible to actually edit the OpenVPN configuration file.  The changes won’t persist after a reboot.  This makes it much more difficult to customize and configure than it would be if it were just a normal Linux box with OpenVPN installed on it.

VLAN’s

Configuring the separate VLAN’s turned out to be a lot more difficult than I was expecting.  In my opinion, the naming scheme for interfaces and the web interface to add VLAN’s / associate interfaces with VLAN’s is inconsistent and illogical.  Wireless interfaces are treated completely different than wired interfaces and can’t be added to what DD-WRT considers the VLAN.  The wired interfaces are added to the VLAN, and then that resulting VLAN is bridged with the wireless SSID (s).  Complicating matters it the fact that the LAN port numbers as shown by DD-WRT may or may not be the in the same order as they are on the device. 

Here is a diagram showing how the physical interfaces, VLAN’s and bridges are connected in my example:

image

This screenshot below shows the interface for adding SSID’s.  Note, for SSID’s to be assigned to different VLAN’s each SSID has to be set to be bridged.  The wireless security tab is where you can configure the authentication / encryption mechanism for each SSID. 

image

Here is the interface for adding LAN ports to VLAN’s.  Note the inability to add a wireless SSID to a VLAN.  Also note the “Assigned to Bridge” column – the “bridge” that the VLAN is assigned to here has nothing to do with the bridges configure in the next screenshot.

image

 

This is the place that you connect the wired VLAN’s to the wireless SSID’s.  Then you put an IP on the resulting bridge interface.  (The IP on the default bridge, br0 is configured on the Basic Setup tab).    A couple items of confusion to pay attention to here.  First, the name “Bridge 0″ is not related to the interface name “br1″.  Secondly, if the SSID’s are set to be bridged, they will all be bridged to br0 by default.  You only have to add bridge assignments for VLAN’s / SSID’s that you don’t want to be bridged to br0.

image

A little further down on this same page is where the WAN VLAN assignment (VLAN 2 by default) is configured, along with the DHCP service offered for non-default bridge interfaces is setup.

image

Capturing Traffic with tcpdump

The WNR3500L has a USB port.  With a flash drive, you can add some persistent storage to DD-WRT.  After formatting and mounting the flash drive I installed ipkg so I would be able to install additional software on the flash drive.  The DD-WRT site has a good explanation of how to do this here.

From time to time, I find it useful to enable traffic capture on the router for troubleshooting.  I was able to install tcpdump through ipkg on the flash drive.  I can run tcpdump to dump traffic to a file on the flash drive.  Then, I can transfer the resulting file back to my computer via SCP and open it up with Wireshark to analyze it. 

Issues: Routing Daemon

For me this was extremely disappointing.  The quagga routing software that comes with DD-WRT will not function.  I found a post on the DD-WRT forum that explained this was because quagga was compiled incorrectly in this version of DD-WRT.  As far as I know this has not yet been fixed.  To me, the routing software is one of the most important aspects of a router.  This is something that should have been tested before the firmware was released.  The fact that it was not, makes me question how much I can really rely on DD-WRT.  If something this significant could slip through the cracks, what else have they messed up?

So instead of using OSPF over the site-to-site VPN to easily tell my other LAN how to reach this site, I have to add static routes on both ends.  This is pretty annoying and shouldn’t be necessary.  In a larger environment this would be a show-stopper.

Other Stuff

One feature I do really like is the Wake-On-LAN support.  You can easily add a computer on the network to a list of Wake-On-LAN hosts that you can remotely start with the click of a button.  With this, I can leave my desktop turned off, saving power, but easily start it up if I need to remote in to it. 

image

DD-WRT uses DNSMasq by default for providing DHCP and DNS forwarding service.  In my environment, I need a couple internal domains to be resolved by DNS servers across the site-to-site VPN.  This is easy enough to do with DNSMasq, by specifying additional options in the DNSMasq section of the Service tab.  In the example below, DNS requests for the two domains specified will be forwarded to one of the two servers specified for each. 

server=/internal-domain.com/IP-of-DNS-Server-1
server=/internal-domain.com/IP-of-DNS-Server-2
server=/internal-domain2.com/IP-of-DNS-Server-1
server=/internal-domain2.com/IP-of-DNS-Server-2

I have not tried updating to a newer version of the DD-WRT firmware.  However, from reading the DD-WRT wiki, it sounds like its not possible to upgrading without clearing the configuration settings and starting over.  To me, this seems like a pretty serious issue as well.  It’s taken quite a while to set everything up and find workarounds for some of the limitations of DD-WRT.  To have to do that all over again when I update the firmware would be a nightmare.  On Cisco IOS devices, you can easily use the same configuration on different versions of IOS, provided the new image supports all of the features used in the configuration.

In summary, while I was eventually able to get DD-WRT to work well enough for me, I can’t really recommend it.  It has a lot of potential, but a lot of the features really need to be polished up before a business can rely on them.  The interface for managing VLAN assignments for example, seems to be an afterthought rather than a primary focus of the device.  Businesses that need these features would probably be better off purchasing one of the Cisco 800 series routers.  While the Cisco hardware is more expensive and some of Cisco’s practices are maddening (like charging for access to updated images), it’ll still be cheaper when you factor in the time to get it up and running.  Configuring VLAN’s, site-to-site VPN’s, etc, on a Cisco router will take much less time than it does with the DD-WRT firmware.

Multiple Connections via Multiple Routers…Part 2

September 12th, 2010

As promised here’s the second issue we ran in to when trying to resolve the first.  

To recap the last post, responses to the traffic coming in to the Tidziwe network through the secondary connection, were being sent back out through the primary connection, because of the default gateway.  This wasn’t working, because the response traffic was being NAT’ed to a different address as it went back out through the primary connection.  We were able to partially get around that by adding NAT entries on the primary router to NAT the source address on that traffic to the correct address for the secondary connection, even though it was really egressing through the primary connection.

This ugly fix appeared to work pretty well at first.  From where I’m located, I was able to get to the publically accessible services through both the primary and secondary connections.  Unfortunately, some of the people located in Malawi weren’t so lucky.  In fact, anyone that used Tonse (the provider of the secondary connection at Tidziwe) as their ISP in Malawi was unable to access the webmail and other services through the secondary connection.  This at first didn’t make sense – if anything it would seem logical that accessing it from the same ISP would have fewer problems, not more.

Long story short, Tonse was dropping the response traffic as is entered their network, which is a reasonable thing to do given the circumstances.  The packets said they were coming from an address inside of Tonse’s network (2.2.2.2 in our example below).  Yet, how could this be given that the packet was coming from Tonse’s upstream provider?

 

NAT Issues2-Clean IPs

Unfortunately in the short term, we don’t have many options besides our current ugly hack.  To get around this additional issue, I added a route map on the Cisco router (the default gateway).  The route map routes outgoing traffic through the DLink if it is:

  1. Going to Tonse’s network, and.
  2. Coming from one of the services that can be accessed through the Tonse connection

Here’s what the configuration looks like:

int LAN-Interface
    ip policy route-map Tonse_Services

access-list 185 remark Used for Tonse_Services route map
access-list 185 permit tcp host 10.1.1.1 eq 443 41.76.0.0 0.0.255.255

route-map Tonse_Services permit 10
    match ip address 185
    set ip next-hop IP-Of-DLink

So far, no major problems with this.  Hopefully we’ll be able to use both connections through a single device in the future, helping us avoid these sort of issues altogether.

Multiple Connections via Multiple Routers…Part 1

August 30th, 2010

We recently discovered what was causing some of the connection issues over the WiMAX connection in Malawi.

There were two issues, but one sort of builds on the other.  The first issue occurs when someone accesses a publically accessible service through the IP from the secondary ISP, Tonse.  We have two routers at this site.  A Cisco 1800 series router is connected to the main satellite connection.  We’re using a DLink VPN router on the Tonse connection, for a site to site VPN to another DLink VPN router at a satellite clinic.

The Cisco router is set as the default gateway on the LAN at the main clinic site.  The Cisco router has no information

It only see’s the outgoing traffic, not any of the incoming traffic that goes through Tonse and the DLink router.

image

1. Users browses to AtMail webmail using Tonse address.

To: 2.2.2.2
From: 5.5.5.5
2. DLink translates destination IP address to internal server.

To: 10.1.1.1
From: 5.5.5.5
3. Internal server replies, sending traffic to default gateway (Cisco router).

To: 5.5.5.5
From: 10.1.1.1
4. Cisco router has no record of incoming connection, translates source address to Ariave IP.

To: 5.5.5.5
From: 1.1.1.1
5. End user’s computer receives the response, but it is not from the address it connected to. The TCP/IP stack does not see this is a response to the TCP connection request, so it discards it and the connect is never established.

This is rather ugly because the Cisco router sends traffic out with a source IP address that doesn’t belong to it (and, is in fact on a completely separate network and service provider from the one it is connected to).  It is also rather ugly because it is unclear (at least to me), how the Cisco router is choosing which NAT entry to use for translating the outgoing traffic.A ugly work-around for this issue is to add NAT entries on the default gateway (the Cisco router in this case).  So, in addition to have the service NAT’ed to the public IP the router itself uses, it also has a second entry in the NAT table connecting the same internal IP / port to the public IP the other router/connection uses.  This appears to cause the Cisco router to to translate the source address of the outgoing traffic to the public address of the other router/connection if it is not part of an existing connection that already came in through the Cisco router.

Doing this however caused the second issue, which I’ll post about in part 2.  It’s a great example of why you shouldn’t do things the “ugly way” if at all possible.  Stay tuned!


Employee Directory for Sharepoint

August 25th, 2010

I actually wrote this while I was in Malawi, forgot to post it until now though:

Earlier in my trip, while I was setting up Windows Sharepoint Services v3 for the folks at Tidziwe, I came across a problem that’s surprisingly common in the Sharepoint world.  They wanted an employee directory on their intranet homepage.  They also wanted department directories, with additional information in them on each of the department sites.  In my mind, the perfect situation would be pulling this information from Active Directory.  That way, the administrative effort is minimized, and there aren’t additional tasks that have to be done for each new user.

Unfortunately, the standard “People and Groups” list doesn’t work too well in this scenario because WSS only pulls the Active Directory attributes in once.  If changes are made in the future to the Active Directory object, those changes won’t be reflected in Sharepoint.  Also, instead of adding the AD user accounts directly in to the Sharepoint groups, I’m using existing AD groups as an intermediary.  (Again, this should cut down on the extra administrative work that has to be done to give a user access – once they are added to the department group in AD, they should have access to Sharepoint, any file shares, etc.)  The People and Groups list does not work at all in this case – it just shows the AD group that’s been added, and not the individual users.  It’s kind of disappointing that Microsoft releases products that integrate so poorly with Active Directory.

So, realizing that an out-of-the-box solution was not going to work here I started to look at custom webparts, and other means with which I could achieve what I wanted.  It appears that there are several expensive 3rd party webparts out there that try to fill this functionality gap.  Still, I felt I could solve this quicker (and cheaper) on my own, so eventually I realized how I can do this.

Our department directories don’t necessarily have to be pulled from the Sharepoint groups, instead I felt that filtering the users based on the “Department” attribute in AD would be sufficient here.  I realized that using DataViews in Sharepoint Designer can pull in data from a variety of sources, I eventually realized I could use a web service to pull the data from Active Directory and pass it in to the DataView.

I created a web service in ASP.NET that returns the user information (first/last name, department, email, phone, etc) from Active Directory.  The web service has a couple methods, one returns all the Active Directory users, the other only returns the users from a particular department.

On the main employee directory, I have a DataView WebPart that uses the web service as its data source.  It lists information for all users.  I have it grouping the employees by their department to make it easy to find.  Each department site also has a department directory for added convenience.  These ones use the second method that just returns the employee information for a particular department.  They both have clickable mailto: links for the email addresses.

Employee Directory Screenshot

Sharepoint Employee Directory

Altogether this seems to work pretty well, it does what it’s supposed to.  There might be some performance issues in larger environments with more hits and more users since it doesn’t currently do any caching.  That shouldn’t be too hard to add if needed however.

If any one is interested in this, send leave me a comment or send me an email. What we’re using can filter the Active Directory users based on the Department attribute, though I tested using groups to do the filtering as well.

Intel Wifi Power Save Polling Issue

August 22nd, 2010

At work we came across an interesting problem with the Intel wifi cards in the Dell E-series Latitudes. People get disconnected from their home wifi networks. It’s happened for at least two people. Their notebooks work fine on the enterprise wide wireless networks.

It turns out that many consumer quality access points that people use at home do not properly implement a power saving feature that the new Intel cards support, called “power save polling”.

The fix is to go into the wireless card device properties in device manager, and change the power management settings. Unchecking the “default/auto” checkbox and dragging the slider over to “Maximum/High Performance”.

Intel has a knowledge base article up on their website here: http://www.intel.com/support/wireless/wlan/sb/cs-006205.htm

Another reason to stay away from the consumer brand network hardware!

Blackberry Wifi Issues

June 25th, 2010

After my return from Africa, I finally got my new Blackberry that I had been waiting for, the Bold 9650. On vacation, I was able to connect use a wifi connection to connect to my BES Express server with no problem at all. Back at home though, it looked like I was able to connect to wifi, but not to the BESx box, that’s on the same internal network.

Initially, I was thinking it was an issue with the wifi connection being used to connect to BES. I tried adding the wifi profile through the policy on my BESx. On further inspection, I realized the device was not actually connected to my wifi network. The wifi diagnostics said it was connected, and it received an IP from my DHCP server. I couldn’t ping anything though. From researching on the web, I found that there might be some issues with using WPA1 personal (with a pre-shared key). I use AES instead of TKIP. It’s a best practice, and its more secure than TKIP. Unfortunately, it appears that the Blackberry doesn’t work properly with this specific combination – WPA1 personal with AES. After switching from AES to TKIP my BB connects with no problem. BES/BIS over wifi works perfectly.

I haven’t tried any other combinations. At my new apartment I’m going to be setting up a new wireless router with dd-wrt. It will support WPA2, so I’ll see how that works.

Trip to Tanzania

June 14th, 2010

 

On the way there we flew through Lusaka and Nairobi, going a little bit out of the way.  Flying back we stayed a day in Nairobi.  It happened to be on Madaraka Day, a holiday in Kenya celebrating when they first obtained autonomy from Britain. 

image

 

Our hotel was cool, I had a huge room.  I’m not sure how anyone could use all that space.  One of the IT guys from Moshi showed us some of the night life there as well.

f2936576 f2958912 f2939904

While we worked most of the time while we were in Moshi, we did get some gorgeous views of Kilimanjaro through the clouds and from the plane:

f2969216 f3051200

We also were able to swing through Nairobi National Park while we were staying there.  While it’s partially surrounded by Nairobi, it’s huge. You certainly don’t feel like you’re in a city while you’re driving around inside.  There were a fair amount of wild animals inside – its not anything like a zoo, the animals weren’t brought there for the park, they were there from the get go.

b1741131 b1840971  b1831835

b1818763

The city of Nairobi was interesting in and of itself as well.  It definitely had more to offer than anywhere else I visited in Africa.

b1903683 f3163648 f3169408

 

All in all, it was a good trip.  Would have been better if my luggage hadn’t got lost but, that’s life.

100_4786 100_4781

Displaying non-Exchange AD users in Global Address List

June 8th, 2010

Yesterday and today I’ve been installing and configuring Microsoft Exchange 2010 here.  Right now this is just a pilot, for a few people here to try Exchange and see if its worth completing moving over to Exchange from AtMail, the email system that’s currently being used.  To make life easier, I wanted to display all Active Directory users in the Global Address List (GAL), including users who don’t have Exchange mailboxes right now.  Instead they have the Email attribute set on their user object in Active Directory.

Exchange will only include users who have been mail-enabled and users that have Exchange mailboxes in address lists (as well as groups, rooms, etc).  In and of itself, this is frustrating because we now have to modify all the user accounts in AD to mail-enable them (even though they already have an email address attribute, there are additional Exchange attributes that have to be added to the object).  To make it worse however, the Exchange Management Console GUI can only do this one user at a time, and it doesn’t use the existing email attribute.  So (as far as I can tell) there is no built-in GUI way to mail enable large numbers of users. 

Fortunately, since 2007 the Exchange management tools have been largely based around PowerShell.  This makes it easy to script stuff like this.  The get-user command will list all of the users from Active Directory.  The enable-mailuser command will mail enable them.  That part didn’t take long to figure out, though getting the commands to work took a little while.  The email address has to be passed into the enable-mailuser command, but using the user’s email address attribute itself doesn’t work, it has to be converted to a string first. 

The last part I figured out thanks to a post by Shay Levy on a Microsoft newsgroup that came up on Google: https://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.windows.powershell&tid=9b03c675-9b9c-431d-933b-4a00901c20c3&cat=en_US_3750E87B-4971-4A5C-A537-45F5D7ABBECC&lang=en&cr=US&sloc=&p=1.  Someone there was asking how to do something similar to this with contacts instead of users.

Here’s the command I used below.  At first, it would be best to start off testing with a single user:

get-user <username> | foreach { enable-mailuser $_ -externalEmailAddress $_.WindowsEmailAddress.toString() }

There are still some issues with this, it comes back with an error if the user has no email address specified in their AD object, and it also probably will come back with an error if the user is already mail enabled or if they have an Exchange mailbox, but it’s a start.

Copyright 2009 Simpliciti Solutions