Upload Windows Autopilot hardware hash easily

Retrieving the hardware hash for a new laptop or VM involves a few steps. Starting PowerShell, configuring the execution policy, installing the get-windowsautopilot script, answering a few prompts, and entering your credentials to upload it to your environment. In this blog post, I will show you how to minimize the number of steps needed.

App registration

To avoid entering credentials during the process, you must register an app in Azure. Follow these steps for that:

  • Go to App Registrations
  • Select New Registration
  • Enter “Autopilot Registration” as the name, and select Register.
  • Select API Permissions on the left side and select Add a permission
  • Select Microsoft Graph
  • Select Application permissions
  • Search for DeviceManagementServiceConfig.ReadWrite.All, select the checkbox and select Add Permissions. Repeat that for the Directory.ReadWrite.All and GroupMember.ReadWrite.All permissions if you also want to be able to add the device to a specific group. (See the “Adding the device to an Entra ID Group” chapter below)
  • Select Grant admin consent for xxxxx.onmicrosoft.com and select Yes
  • Select Certificates & secrets on the left side and select New client secret.
  • Enter “Autopilot Registration Secret” as a description. For example, please select how long the client secret should be valid (I selected 24 months) and click Add.
  • Select the copy icon behind the value column and copy it somewhere safely (A password manager or database)
  • Select Overview on the left side, copy the Application (client) ID plus the Directory (tenant) ID, and save them with the value you copied in the previous step.

Creating the scripts

Now that we have an application registration that we can use for authenticating, we can create two script files that you can put on a USB drive to launch and will take care of getting the hardware hash and uploading it to your tenant. Copy the contents below and save them as autopilot.cmd and autopilot.ps1, for example, on a USB drive. (I changed the IDs to xxxx. You need to replace them with the IDs that you saved when creating the app registration, of course 🙂 )

Autopilot.cmd

powershell.exe -executionpolicy bypass -file .\autopilot.ps1

Autopilot.ps1

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force:$true
Install-Script get-windowsautopilotinfo -Confirm:$false -Force:$true
get-windowsautopilotinfo -Online -TenantId xxxx -AppId xxxx -AppSecret xxxx
shutdown.exe /s /t 10

Running the autopilot.cmd

When your device is on the first OOBE (Out Of Box Experience) screen, the one with the language selection, you can follow these steps:

  • Press Shift-F10 to get to a command prompt
  • Go to your USB drive by entering E: (This could be different in your case)
  • Enter “autopilot.cmd” to start the script

The autopilot.ps1 starts, installs the NuGet provider, downloads the get-windowsautopilot script, connects to the tenants using the app registration details, and uploads the hardware hash. When done, it shutdowns the system in ten seconds, and your system is ready to go start an Autopilot installation!

The uploaded hardware hash in Endpoint Manager from my test tenant:

Adding a Group Tag

You can add Group tags to the script if you use Group tags for deployment profiles. Change the autopilot.ps1 on your USB drive to the one below: (Added the –GroupTag parameter. I used VMware as an example)

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force:$true
Install-Script get-windowsautopilotinfo -Confirm:$false -Force:$true
get-windowsautopilotinfo -Online -TenantId xxxx -AppId xxxx -AppSecret xxxx -GroupTag VMware
shutdown.exe /s /t 10

Adding the device to an Entra ID Group.

You can also add the device to a specific group during the process, such as a WuFB or Autopatch group. Change the autopilot.ps1 on your USB for that to the one below: (Added the –AddToGroup parameter. I used ‘Windows Autopatch – Last’ as an example)

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force:$true
Install-Script get-windowsautopilotinfo -Confirm:$false -Force:$true
get-windowsautopilotinfo -Online -TenantId xxxx -AppId xxxx -AppSecret xxxx -AddToGroup 'Windows Autopatch - Last'
shutdown.exe /s /t 10

Combining Group Tag and AddToGroup parameters

These two parameters can be used together, too. In the example below, I combined both parameters from the chapters above:

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force:$true
Install-Script get-windowsautopilotinfo -Confirm:$false -Force:$true
get-windowsautopilotinfo -Online -TenantId xxxx -AppId xxxx -AppSecret xxxx -GroupTag VMware -AddToGroup 'Windows Autopatch - Last'
shutdown.exe /s /t 10

59 thoughts on “Upload Windows Autopilot hardware hash easily

  1. Pingback: Blogpost – Upload Windows Autopilot hardware hash easily – 247 TECH

  2. Pingback: Deploy a Hyper-V VM and register it for Autopilot automatically using PowerShell | PowerShell is fun :)

  3. Kindly provide script and steps to implement in SCCM OSD task sequence to upload hardware hash to intune with multiple Group tag

    • I think all of the scripting parts is already in this blog, the shutdown part should be removed in a task sequence…I’m not an SCCM man, but I did see a few examples online about gathering it to a CSV which requires manual action. My script in this blog post uploads it automatically with the app registration details, Group Tag is possible also but not sure how you can specify which group tag for which deployment 😉

    • It takes a little bit, but you can apply a clean image, install config manager, then run the script and pass through an argument for the group tag after it boots to the OS and sets up config manager (the argument could be based on input in the UI++ or if you just have self-deploy and user driven profiles then a model check or something to ensure TPM 2.0 is enabled). Then what I was doing was applying the clean image again and using a post action to reboot to OOBE because even after using the Prepare Windows for Capture step to get rid of the client, it would still show up as hybrid. I also add a sleep step to the script at the end for about 5 minutes to make sure the correct autopilot profile is assigned.

  4. Hey Harm! Thanks so much for this useful information and instructions! I was able to replicate it and it works! Question for you: Do you think this is possible to deploy this script through Intune?

  5. Thank you so much for this useful information and instructions! We were able to replicate this and works great locally! Question for you: Do you think this script can be deployed through Intune? We have a ton of existing devices in our environment and want to mass upload the hash without touching individual devices.

    • Yes, you could deploy the script using Intune using the Scripts pane in Devices but don’t forget to remove the shutdown line 😉 By deploying it as s script, not a Win32 package, it will only run once if succesful. Assign it to one device and see if that works and then you can assign it to more devices.

      Those devices in your environment, are they joined to Intune using Hybrid or Workplace join? There is a setting in the deployment profile to convert devices to Autopilot devices

  6. I noticed Get-WindowsAutopilotInfo now has an AssignedComputerName parameter and I was wondering if there was a way to upload that alongside the group tag with this method? The main pain point for autopilot right now is the naming scheme but being able to use UI++ to collect the site and asset number to create the computer name and pass that along to the script would be great and allow for the auto import of devices to our asset management system too by using the first part of the name for the site and the last part of the asset number.

    • You can adjust the autopilot.ps1 script with that parameter of course, you could wrap a script around it to call the get-windowsautopilotinfo.ps1 script with a parameter… Site and asset sounds like SCCM? No experience in that myself 🙂

      • Sorry, site probably wasn’t the best word to use. I’m at a school district, so the naming scheme I would be shooting to replicate from SCCM would be the three letter initials for the individual schools followed by the 5 digit asset number for internal tracking. I also thought this was the upload hash script that pushes the hash and group tag using MSGraph. I had no idea you could use an App ID and Secret with get-windowsautopilotinfo, so I’m definitely switching to your method since it simplifies everything and solves my problem, lol.

    • 😊You could also use multiple Deployment profiles based on dynamic groups that are filled with group tags. In the profile you can specify the first few letters and a randy number between a certain range. Option 😅

    • I did something similar, my method was once it was autopiloted and given the right tag I then had a Deployment profile assigned to a group with dynamic membership rule containing the tag, the profile allowed me to pre-provision the machines so as they were in the group the profile was set to name the machine so I had our naming convention L-EXO-%serial% and it used the device serial number to temporarily name and then you can always rename afterwards once device is in intune.

  7. Thank you so much for this useful information and instructions. I have one question related to secrets key and AppID, In current scenario secrets key and AppID is visible and can be compromised. how we can avoid that.

    • Thank you 🙂 And I understand your point, the API permissions are there and can be used. I think using the SecretManagement or Azure Key vault could be an option to store and retrieve those credentials from, having to type an unlock password for that would be something I guess…

      Will put that on my To-Do list !

      • I have one question related to secrets key and AppID, In current scenario secrets key and AppID is visible and can be compromised. how we can avoid that.

        I would appriciate you if you can help in this regard.

        Thanks,
        Manu Kamboj

  8. It’s not working here. What are I doing wrong:

    powershell.exe -executionpolicy bypass -file D:\Autopilot.ps1
    Name Version Source Summary —- ——- —— ——- nuget 2.8.5.208 https://onege… NuGet provider for the OneGet meta-package manager Connected to Intune tenant 5c885ec7-c6cf-45b9-ac4c-f657fa82845f using app-based authentication (Azure AD authentication not supported)
    Gathered details for device with serial number: 7DDXPN3
    Add-AutopilotImportedDevice : System.Net.Http.HttpRequestException: 401 Unauthorized
    {“error”:{“code”:”UnknownError”,”message”:”{\”ErrorCode\”:\”Forbidden\”,\”Message\”:\”{\r\n \\”version\\”: 3,\r\
    \n \\”Message\\”: \\”An error has occurred – Operation ID (for customer support): 00000000-0000-0000-0000-000000000
    000 – Activity ID: bd548cc9-84b4-4747-b50c-72ddf471fdea – Url: https://fef.msub01.manage.microsoft.com/DeviceEnrollment
    FE_2305/StatelessDeviceEnrollmentFEService/deviceManagement/importedWindowsAutopilotDeviceIdentities?api-version=5022-1
    2-08\\”,\r\n \\”CustomApiErrorPhrase\\”: \\”\\”,\r\n \\”RetryAfter\\”: null,\r\n \\”ErrorSourceService
    \\”: \\”\\”,\r\n \\”HttpHeaders\\”: \\”{\\\\”WWW-Authenticate\\\\”:\\\\”Bearer realm=\\\\\\\\”
    urn:intune:service,f2f6fa41-932f-4d25-9e56-14ec49d3405a,f0f3c450-59bf-4f0d-b1b2-0ef84ddfe3c7,3e9c57b9-808d-4aa0-9500-4b
    2d369279e7\\\\\\\\”\\\\”}\\”\r\n}\”,\”Target\”:null,\”Details\”:null,\”InnerError\”:null,\”InstanceAnnotat
    ions\”:[]}”,”innerError”:{“date”:”2023-05-31T12:16:37″,”request-id”:”bd548cc9-84b4-4747-b50c-72ddf471fdea”,”client-requ
    est-id”:”bd548cc9-84b4-4747-b50c-72ddf471fdea”}}}
    At C:\Program Files\WindowsPowerShell\Scripts\Get-WindowsAutoPilotInfo.ps1:331 char:17
    + … imported += Add-AutopilotImportedDevice -serialNumber $
    .’Device Seri …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Add-AutopilotImportedDevice

  9. Hi Harm!! Firtstly thanks for this. However when i try to run the script now it comes up with error , has anything changed?

  10. Hello, what do you use the shutdown command for?
    We have a deployment with AzureAd-registered devices and local users, we want to move them to AzureAd-Joined profiles with Autopilot, we have to factory reset these devices to make the migration?

    • I used the shutdown command so that you don’t have to do it yourself 😉 This is so that when you turn it on, it will start the Autopilot process correctly. (This part is done on a clean / fresh installation in the first screen (Shift-F10) after deployment)

      You could also join the machine to your Azure AD from the Settings/Accounts menu. That way, you will also get Intune packages/settings, etc. But the user could choose to be an admin then. That’s not what you want. (Or you should do it for them and choose that the account is a normal user)

      Uploading the hardware hash, factory reset/wipe with Windows 11 ISO USB, and enrolling into Autopilot is better. Intune manages clean machines and everything on it, no things that the local (Admin?) users have put on there…

  11. Hi, I received an error “Connect-MgGraph:The provided access token has expired. Set a valid access token to -AccessToken parameter and try again”. Any ideas about what is happening?

  12. Hey Harm, I tried the same app on a virtual machine and it worked as expected. Probably it’s a problem with my physical laptop. I’ll try to format it and try again. Thank You for your support.

  13. Failing with below error with the 3.9 version

    Administrator:
    E: \Autopi10tLlK>powershe11. exe -executionpolicy
    bypass -file
    Source
    . \autopi10tLlK . PSI
    Summary
    Name
    nuget
    Version
    2.8.s. 288
    https : //onege. .
    NuGet provider for the OneGet meta-package manager
    Installing module WindowsAutopi10tIntune
    Connected to Intune tenant 49e84289-ß89e-4bdd-ac8c-22aeßßSf43d6 using app-based authentication (Azure AD authentication not s
    upported)
    New- Ci mSession
    The WinRM client cannot process the request. If the authentication scheme is different from Kerberos,
    or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must
    be added to the TrustedHosts configuration setting. Llse winrm. cmd to configure TrustedHosts. Note that computers in
    the TrustedHosts list might not be authenticated. You can get more information about that by running the following
    command: winrm help config.
    At C: \Program PSI :218 char:15
    $session
    New- CimSession -ComputerName Scamp -Credential $Credentia .
    + Categorylnfo
    + FullyQua1ifiedErrorId
    + PSComputerName
    . NotEnab1ed: ( : ) [New-CimSession], CimException
    HRESLILT 8x883388e4,Microsoft . Management . Infrastructure. CimCmd1ets . NewCimSessionCommand
    . ITAUK
    Get-Cimlnstance .
    Cannot bind argument to parameter ‘CimSession’ because it is null.
    At C: \Program PSI : 223 char:42
    $serial
    (Get-Cimlnstance -CimSession $session -Class V,’in32
    + Categorylnfo
    + FullyQua1ifiedErrorId
    . InvalidData: ( : ) [Get-Cimlnstance], Parameter8indingVa1idationException
    . ParameterArgumentVa1idationErrorNu11NotA110wed ,Microsoft . Management . Infrastructure. CimCm
    dlets . GetCimInstanceCommand
    Get-Cimlnstance .
    Cannot bind argument to parameter ‘CimSession’ because it is null.
    At C: \Program PSI : 226 char:45
    $devDetai1
    + Categorylnfo
    (Get-Cimlnstance -CimSession $session -Namespace .
    . InvalidData: ( : ) [Get-Cimlnstance], Parameter8indingVa1idationException
    + FullyQua1ifiedErrorId
    . ParameterArgumentVa1idationErrorNu11NotA110wed ,Microsoft . Management . Infrastructure. CimCm
    dlets . GetCimInstanceCommand
    Get-Cimlnstance .
    Cannot bind argument to parameter ‘CimSession’ because it is null.
    At C: \Program PSI : 248 char: 38
    $cs = Get-Cimlnstance -CimSession $session -Class Win32 C .
    + Categorylnfo
    + FullyQua1ifiedErrorId
    . InvalidData: ( : ) [Get-Cimlnstance], Parameter8indingVa1idationException
    . ParameterArgumentVa1idationErrorNu11NotA110wed ,Microsoft . Management . Infrastructure. CimCm
    dlets . GetCimInstanceCommand

    • As you can see in the comment just above your one, I tried it yesterday and worked out fine. Did you modify it perhaps? I see cimsession things, are you doing this remotely to another client?

  14. Hi,
    I am getting the error
    Add-AutopilotImportedDevice : Microsoft.Graph.PowerShell.Authentication.Helpers.HttpResponseException: Response Status code does not indicate success: Unauthorized (Unauthorized)

    • I had the same issue. Here is how to fix it. When you click on “Add a permission” you need to make sure you click the big “Microsft Graph” banner (very top) under the “Commonly used Microsoft APIs” and then select “Application permissions” and NOT the “Delegated permissions”.

  15. please let us know possible to add group tag gui selection and same will be uploaded to intune hash (some modifications in script2) and another script for prompting group tag GUI

    • You could change it something like this to the script for a GUI selection:

      Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force:$true
      Install-Script get-windowsautopilotinfo -Confirm:$false -Force:$true
      $grouptags=”Test”, “Test 2”, “Test 3”
      $grouptag=$grouptags | Out-GridView -Title “Select the Group Tag (Only one) and click OK” -PassThru
      get-windowsautopilotinfo -Online -TenantId xxxx -AppId xxxx -AppSecret xxxx -GroupTag $grouptag
      shutdown.exe /s /t 10

      or to this tor prompt for one:

      Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force:$true
      Install-Script get-windowsautopilotinfo -Confirm:$false -Force:$true
      $grouptag= Read-Host “Please enter Group Tag”
      get-windowsautopilotinfo -Online -TenantId xxxx -AppId xxxx -AppSecret xxxx -GroupTag $grouptag
      shutdown.exe /s /t 10

      Not tested yet, let me know if this works!

      • kindly let me know how to use this in the tasks sequence needs to create separate powershell steps or above line needs to add script2 where to add

  16. He estado pensando en como hacer esto y tu publicación me ha ayudado en gran manera, en mi caso como parte del unnatend agregare el Script para que se ejecute posterior al OOBE, gracias de antemano

  17. Intune hardware hash value is upload and after running task sequence once again hardware hash value is not loaded if anything modified in the script

  18. Hi and thanks for the easy solution.

    My question is what changes should I make in order to avoid doing external calls to get the autopilotinfo script? I assume get rid of the first line, add path on the second line with the locally installed script, and then the rest as is?

    Thank you

    • If you have a local installed script (The file being on a USB, for example), then you can skip the first line two lines and call the local installed script by specifying it. But… The Get-WindowsAutopilotinfo will install modules needed for authentication, and prompt you for installing the NuGet provider

      • Ok, thank you. I see that one of the packages requires a lot of dependencies, so in your opinion, downloading them locally to the USB once is kinda unnecessary and possibly a headache?

        Thank you

      • While it might work saving them to the USB and importing them from there, you will have to update that when newer versions arrive. The online parameter does that for you, it downloads the latest version of the dependencies for you

  19. Hello,

    Any way to secure the shared secret, would not want it to be plain text in the script.

    Thank you in advance.

    • You could retrieve it from a file on the seperate USB stick and leave it out of the script. (That way you would just have a USB stick containing that file without any information and you would need both the script and that USB stick to authenticate) For example, something like this were you read the password from a txt file on e:\ in the windowsautopilotinfo.txt file.

      Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force:$true
      Install-Script get-windowsautopilotinfo -Confirm:$false -Force:$true
      $appsecret=get-content e:\windowsautopilotinfo.txt
      get-windowsautopilotinfo -Online -TenantId xxxx -AppId xxxx -AppSecret xxxx
      shutdown.exe /s /t 10

      Alternatively, you could use get-windowsautopilotinfo -Online instead of get-windowsautopilotinfo -Online -TenantId xxxx -AppId xxxx -AppSecret xxxx . You would then need to enter the credentials of the Admin and use MFA. Or, authenticate using a YubiKey?

  20. Hi and thank you for the article.

    I want to use the -AddToGroup option, but I want to be sure that I only give the necessary permissions to my app registration. So far I have given
    DeviceManagementServiceConfig.ReadWrite.All, and Group.ReadWrite.All, (it fails) but I am not sure what other permissions I need

    Thank you

    • Did you select “Application permissions” and not the “Delegated permissions” for the app registration? Will check tomorrow to see if DeviceManagementServiceConfig.ReadWrite.All is still sufficient for this.

      • Hi and thanks for the instant reply.

        Yes, I did choose Application permissions.

        Thank you and good night

    • I did some testing, and also updated this blog post with that. The permissions required for adding the device to a group while adding it to the Autopilot devices are :
      – Directory.ReadWrite.All
      – GroupMember.ReadWrite.All

      • Hi and thank you very much for your answer. The updated post helps so much

        Finally, I almost forgot, congratz for your MVP in early March(?)!! Fully deserved for your help with PS

      • No problem, glad to help and thanks for the question abou that! Always good to make things more complete 🙂 And yes , thank you! Such a special moment and so happy with that! Good night and have a great Easter weekend!

Leave a Reply to Harm VeenstraCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.