Creating Windows ISO with Autopilot JSON Injected

When migrating to the modern Autopilot world, if you are not purchasing net-new devices, you may find yourself having to rebuild current ones.

This means getting these devices into Autopilot devices. The most popular option (and rightly so) is to use the Get-WindowsAutopilotInfo script, whether that’s the official one or the community edition

Sometimes though, you might want a more simple solution, something a bit plug-and-go. This is something I recently came across so thought why not script it!

V2.0 update – Now supports language/locale selection for ISO

You can grab the script from GitHub here

Or from PowerShell Gallery:

Install-Script -Name create-windows-iso-with-apjson

The result is a new script to create a Windows ISO, running the script will do the following:

  • List your Autopilot profiles in a grid-view to select the one to use
  • Prompt for a language for the ISO
  • Grab the JSON for you
  • Popup the OS choice, this is grabbed directly from Microsoft to list only the currently supported versions
  • Create a new folder in c:\temp to work from
  • Grabs the ISO URL using the excellent Fido from Pete Batard
  • Downloads the ISO
  • Mounts the ISO
  • Grabs the Windows Professional WIM from within in
  • Mounts the WIM and injects the autopilot JSON
  • Converts the new WIM to an ISO (using oscdimg)
  • Cleans up everything but the ISO

As with all scripts, you can supply parameters to use an Azure App Reg for the Autopilot profile part so you could run this on an automated schedule.

When using this with an Autopilot dynamic group, the rule needs to be:

(device.devicePhysicalIDs -any (_ -contains "[ZTDid]")) -or (device.enrollmentProfileName -eq "OfflineAutopilotprofile-PROFILEIDHERE")

Replace PROFILEIDHERE with the ID listed in the initial popup when selecting the profile to deploy (it’s also in the address bar within the Intune portal)

Hopefully you find this useful!

71 thoughts on “Creating Windows ISO with Autopilot JSON Injected”

  1. I just tested it on VM and actually same as normal iso…
    and in the end I logged in with different tenant.
    No errors while creating iso.

    I thought that this iso would post hw hash to autopilot…

    Reply
  2. Hi Andrew,
    When running your latest script, 1.0.4, I came across this error:

    Attempted to divide by zero.
    At C:\Program Files\WindowsPowerShell\Scripts\create-windows-iso-with-apjson.ps1:462 char:5
    + [int] $dlProgress = ($download.BytesTransferred / $download.Bytes

    Any thoughts as to how I may be able to resolve this?

    Thanks

    Reply
  3. Hello,
    I get this error when downloading the ISO?

    Connected to Intune tenant
    Selecting OS
    Finding latest supported versions
    Windows 11 22H2 Chosen
    Downloading Fido
    Fido Downloaded
    Grabbing ISO URL
    Error: We are unable to complete your request at this time. Some users, entities and locations are banned from using this service. For this reason, leveragi
    ng anonymous or location hiding technologies when connecting to this service is not generally allowed. If you believe that you encountered this problem in e
    rror, please try again. If the problem persists you may contact Microsoft Support – Contact Us page for assistance. Refer to message code 715-123130 and 32d
    f09d1-9630-4566-84aa-b6b30187a952.
    Downloading OS ISO
    Start-BitsTransfer : Impossible de valider l’argument sur le paramètre «Source». L’argument est Null ou vide. Indiquez un argument qui n’est pas Null ou
    vide et réessayez.

    Reply
  4. Hi Andrew,

    Thank you for this great tutorial, however, you can provide more information on the following steps:

    -Grabs the Windows Professional WIM from within in
    -Mounts the WIM and injects the autopilot JSON
    -Converts the new WIM to an ISO (using oscdimg)
    -Cleans up everything but the ISO

    Thanks a lot

    Francois

    Reply
    • Hi Francois,
      Of course.
      It uses Fido to find the URL for the Windows version selected
      Then it uses mount-diskimage to mount it to a temporary location.
      It grabs the install.wim from that location and uses mount-windowsimage to mount that.
      The JSON is injected with a simple copy command
      Then is dismounts (dismount-windowsimage)
      Deletes the original install.wim and replaces with the new one (export-windowsimage)
      It then downloads oscdimg which creates the ISO

      At the end it deletes all temp files leaving just the ISO and Fido

      Hope this helps. The script is fully commented if you want to see exactly what’s happening

      Reply
  5. Hi Mate,

    What if I manually reset a device on windows reset – provisioned via autopilot, after the reset, can the end user login with their credentials after the reset?

    Reply
  6. Hi,

    The ISO download works again.
    I was able to register a PC correctly from Microsoft AutoPilot.

    Thanks for the script, but I have one last question. In my Autopilot Deployment Profile, I’ve set the device name to automatic after installing the PC via ISO, the PC has a default name of DESKTOP-XXXXX.

    How can I resolve this?

    Reply
  7. Hi,

    I am new in this and was wondering if I could add this to a usb and make it bootable, then try booting from usb everytime to leverage autopilot

    Reply
  8. Hi,

    In the deployment profile, I’ve set up automatic naming, and I’ve run the Powershell script again.
    Unfortunately, the computer is still named DESKTOP-XXXXX
    Here’s my deployment profile:

    Out-of-box experience (OOBE)
    Deployment mode: Self-Deploying (preview)
    Join to Azure AD as : Azure AD joined
    Language (Region) :French (France)
    Automatically configure keyboard : Yes
    Microsoft Software License Terms :Hide
    Privacy settings : Hide
    Hide change account options : Hide
    User account type: Standard
    Allow pre-provisioned deployment : No
    Apply device name template :Yes
    Enter a name: 23-%SERIAL%

    Reply
  9. So since this script doesent upload the hardware hash, you cant have a dynamic group containing the query: (device.devicePhysicalIDs -any _ -contains “[ZTDId]”), to the autopilot profile? You would need a dynamic group containing all windows devices instead?

    I tried provisioning a computer with this, but it didnt apply the name template, which i am assuming, is because the autopilot profile is assigned to a group with the dynamic query i listed above. Is this assumption correct? And the device only shows as an Azure AD device and not an autopilot device, is this also because of the assignment or is this expected behavior?

    Reply
    • Hi,
      You need to change the dynamic rule to grab offline enrolled devices:
      (device.devicePhysicalIDs -any (_ -contains "[ZTDid]")) -or (device.enrollmentProfileName -eq "YOURPROFILEID")
      Make sure you have Convert existing devices to Autopilot devices set to yes on your profile and that this group is assigned to it. Then they should also convert to Autopilot

      Reply
  10. Hey Andrew!

    The script works as expected and the iso is build properly.

    But I have a few questions.
    Do we need to import the device into autopilot first?
    Can we install the iso offline and all things (including apps) are still get installed?
    How do I know that the ESP is using the iso and not downloading the things from Intune?
    To use the iso properly which settings should we activate or deactivate in Intune?

    Thank you!

    Reply
    • Hi,
      No need to add to autopilot first. If you add a dynamic group based on the autopilot profile, you can assign that and convert all existing objects to Yes to get these listed in Autopilot.
      You will need an internet connection for Autopilot to happen and the apps installed.
      It still uses Intune to download everything, this just enrols into Autopilot

      Reply
  11. Hi, i tried changing my autopilot assignment group to this dynamic query: (device.devicePhysicalIDs -any (_ -contains “[ZTDid]”)) -or (device.enrollmentProfileName -eq “TID001 Autopilot”). However it doesent seem to work. It does not apply the name template and skip language and privacy settings.

    When trying to validate a device provisioned by the iso, towards the group it says: dynamic membership rule validation error: invalid characters found in the rule. Invalid characters in the rule: ”

    I tried changing the query to (device.devicePhysicalIds -any (_ -contains “[ZTDid]”)) or (device.enrollmentProfileName -eq “TID001 Autopilot”). It doesent complain about invalid characters in the rule. But is unable to validate devices provisioned by the ISO.

    Is there anything wrong with the assigments/dynamic queries?

    Reply
  12. I changed the dynamic query to this: (device.devicePhysicalIDs -any (_ -contains “[ZTDid]”)) -or (device.enrollmentProfileName -eq “56f88377-e152-4ebc-8be5-cc0b5ddc7c07”) and provisioned a new device. It still doesent populate any devices or applies the autopilot settings from the profile.

    Reply
  13. Hi Andrew!

    Thanks for your reply!

    Maybe it’s a silly question, but for what can I use the ISO if everything get downloaded from Intune?

    Reply
  14. Hi, i am having the same issue. Just to be clear the dynamic query should be this?

    (device.devicePhysicalIDs -any (_ -contains “[ZTDid]”)) -or (device.enrollmentProfileName -eq “nameofautopilotprofile-IDofAutopilotProfile)

    Reply
  15. So the query should look like this?

    (device.devicePhysicalIDs -any (_ -contains “[ZTDid]”)) -or (device.enrollmentProfileName -eq “nameofautopilotprofile-IDofAutopilotProfile)

    Reply
  16. Hi!

    I’ve corrected the dynamic query but still face some issues:

    1. The ESP runs fine but still does not apply the Device nametemplate and does not skip the privacy settings.
    2. The devices are not populated to the corrected dynamic group.

    Is there something wrong in the ISO, or anything specific i should look for to troubleshoot this?

    this is the dynamic group: (device.devicePhysicalIDs -any (_ -contains “[ZTDid]”)) -or (device.enrollmentProfileName -eq “OfflineAutopilotprofile-56f88377-e152-4ebc-8be5-cc0b5ddc7c07”)

    Reply
    • If the device is not populating into the group, that is why the ESP is failing. Try using the What-If tool in the dynamic rule builder. It might be worth checking if it is case sensitive to begin with

      Reply
  17. Hi!

    I’ve managed to get the autopilot group populating the device into the group in a different tenant. However this happens after the ESP has run, and therefore the name template and privacy settings wont apply. Any tips on what to do here?

    Reply
  18. Right, so from a fresh installed VM with the ISO, without any record in Azure AD. The device will still populate in to the group? Otherwise its always gonna populate after the ESP is finished.

    Reply
  19. I tried to create a new VM with the ISO and wait to see if it would make a difference. The device is not populated in to the autopilot group. How is Azure AD going to know about the device, when it is not joined or registered to Azure AD?

    I just dont understand how a new device that does not exist in Azure AD, will be populated into a dynamic group. I cant use the rule validation towards the device because it does not exist in Azure AD.

    Reply
  20. But how is assigning the esp to users only going to help? The naming convention from the autopilot still wont apply the name template because of this change?

    If the autopilot assigment doesn’t matter, is something wrong with the json itself then?

    Thanks for all the reponse.

    Reply
    • Yes, it sounds like there is either an issue with your profile, or with the JSON it has downloaded and injected. Does the OOBE user login appear ok?
      If it does, you could break out into a PowerShell window and inspect the JSON file

      Reply
  21. Yeah, I looked at the JSON and the ‘ztdcorrelationID’ value and ‘cloudassigneddevicename’ are set to null. Whenever I download the JSON config manually, I get the correct configuration in the JSON. Is there anything I can do to fix this? Or can I add my locally downloaded JSON file to the ISO?

    Reply
  22. Hello,

    Is there a way to have the program pull a different image? I just tried to use the tool and the image that it downloads does not accept our product keys and it also isn’t working with the key pulled from the bios. Is there a way I can use my own image or is there something i doing wrong?

    Reply
  23. Hello,

    when we skip entering the product key the Windows version defaults to education and we are unable to upgrade from there. I have also tried to enter a product key when prompted and it states that it’s not compatible with the version that is installed. When using the media creation tool or using Rufus to download a Windows iso works without issue.

    Reply
  24. This is failing with the following error. I’m running it as admin as well:

    Write-Progress : Cannot validate argument on parameter ‘PercentComplete’. The argument is null, empty, or an element of the argument collection contains a null value. Supply a collection that does not contain any null values and
    then try the command again.
    At C:\Program Files\WindowsPowerShell\Scripts\create-windows-iso-with-apjson.ps1:529 char:102
    + … le…” -Status “$dlProgress% Complete:” -PercentComplete $dlProgress;
    + ~~~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Write-Progress], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.WriteProgressCommand

    Attempted to divide by zero.
    At C:\Program Files\WindowsPowerShell\Scripts\create-windows-iso-with-apjson.ps1:528 char:5
    + [int] $dlProgress = ($download.BytesTransferred / $download.Bytes …

    Reply
  25. Is there anyway to implement group tags?
    I know the hardware hash is not uploaded, but you can use convert all targets to Autopilot, but then adding the group tag becomes a manual step?

    Reply

Leave a Comment