There are lots of automated tools for migrating data from Google Apps to Office 365, including Dell's Quest OnDemand Migration for Email and Agile IT's AgileAscend. You should consider using one of these products -- even if you're switching over just a few users like I was -- or it will just take too long.
Thinking way too much of my own skills, I assumed that the process would be time-consuming but not all that challenging. The migration took me more than two days -- although I was working on other things at the same time – and I'm still cleaning up small problems it caused.
If you look up migration techniques on the Internet you'll see a lot of bad advice, mostly about using IMAP. Fortunately, you'll have to go this route only if you're one of the grandfathered-in free Google Apps users. If you're a Google Apps for Business customer, you have Exchange ActiveSync support for access to your calendars, contacts and email, and this is the secret to a much more straightforward migration. If you haven't been using Google Apps Migration For Microsoft Outlook and Google Apps Sync For Microsoft Outlook to access your Google Apps email, calendar and contacts, now is the time to set it up.
There are two ways to proceed: In Outlook, export all the data from each account to a PST file. Then, after connecting to your Office 365 account, import the PST file. The second way is to connect to both the Google and Microsoft accounts from the same instance of Outlook and drag and drop the content from the former to the latter.
It sounds so simple, and eventually it worked, but it wasn't as easy as it sounded. First, I tried the second, drag-and-drop method. But I was dealing with some very large accounts with many gigabytes of data, and Outlook was not up to the task. I don't know what caused it, but whenever I tried either drag and drop or copy and paste, Outlook would become unresponsive. It might have been that the migration was actually being performed and only the UI was unresponsive, and that if I left it for many hours it would complete, but I decided that wasn't a reasonable use of my time.
The other method worked better, although Outlook also dislikes very large PSTs and becomes unresponsive when importing them. I got better results by creating partial PSTs -- just the calendar, just the contacts, and different parts of the email -- and importing them one at a time.
The main problem I was left with -- and this is a common problem -- is duplicate entries in the contacts. No matter how often you tell the import program not to import duplicates it does it anyway (pictured).