← All writing

Using Microsoft Graph to integrate Autopilot into SCCM task sequences

A lot of IT work in a school district is balancing the old with the new: keeping legacy systems running while bringing in modern ones. This post is about a PowerShell script I used to bring Windows Autopilot into our imaging process, in an environment where some devices were managed by SCCM and others by Intune.

What you need

  • SCCM (Configuration Manager) task sequences for imaging
  • Devices registered in Windows Autopilot
  • An Entra ID (Azure AD) app registration with Intune read/write permissions, so the script can update devices and trigger a sync

The old way: fat images and provisioning packages

For years we used "fat" images, with every application preinstalled. When Intune first came in, for high school student laptops during the Covid lockdowns, technicians applied provisioning packages by hand on top of that fat image. They had to pick the right task sequence too: the standard one, or an Intune one that skipped the ConfigMgr client and rebooted to the out-of-box experience (OOBE).

The first two things I changed were dropping the fat image, so software installed during the task sequence, and merging the two task sequences, so the variables chosen in the UI++ step decided whether a device was set up for Intune and got the provisioning package before rebooting.

Some of that had to be walked back. Intune devices weren't ready when they were handed to people, because software was still installing after the first sign-in, and imaging took longer because software installed during the task sequence. I kept the merged task sequence but went back to the fat image, so imaging stayed fast and Intune devices had the basics installed before anyone signed in.

The new way: Autopilot and clean images

Later we moved all staff laptops, and student laptops at more schools, to Intune, and I wanted to make real use of Autopilot. My first idea was to run the Autopilot hash upload script from a flash drive at OOBE on new devices. I wrote a Windows Forms front end so techs could set the location and asset number for the device name, then upload the hash, wait about six minutes for the profile to be assigned, and reboot. It worked, but it added too many steps for the techs.

So it had to happen during imaging. The catch is that you can't upload a device hash from Windows PE, because it doesn't have the WMI namespaces the hash upload needs.

There was a second catch. We have a wide mix of models, some of them consumer devices, and having TPM 2.0 doesn't guarantee a device supports attestation for Autopilot self-deploying mode. (Our HP 255 G7 laptops were the main problem there.) I tested every student laptop model, and since they all worked, I added a task sequence step that sets a TPMSupport variable to true for those models. I've added other models as I've tested them. Devices that can't self-deploy still get the fat image, but we're moving in the right direction.

What the script decides

The script runs inside the task sequence and does two things:

  1. Checks whether the device is already registered in our tenant, which means it has an Autopilot profile.
  2. Based on that, the task sequence either lays down a clean image and boots to OOBE for the enrollment status page, or applies the fat image and uses a provisioning package to join Azure AD with a bulk enrollment token.

See the original script on GitHub

How the script works

  • Graph app credentials. The script starts with the client ID, client secret and tenant ID for an Entra ID app with Intune read/write permissions. Replace the placeholders with your own, and keep the real values out of source control.
  • Environment setup. It sets LOCALAPPDATA, which isn't defined when running as SYSTEM, and works from the system's temp folder.
  • Modules. It makes sure PowerShellGet and PackageManagement are present, installs them if not, then installs and imports the Autopilot and Intune modules it needs.
  • Task sequence variables. It opens the task sequence environment through its COM object and reads values like TPMSupport, the computer name and the Autopilot profile.
  • Serial number. It reads the device's serial number through WMI. If that fails, it sets the ProfileAssigned task sequence variable to False and stops.
  • Connect to Microsoft Graph with the app credentials. On failure, ProfileAssigned is set to False.
  • Find and update the device. It looks the device up in Autopilot by serial number, updates its details, and starts an Autopilot sync. If the device isn't found or the update fails, ProfileAssigned is set to False.

The rest of the task sequence can then use ProfileAssigned to choose between the clean-image path and the fat-image path.

Update: a version without modules

A few weeks after this post I replaced the script with UpdateAutopilotGraph.ps1. Windows PE can't install modules natively, so instead of adding steps to make the PowerShell Gallery work there, the new version calls the Microsoft Graph REST API directly to find the device by serial number and update its display name and group tag. It reads OSDComputerName and GroupTag from the task sequence, and can also run outside one with those passed as parameters.

If you're working through a similar mix of SCCM and Intune, feel free to use and adapt either version. Happy scripting!