Create a bootable Windows 10 Autopilot device with PowerShell!

The most common complaint that I’ve received from people over the last few years around Intune / Autopilot / Modern Management is that people find it frustrating how much effort is involved in getting a device prepared to handover to a client for Autopilot enrollment.

I totally agree – the sales pitch we are all given is that your staff can go out to a big-box store, buy a laptop and in minutes be greeted with a “welcome to mega-corp” login screen.

What is never told to us is that before we do any of this, we need to:

  • make sure a CLEAN copy of windows 10 is installed on the device – which it never is.
  • Capture the hardware hash of the device and upload it to Intune.
  • Finally, if the device is already past the OOBE, re-image the device and hand it over to the staff member..

Hardly the autonomous, streamlined sales pitch we’ve been sold – is it?

Well, I’m here to say I want to make it a little less painful with my first published PowerShell module – Intune.USB.Creator!

This is a solution developed over the last few years and road-tested with multiple clients and environments – something that is reliable enough that I’m happy enough to share it as a complete solution – something I rarely do due to being an obsessive perfectionist…

Let’s get into how we use it!

Pre-Requirements

First things first, we need to make sure the device you are going to use to build the Autopilot device has a few pre-requisites:

The module was written primarily for PowerShell 7 – if you don’t have it yet, there’s a bunch of ways to get it on your machine. Below is probably the easiest of the lot..

Some of the helper functions rely on other modules – so let’s install those (using PowerShell 7 of course..)

The module uses Windows 10 installation media to create the bootable media. This can be procured from many locations – if you do not have access to this, someone you work with will – just make sure you have a copy of the latest *.iso on your device.

Finally, let’s install the Intune.USB.Creator module..

How to use

Once all the pre-requirements are installed, plug a USB into our device and let’s create an Autopilot provisioning device.

Open up PowerShell 7 as an administrator and we will type in the following command:

Hitting enter will kick off the device provisioning code..

A few things to note on each parameter:

  • WinPEPath (Required) – I’ve put a copy of WinPE up on my own storage account – feel free to use it, but if the cost of storage ends up too much, I will take this down. So grab a copy now and store it locally. Consider this fair warning.
  • WindowsIsoPath (Not required) – as mentioned in the pre-requirements section, you need to source your own copy of Windows 10. This shouldn’t be difficult. Try and get a copy of the “multi-edition” so you can build different variants if required. If you don’t provide a path to a copy of Windows 10, the device will still be provisioned, but there will be nothing added to the solution except for WinPE.
  • GetAutopilotCfg (Not required) – this is a simple switch to allow you to log in to an Azure tenant and capture the Autopilot configuration files. If you omit this, you will end up with a provisioning device that installs windows 10 and does nothing else.

Once our USB has been created, all that is required to do is plug it into our target device and boot from it.

WinPE will load and trigger the built in provisioning script which will load the operating system onto the device and inject the Autopilot configuration file.

Once the device has been provisioned – remove the USB and reboot. We should now be greeted with the standard Out of Box Experience, ending with the ability to log in to the tenant we captured the Autopilot configuration from!

Pretty cool, if I do say so myself.

Using this solution should provide you with a bootable USB that will get you from “out of box” to “ready to enrol” in less than 5 minutes.

As usual, source code for everything demonstrated here is available on GitHub, the module itself is available on the PowerShell Gallery and I am always up for a chat on Twitter.

— Ben

42 comments

  1. Jamie Knowles
    May 5, 2020 at 9:16 am

    Thanks for sharing this, it’s excellent! I’ve got 2 autopilot profiles in my tenant but I didn’t get the choice. The warning I got onscreen during the script, ‘Grabbing Autopilot config file from Azure..’ -> ‘Multiple Autopilot policies found – select the correct one..’ -> ‘WARNING: The term ‘Out-ConsoleGridView’ is not recognised as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if path was included, verify the path is correct and try again.’ I installed all the modules you listing so I guess i’m missing one, could you let me know which?

    • Jamie Knowles
      May 5, 2020 at 1:38 pm

      Just to confirm I had to run -> ‘Install-Module Microsoft.PowerShell.ConsoleGuiTools’ to install the missing module and then all worked 🙂

      • Ben
        May 6, 2020 at 12:04 am

        Yep – totally forgot to add that in the pre-reqs – I’ve now included it!

        • PieKie Black
          May 6, 2020 at 2:58 pm

          Grabbing Autopilot config file from Azure.. WARNING: Could not load file or assembly ‘Microsoft.IdentityModel.Clients.ActiveDirectory, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’. Could not find or load a specific file. (0x80131621).
          I get this error when trying to create the media. The process does complete but I never get the prompt to enter Azure credentials to grab the autopilot profile file. Installed all required modules as per article. Anything that I might be missing.

          • PieKie Black
            May 6, 2020 at 5:52 pm

            I found the issue. It was related to the Scope of ExecutionPolicy.
            Get-ExecutionPolicy -list
            Scope ExecutionPolicy
            —– —————
            MachinePolicy Undefined
            UserPolicy Undefined
            Process Undefined
            CurrentUser Bypass (this was set to Restricted for some weird reason)
            LocalMachine Bypass

          • Nick
            May 21, 2020 at 9:31 am

            I am still getting the same error as you on a brand new built laptop so feel like I am still missing a dependency.

  2. Philippe
    May 22, 2020 at 9:26 am

    Hi Ben,
    I’ve installed the Poweshell7 and all the modules.
    I have the WinPE.zip and W10 iso in a local folder (the path are OK)
    But when I try to Publish-ImageToUSB … the script start 2 seconds (I can see the beginning off the script) and the powershell console is switch off. I don’t have time to see where it stop :/
    I don’t have any idea where to looking for now to solve this problem, do you have any idea ?

    • Philippe
      May 26, 2020 at 11:10 am

      Hi,
      I found the problem, not enough space left in my hard drive, the script close at “Get-RemoteFile -fileUri $winPEPath -destination $usb.downloadPath -expand” :c/
      It’s ok now :c)
      After, I’ve got a problem with the .iso : no .wim file, so I’ve created one with dcim (Windows Education (4)) and replace Region get wim from ISO by “Get-RemoteFile -fileUri “D:\W10_autopilot_install\install.wim” -destination $usb.WIMPath” to test.
      Everything looks fine now :c)
      Thanks a lot for your work !

  3. Oskar
    May 28, 2020 at 10:58 am

    I’ve not looked at the code, but I can’t see in the blog post how the issue with capturing and uploading the HW hash is adressed. Is this still a pre-req or something that is done at the same time as getting the AP config?

    • Ben
      June 2, 2020 at 5:35 am

      By utilizing “offline autopilot enrollment” we bypass the need to pre-load the hardware hash into the environment. If your autopilot policy is configured for it you can set it to throw the device hw hash into your environment so that it will always be a member of your tenant on consecutive resets / reimages.

  4. Per Salmi
    May 28, 2020 at 2:33 pm

    Hi! While testing the module I find that the tenant must allow personally owned devices in the device enrollment restrictions to let the end user enroll the device. Is there a way to use it even if personally owned devices are blocked? You get a 80180014 error after the account login stage when personally owned devices are blocked.

  5. Mike
    June 23, 2020 at 7:56 pm

    Does this USB Creator also support legacy booting from floppy on older devices(mbr vs gpt)? It works great on newer uefi bios systems but I run into issues with older Dell Optiplex systems from 10 years ago that do not support uefi. Will have to test more.

    • Mike
      June 23, 2020 at 8:02 pm

      correction: booting from USB on older devices…

      • Trace
        October 12, 2020 at 6:06 am

        @Mike what error do you get on older devices? Is it invalid partition table? If so I’m getting the same issue. is there a work around?

  6. Esko K
    June 25, 2020 at 11:52 am

    Clearing Disk: 1 Creating New Partions New-Partition: C:\Users\xxxxxxxxxx\Documents\PowerShell\Modules\Intune.USB.Creator\1.0.1.285\Private\Set-USBPartiton.ps1:22 Line | 22 | … ss.drive = (New-Partition -DiskNumber $diskNum -Size 2GB -AssignDrive … | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Not enough available capacity Activity ID: {5db62cc6-a32a-4ff4-a6ce-0e4709f7c701} New-Partition: C:\Users\xxxxxxxxx\Documents\PowerShell\Modules\Intune.USB.Creator\1.0.1.285\Private\Set-USBPartiton.ps1:23 Line | 23 | … s.drive2 = (New-Partition -DiskNumber $diskNum -UseMaximumSize -Assig … | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Not enough available capacity Activity ID: {f206cbdf-bc13-4d13-a6b1-9547397bdddd} Writing WinPE to USB.. WARNING: You cannot call a method on a null-valued expression. Writing Install.wim to USB.. WARNING: You cannot call a method on a null-valued expression. Writing Autopilot to USB.. WARNING: You cannot call a method on a null-valued expression. Grabbing provision script from GitHub.. WARNING: The filename, directory name, or volume label syntax is incorrect. : ‘C:\Windows\System32\:\scripts\Invoke-Provision.ps1’

  7. Logan Case
    June 25, 2020 at 3:01 pm

    How can we access your WinPE files to download them? I looked for a link but couldn’t find anything.

    • Ben
      June 28, 2020 at 5:39 am

      Look closer. “https://githublfs.blob.core.windows.net/storage/WinPE.zip”

  8. Esko K
    June 29, 2020 at 4:42 am

    I had to remove all partitions from usb memory before I could finish creating the bootable device.

  9. Mats Markussen
    June 30, 2020 at 1:49 pm

    Receive an error when trying to create the usb

    Writing WinPE to USB..√

    Writing Install.wim to USB..√

    Writing Autopilot to USB..

    WARNING: You cannot call a method on a null-valued expression.

    Grabbing provision script from GitHub..
    WARNING: Could not find a part of the path ‘D:\scripts\Invoke-Provision.ps1’.

    Seems like it fails to copy the contents of the WinPE folder to the newly created partition.
    Downloaded and use a local version of WinPE

    • Mats M
      July 1, 2020 at 1:57 pm

      never mind me, didn’t notice powershell 5 launched even though 7 was installed

  10. Kris
    July 12, 2020 at 12:48 am

    Grabbing provision script from GitHub..

    Grabbing PWSH 7..
    VERBOSE: Destination: D:\scripts\pwsh
    VERBOSE: About to download package from ‘https://pscoretestdata.blob.core.windows.net/v7-1-0-daily-20200709/PowerShell-7.1.0-daily.20200709-win-x64.zip’
    WARNING: Response status code does not indicate success: 404 (The specified resource does not exist.).

    how can i workaround this
    Thanks,
    Kris

  11. Kris
    July 12, 2020 at 9:59 pm

    hi Ben

    getting below error any idea why

    Number TotalSize(GB) Name
    —— ————- —-
    0 238.47 SAMSUNG SSD CM871a M.2 2280 256GB
    1 7.21 Verbatim STORE N GO

    Please select Desired disk number for USB creation: 1

    Clearing Disk: 1
    Creating New Partions

    Writing WinPE to USB..√

    Writing Install.wim to USB..√

    Writing Autopilot to USB..√

    Grabbing provision script from GitHub..

    Grabbing PWSH 7..
    VERBOSE: Destination: D:\scripts\pwsh
    VERBOSE: About to download package from ‘https://pscoretestdata.blob.core.windows.net/v7-1-0-daily-20200709/PowerShell-7.1.0-daily.20200709-win-x64.zip’
    WARNING: Response status code does not indicate success: 404 (The specified resource does not exist.).

    • Ben
      August 18, 2020 at 1:38 am

      There was an issue with the current build of the PowerShell 7 installation media. This has been fixed up and a fix applied to a new build of the module. Please update locally.

  12. marcellos
    July 14, 2020 at 5:20 pm

    This is amazing work! I am using this all over the place now. Can you explain how you made the custom WinPe that behaves that way, and why your bootable USB works with Secure Boot enabled, but none of the ones i create work? Would it be possible to automate the exit as well so that it is completely unattended like the classic installers we create today with AutoUnattend.xml on the root?

    • Ben
      August 18, 2020 at 1:32 am

      The WinPE media was captured from a rough demo of the same concept that someone else was trying to build but wasn’t working very well. Unattend.xml files can be used with this solution as well – they need to be placed in a specific location within the usb partitions – the provisioning script does look for them.

  13. Santeri
    July 29, 2020 at 8:41 am

    Hi! Thanks for sharing this excellent way of creating USB installers with Autopilot. I still have one big problem with this. No matter what I try to do, it will never prompt me to enter Azure credentials for obtaining the autopilot configuration. I have tried with multiple computers etc, but the problem still persists. Another strange thing is that it won’t let me choose the destination disk, it just uses my computer’s internal SSD by default. If I enter the parameter “-DiskNumber” I can get it to choose the right disk. Despite, it still wont get the Autopilot configuration from Azure. What could cause this? I have installed all prerequisites listed and I am using Powershell 7.

    • Ben
      August 18, 2020 at 1:29 am

      Make sure you are using the latest version of the module – there’s been a lot of bug fixes added recently that should fix the problems you are having.

  14. MK
    August 6, 2020 at 12:42 pm

    Having the same issue Grabbing Autopilot config file from Azure.. WARNING: Could not load file or assembly ‘Microsoft.IdentityModel.Clients.ActiveDirectory, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’. Could not find or load a specific file. (0x80131621). Execution policy is set to unresticted. Any idea what is missing?

    • Ben
      August 18, 2020 at 1:26 am

      As mentioned previously, this is an issue with not having the auth libraries accessible in PowerShell 7. Please install the module in PowerShell 5.1 first and try again.

      • MK
        August 19, 2020 at 9:30 pm

        Awesome! Thanks! That worked!

  15. Corey
    August 7, 2020 at 5:37 am

    I’m having an issue and perhaps it’s my lack of understanding of how this is supposed to work. During the creation of the USB drive it gets partitioned at 2GB which seems correct based on the Set-USBPartition command, however it looks like I need a larger partition. I get a message saying:

    Disk number selected.
    Clearing Disk: 1
    Creating New Partions New-Partition:
    Not enough available capacity

    I’ve got heaps of space, they’re 32GB USB drives, but the preceding steps are formatting it to 2GB, it carries on and eventually says it’s done. However if I try to use that USB I get the error that “Install.wim” isn’t found, I guess that’s because it couldn’t fit on the 2GB partition. I copied the USB onto another one and extracted the install.wim file and manually copied it into the images folder and gave that a go, it got further however I’m missing the “imageIndex.json” file which I guess hasn’t been created as part of the failed install.wim copying, what am I doing wrong?

    • Ben
      August 18, 2020 at 1:25 am

      Not sure. I’ve recently updated the module to fix a few usability bugs – perhaps update and try again.
      If you continue to have issues, please raise an issue ticket on the project page – https://github.com/tabs-not-spaces/Intune.usb.creator

  16. Marcus Horne
    August 14, 2020 at 7:19 am

    Getting this error when trying to downlad the Autopilot config?!

    Grabbing Autopilot config file from Azure..
    WARNING: Could not load file or assembly ‘Microsoft.IdentityModel.Clients.ActiveDirectory, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’. Could not find or load a specific file. (0x80131621)

    • Ben
      August 18, 2020 at 1:22 am

      That error is related to trying to use the ADAL auth libraries in PowerShell 7 but not having them accessible in PowerShell 5.1. Try installing the module in 5.1 first and it should resolve the problem.

  17. Aaron
    August 14, 2020 at 11:57 am

    This works great apart from it defaults the keyboard to US English. In my policy it is set to UK English, and once it has gone through OOBE language settings show that it is applied except fro Keyboard which is system default. Any ideas what is causing this?

    • Ben
      August 18, 2020 at 1:20 am

      Using the wrong installation media for your region i’m guessing..

      • Mark Lajer
        October 22, 2020 at 9:04 pm

        We see the same thing. We set Keyboard to Danish but installing English iso. Even if we select Danish in the installation process the keyboard is US English.

  18. Gringo
    October 8, 2020 at 5:30 pm

    error:
    Creating New Partions

    Writing WinPE to USB..√

    Writing Install.wim to USB..√

    Writing Autopilot to USB..

    WARNING: You cannot call a method on a null-valued expression.

    Grabbing provision script from GitHub..
    WARNING: Could not find a part of the path ‘D:\scripts\Invoke-Provision.ps1’.

    Drive D: becomes no content or data inside.

  19. Brian H
    November 1, 2020 at 11:19 pm

    Great work – thank you for sharing. Is it possible to create an .iso output file instead of the USB Bootable ? (One could always us Rufus or other to convert from ISO to USB)

  20. bruno
    November 23, 2020 at 5:02 pm

    I noticed that when I run this on a laptop, I get “the request is not supported” almost immediately after the “Setting power policy to ‘High Performance’ .. ” message comes up. Looking at the script, I also see the next step is a Welcome Screen display.

    Is there something special I should do for Laptops or something different? I have tried on 20H4, 2004, 1909, and 1903 versions of Windows and still cannot get this to run on Laptops. Love to hear any thoughts on the matter.

  21. Trace
    November 28, 2020 at 4:50 am

    I’m trying to run this with what is now PowerShell 7.1.0 and am getting errors that did not exist previously. Is there a way to force it to use the older version of PS 7? I have tried copying the \Scripts\pwsh\ from an older thumb drive, but that somehow has environmental variables to a different tenant. Any help would be appreciated!

    • Trace
      November 29, 2020 at 5:29 am

      So the script PublishImagetoUSB.ps1 is calling a command to automatically download the latest version of PS7, which is now PS7.1 downloading a ZIP version and saving it to the scripts folder of the USB drive as \scripts\pwsh. When using 7.1 it was throwing an error about Power settings and performance in the WINPE environment. I finally got this to work by downloading 7.0.3 from https://github.com/PowerShell/PowerShell/releases/download/v7.0.3/PowerShell-7.0.3-win-x64.zip deleting the contents of the pwsh folder, and putting this version in there. (I was having a separate issue, and I assumed they were related — they were not!) Hope this helps others.

Comments are closed.