This post was originally published on the blog of my employer, Mollie, in March of 2023.

The notion of “Migrating MDM” is something that makes a lot of Mac Admins nervous, and rightfully so. Apple doesn’t make it easy to seamlessly re-enroll your devices in a new MDM provider, requiring mandatory user action on their devices to complete the re-enrollment process. If the end user doesn’t (or can’t) complete the enrollment into the new MDM, then the device is now no longer managed, and this can be a headache.

In moving to an MDM that better suited our needs, we learned that it is possible to migrate your fleet, if you can make the migration easy and well-documented enough that end-users are empowered to migrate themselves. Ultimately, we moved almost 900 machines to a new MDM in just a little over 4 weeks. So how did we do that?

Some background

Jamf Pro was the first MDM solution we ever used, starting back in 2019 (which is basically ancient at Mollie). The initial implementation was carried out by one of our suppliers, and had seen several owners in the years since then. Configurations had piled up, and our Jamf Pro server had started to require a fair bit of maintenance overhead. At the same time, our patching situation regarding Applications and OS versions wasn’t great (with some Mollies even running versions of macOS that predated our usage of Jamf Pro). Jamf Pro also required a fair bit of custom work for some things (e.g. DEPnotify for onboarding), and ultimately needed a complete rework to get it into a nice, maintainable state that would work for us going forward.

This led us to start looking at other MDM solutions. If we basically had to redo everything and start from zero, it was worth having a look to see what else was available. During this search, Kandji caught the eye of IT Engineering. Kandji promised a much nicer patching system with Auto Apps and (more importantly) Managed OS. They also offered a built-in Out-of-box-experience, which provided a smooth (and intuitive) guide to setting up a Mac for the first time. This provided us with an easy way to reduce the workload of patching our fleet, and reduce the amount of bespoke tooling we had to maintain to achieve useful functionality.

Impressed by this functionality, we decided to go ahead with Kandji. Time pressure for this migration came from us deciding to go with Kandji only 8 weeks prior to our Jamf Pro renewal. While Jamf Pro Cloud instances generally don’t stop functioning on the day after a contract ends, it wouldn’t be wise to rely on the instance staying up as a means to manage our device fleet. We set a deadline of August 29th for the migration to commence, giving us just under a month to completely empty out Jamf Pro.

Getting prepared

Aside from some Mac minis used for presentations, we follow a 1:1 deployment1 model. Before the migration, we wanted to identify which employees had devices actively in use, and which device(s) they were using. This way, if we noticed that a migration had been started (device unenrolled from Jamf Pro), but the device hadn’t appeared in Kandji, we knew who to reach out to. At the time of the migration, we did not yet have an IT Asset Management solution in place with all of our assets in it. As a result, we relied mostly on Jamf Pro’s own inventory (and some custom Extension Attributes) to identify who was using a machine. We then identifed from our HR system which employees qualified to be migrated - excluding employees on garden leave, or other forms of long-term leave.

From this information, we created a massive Google Sheet of all the serial numbers that had to be migrated, and their associated user. This was our planning and tracking document; all of us in IT Engineering would track migrations and record them here. We created a Slack channel for Kandji enrollment notifications to feed into, using their Slack integration, so that we could track migrations and update the sheet.

Migrating Macs

One of the neat things that Kandji did was provide us with a migration agent that we could deploy onto machines from Jamf Pro, which would move them over to Kandji. The principle of this agent is that you can reassign all your Macs from your current MDM to Kandji in Apple Business Manager2, deploy the agent, have users click a few buttons on their end, and the device is migrated. The agent does cool stuff for you, including checking if the device is currently enrolled in Kandji (at which point it removes itself).

But, we wanted more. We wanted to make sure it was super clear to anyone who triggered the migration from Jamf Self-Service (JSS) what was about to happen, and what they had to look out for. We had to make sure that, armed with a short set of instructions, anyone could migrate themselves.

A macOS notification that reads 'Device Enrollment: Mollie B.V. can automatically configure your Mac.'.
This one notification has a lot of explaining to do.

We decided to leverage the wonderful swiftDialog to create an instructive pop-up right when the migration is ready to commence3. Once Mollies had read the pop-up, they could click “I’m ready!”, which would trigger the migration notification. This meant that they were not only aware of what they could expect from the migration, but hopefully felt more a part of the process (instead of feeling that things were just happening to them which they had no agency over).

A macOS popup, with the previous image at the top of it. Text underneath reads: 'Migrating to Kandji: when you click Im ready, a dialog will appear in the top right of your screen as shown above. Please click on the dialog when it appears.'. The Mollie logo is on the left-hand side of the dialog, and in the bottom right there is a button that says 'Im ready'.
Describing what is about to happen.

Because the Kandji migration agent is just a shell script that gets run with a LaunchDaemon4, we were able to easily add these extra bits that we wanted, and remove things that weren’t relevant to us. Another thing we needed that the Kandji agent didn’t offer was a way to get your machine out of Jamf Pro. Because (most of5) our machines are ADE-enrolled6, the profile is non-removable on the client-side. This presented a problem, as you can’t install one MDM profile over another - you have to remove the previous enrollment before starting the next one. To make the migration as self-service as possible, we created a user on the Jamf Pro instance with one permission - to unenroll devices from Jamf Pro. We embedded this account in the migration agent, and during the preparation phase, the agent would tell Jamf Pro to unenroll the computer it was running on. The agent would then wait until this was complete, and then allow for the enrollment into Kandji to be triggered.

A high-level overview of the migration flow

This did mean that we would have machines that were briefly not in any kind of MDM, but as mentioned before, we knew which machines we were migrating (and who to reach out to). Additionally, we were already using MunkiReport for monitoring Macs, so we simply modified the MunkiReport agent to include a fallback configuration to keep talking to MunkiReport, even when the configuration from Jamf Pro was removed during unenrollment. This meant we could actively see the status of clients who were between MDMs, and reach out to users if we noticed they’d been in that phase for longer than expected. The LaunchDaemon for the migration agent also triggered the pop-up very frequently (every 5 minutes), so Mollies that were between MDMs were aware that they weren’t in Kandji yet, and reached out for assistance.

Assigning apps: who gets what?

A feature that Kandji didn’t have when we migrated was scoping rules that were as solid as those in Jamf Pro7. Software items could only be assigned to Blueprints, which are monolithic definitions for how a device should be configured. A device in Kandji can only be assigned to a single Blueprint at a time. Jamf Pro offered very powerful assignment rules, allowing you to assign almost anything to almost any Mac (or group of Macs).

Because we have several different teams (some with licensed software that they need), we wanted to scope that licensed software to the right groups. However, going through the list of employees and manually assigning their blueprint to their machine was something that definitely would take up too much time, so we came up with a better solution.

A flow diagram showing how clients send their serial numbers to Okta workflows, and based on the user assigned to that serial in Kandji, a decision is made about which Blueprint a client should receive from Kandji.
Our better solution

Using Okta workflows, we set up a flow that would use the Kandji API to move Macs between Blueprints. We installed and ran a small script on every machine that was newly enrolled in Kandji immediately during enrollment, which sent the devices serial number to this Okta worflow. The workflow would then look up the device in Kandji, check the department attributes for the employee assigned to that device, and assign the appropriate Blueprint. If a device was not assigned to a user, it would get the General Blueprint by default.

This approach allowed us to handle having new employees enroll in Kandji before we started the migration, so that we weren’t migrating new joiners on their first day. The integration with Okta workflows also meant that we could easily have a little ad-hoc User Acceptance Testing group - the first 100ish Mollies to migrate were added to a Slack channel, where they could share their thoughts on opinions on the strengths and weaknesses of the migration process.

The rollout

Once we were happy with the agent and our deployment plan, we tested it dozens of times on test machines. IT Engineering as a whole were first to be migrated, to check if there were any unexpected surprises, leading to some small tweaks to text and timing. Then, on August 25th, we soft-launched the Kandji migration during an IT soapbox session, inviting attendees to try out migrating. First impressions were positive, with a handful of people migrating themselves during the week leading up to go-live. We ironed out some more bugs in this time, and we were feeling confident for the rest of the rollout. So confident, in fact, that we set the official migration deadline to be September 20th. We anticipated that there would be employees on various kinds of leave throughout the migration, so we planned to wipe their devices upon their return (dependent on whether our Jamf Pro instance was still up). On the 29th of August, we posted a message in #general on Slack and through other internal communication mechanisms announcing the migration. On this first day, we saw 56 Mollies migrate themselves, which was a pretty decent chunk of our employee population. Our next big spike came on the 1st of September, when a servicedesk ticket was created for everyone who had yet to migrate, triggering a notification email. This caused more than 10% of the staff population to migrate in a single day, which was the biggest migration day we saw throughout.

A cool graph to show Kandji uptake

We migrated the last employee who wasn’t out on long-term leave on October 3rd, after our Jamf Pro instance did not immediately disappear. We successfully managed to migrate the entire org, excluding those on leave, prior to the September 29th deadline, with over 90% of employees having been migrated prior to our self-imposed deadline of September 20th.

Looking back

Overall the migration was highly successful, exceeding even our own expectations, although that’s not to say it was without issue. When migrating this many devices, you’re almost guaranteed to run into every quirk that Apple’s MDM framework provides, the most notable of which being the migration notification occasionally not appearing.

Another pain point was Kandji’s Managed OS upgrading Macs to the latest release of macOS post-migration. While this was a necessary step (and our macOS update compliance is night and day compared to before), buggy behaviour in macOS’ softwareupdate binary meant that updates occasionally failed, extending the duration of migration sessions for some employees.

Migrating from one MDM to another may seem scary, and it’s still a task that should be taken seriously. As we learned however, it’s also totally doable, and making it easy for your end-users to be involved saves you from having to wipe every device to do it.


  1. Each Mac has only one user. ↩︎

  2. A sort of database of all the Apple devices Mollie has purchased, which tells your device which MDM it should talk to when you set it up (or whether it should talk to one at all). ↩︎

  3. Unfortunately we had some devices on versions of macOS prior to Big Sur that needed to be migrated, which swiftDialog does not support. We used jamfhelper for these devices, which was a little less glamorous but just as informative. ↩︎

  4. A task that runs in the background ↩︎

  5. We had a couple of older machines that either predated our ABM registration, or were purchased incorrectly and weren’t in ABM. These machines were replaced instead of migrated. ↩︎

  6. Automated Device Enrollment. This is the process that generally happens when setting up your Mac for the first time. The opposite of this is User Enrollment, where you may enroll your device in an MDM at your own leisure, and remove the MDM configuration whenever you like. ↩︎

  7. Kandji introduced much better scoping at the end of last year, which will allow us to simplify our environment immensely: ↩︎