Skip to content

Instantly share code, notes, and snippets.

@nicolonsky
Last active February 5, 2024 13:32
Show Gist options
  • Save nicolonsky/29568077bcad7135ea7a6182742f4a55 to your computer and use it in GitHub Desktop.
Save nicolonsky/29568077bcad7135ea7a6182742f4a55 to your computer and use it in GitHub Desktop.
Bulk Update Windows Autopilot entities
Connect-MSGraph
Update-MSGraphEnvironment -SchemaVersion "Beta" -Quiet
Connect-MSGraph -Quiet
# Get all autopilot devices (even if more than 1000)
$autopilotDevices = Invoke-MSGraphRequest -HttpMethod GET -Url "deviceManagement/windowsAutopilotDeviceIdentities" | Get-MSGraphAllPages
# Display gridview to show devices
$selectedAutopilotDevices = $autopilotDevices | Out-GridView -OutputMode Multiple -Title "Select Windows Autopilot entities to update"
$selectedAutopilotDevices | ForEach-Object {
$autopilotDevice = $PSItem
# Change names according to your environment
$autopilotDevice.groupTag = "MOSD"
#$autopilotDevice.orderIdentifier = "ORDER1234" | updating orderidentifier is currently not supported
$requestBody=
@"
{
groupTag: `"$($autopilotDevice.groupTag)`",
}
"@
Write-Output "Updating entity: $($autopilotDevice.id) | groupTag: $($autopilotDevice.groupTag) | orderIdentifier: $($autopilotDevice.orderIdentifier)"
Invoke-MSGraphRequest -HttpMethod POST -Content $requestBody -Url "deviceManagement/windowsAutopilotDeviceIdentities/$($autopilotDevice.id)/UpdateDeviceProperties"
}
# Invoke an autopilot service sync
Invoke-MSGraphRequest -HttpMethod POST -Url "deviceManagement/windowsAutopilotSettings/sync"
@rwarrick66
Copy link

Great script! I have 3500 devices that were converted to Autopilot and will ultimately need GroupTags added so they can be reset. Is the existing device name available in the grid view so we can multi-select and apply GroupTags? That would really sweeten this up since the devices are already prepended with the correct site code (that match the GroupTags.)

@mravikant
Copy link

Hi Nicola,

I am trying to run above script but I am getting below error. Could you please assist with this issue?

I am able to use GET function but the POST Content and Sync functions.

Updating entity: M00ABBD | groupTag: TESTAutoPilot | orderIdentifier:
Invoke-MSGraphRequest : 404 Not Found
{"error":{"code":"ResourceNotFound","message":"{\r\n "_version": 3,\r\n "Message": "An error has occurred - Operation ID (for customer support): 00000000-0000-0000-0000-000000000000 - Activity ID: df4dbdd1-2000-4145-84a5-2e501c6437b0 -
Url: https://fef.msua07.manage.microsoft.com/DeviceEnrollmentFE_2306/StatelessDeviceEnrollmentFEService/deviceManagement/windowsAutopilotDeviceIdentities('M00ABBD')/microsoft.management.services.api.updateDeviceProperties?api-version=5023-05-11
",\r\n "CustomApiErrorPhrase": "",\r\n "RetryAfter": null,\r\n "ErrorSourceService": "",\r\n "HttpHeaders":
"{}"\r\n}","innerError":{"date":"2023-07-17T05:26:56","request-id":"df4dbdd1-20bb-4145-84a5-2e501c6437b0","client-request-id":"df4dbdd1-20bb-4145-84a5-2e501c6437b0"}}}
At C:\Temp\Add-Update-GroupTagstoAutoPilotDevices\2-Add-Update-GroupTagstoAutoPilotDevices.ps1:31 char:5

  • Invoke-MSGraphRequest -HttpMethod POST -Content $requestBody -Url ...
    
  • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : ConnectionError: (@{Request=; Response=}:PSObject) [Invoke-MSGraphRequest], HttpRequestException
    • FullyQualifiedErrorId : PowerShellGraphSDK_HttpRequestError,Microsoft.Intune.PowerShellGraphSDK.PowerShellCmdlets.InvokeRequest

Regards,
Ravikant

@AndyHoveringBeard
Copy link

Hi,
I was using this to bulk update my customers devices after they had been hybrid joined to Intune and sucked into Autopilot by assigning them to an Autopilot profile which is set to convert all assigned devices to Autopilot. I needed to set a tag on each device depending on which on-prem OU the device was originating from so that when it is built from Autopilot it can be Domain Joined into the same OU (use a group with a dynamic device rule for each tag). This worked fine up until sometime around september last year. I have since then had 2 other projects for other customers who are on the same journey and needed the same thing. Unfortunately I was forced to tag each device manually in Intune :(

This is the script which worked before and has stopped working now - it is based on your wonderful code above. Can anyone help explain why it no longer works or how to get it working again?

`# GroupTagBulkListWinAutopilotDevices-v4.ps1

Run this script with local admin rights, preferably on a Windows server.

$ErrorActionPreference = "SilentlyContinue"

Trust scripts downloaded from the PSGallery

Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted

Update Nuget Package and PowerShellGet Module

Install-PackageProvider NuGet -Scope CurrentUser -Force

Install the required PowerShellGet module if not already installed

if (-not (Get-Module -ListAvailable -Name PowerShellGet)) {
Install-Module PowerShellGet -Scope CurrentUser -Force -AllowClobber
}

Remove old modules from existing session

Remove-Module PowerShellGet,PackageManagement -Force -ErrorAction Ignore

Import updated module

Import-Module PowerShellGet -MinimumVersion 2.0 -Force
Import-PackageProvider PowerShellGet -MinimumVersion 2.0 -Force

MSAL is used with a registered MsalClientApplication in Azure for MSGraph Authentication and requires a -ClientId and -ClientSecret or Certificate

See https://github.com/AzureAD/MSAL.PS

Install the required MSAL.PS module if not already installed (Not supported by Microsoft)

if (-not (Get-Module -ListAvailable -Name "MSAL.PS")) {
Install-Module -Name "MSAL.PS" -SkipPublisherCheck -Force -AllowClobber
}

Install the required AzureAD module if not already installed

if (-not (Get-Module -ListAvailable -Name AzureAD)) {
Install-Module -Name AzureAD -Force -AllowClobber
}

Install the required Graph.Intune module if not already installed

if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Intune)) {
Install-Module -Name Microsoft.Graph.Intune -Force -AllowClobber
}

Check if the OS is a client version

$isClientOS = (Get-CimInstance Win32_OperatingSystem).ProductType -eq 1

If it is a client OS

if ($isClientOS) {
# Check if RSAT is installed for Windows 10 or 11 client OS
$feature = Get-WindowsCapability -Online | Where-Object { $_.Name -like 'Rsat*' }
if ($feature.Count -gt 0 -and $feature[0].State -ne 'Installed') {
# Install RSAT tools
Add-WindowsCapability -Online -Name $feature[0].Name
}
}

If it is not a client OS

if (-not $isClientOS) {
# Check if RSAT-AD-PowerShell feature is installed (ONLY WORKS ON SERVER OS)
$featureInstalled = Get-WindowsFeature RSAT-AD-PowerShell -ErrorAction SilentlyContinue
# If not installed, install the feature (ONLY WORKS ON SERVER OS)
if ($featureInstalled -eq $null) {
Install-WindowsFeature RSAT-AD-PowerShell -IncludeAllSubFeature -IncludeManagementTools
}
}

Install the required ActiveDirectory module if not already installed

if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
Install-Module -Name ActiveDirectory -Force -AllowClobber
}

Import Modules that are not loaded for the current session

Check if AzureAD module is already imported

if (-not (Get-Module -Name AzureAD -ListAvailable)) {
Import-Module -Name AzureAD -Force
}

Check if ActiveDirectory module is already imported

if (-not (Get-Module -Name ActiveDirectory -ListAvailable)) {
Import-Module -Name ActiveDirectory -Force
}

Check if Microsoft.Graph.Intune module is already imported

if (-not (Get-Module -Name Microsoft.Graph.Intune -ListAvailable)) {
Import-Module -Name Microsoft.Graph.Intune -Force
}

Connect-MSGraph
Update-MSGraphEnvironment -SchemaVersion "Beta" -Quiet

Set-MgEnvironment -GraphEndpoint "https://graph.microsoft.com/v1.0" -SchemaVersion "beta"

Connect-MSGraph -Quiet

CLS

Define parameters for AD retrieval of computer objects from an OU so that they can be tagged the same.

$OUpath = 'OU=HybridJoin,OU=Laptop,OU=Workstations,OU=Contoso,DC=com'

$GrpTag = "Book"

$OUpath = 'OU=HybridJoin,OU=Conference,OU=Workstations,OU=Contoso,DC=com'

$GrpTag = "Conf"

$OUpath = 'OU=HybridJoin,OU=Desktop,OU=Workstations,OU=Contoso,DC=com'
$GrpTag = "Dtop"

$ExportPath = 'c:\temp\computers_in_ou.csv'
Get-ADComputer -Filter * -SearchBase $OUpath | Select-object Name | Export-Csv -NoType $ExportPath -Force

Define the input file path

$InputFilePath = 'c:\temp\computers_in_ou.csv'

Fetch all devices from Intune

$intuneDevices = Invoke-MSGraphRequest -HttpMethod GET -Url "deviceManagement/managedDevices" | Get-MSGraphAllPages

Load computer names from the input file

$computerNames = Get-Content -Path $InputFilePath

Define an array to store results

$results = @()

foreach ($computer in $computerNames) {
Write-Output "A Computer was found named: " $computer
$PC = $computer.Trim('"')
# Find a matching device in Intune based on computer name
Write-Output "Trying to match " $.deviceName " and " $PC
$matchedDevice = $intuneDevices | Where-Object { $
.deviceName -eq $PC }

if ($matchedDevice) {
    Write-Output "Matching Success"
    $results += [PSCustomObject]@{
        ComputerName  = $PC
        SerialNumber = $matchedDevice.serialNumber
    }
    $computerSerial = $matchedDevice.serialNumber
    Write-Output "SN " $computerSerial

    # Get all autopilot devices
    $autopilotDevices = Invoke-MSGraphRequest -HttpMethod GET -Url "deviceManagement/windowsAutopilotDeviceIdentities" | Get-MSGraphAllPages
    $matchedDevice2 = $autopilotDevices | Where-Object { $_.serialNumber -eq $computerSerial }
    
        if ($matchedDevice2) {
                    $matchedDevice2.groupTag = $GrpTag

    $requestBody = @"
    {
        groupTag: "`"$($matchedDevice2.groupTag)`"",
    }

"@

    Write-Output "Updating entity: $($matchedDevice2.id) | groupTag: $($matchedDevice2.groupTag)"
    Invoke-MSGraphRequest -HttpMethod POST -Content $requestBody -Url "deviceManagement/windowsAutopilotDeviceIdentities/$($matchedDevice2.id)/UpdateDeviceProperties"
}

}

}

Invoke an autopilot service sync

Invoke-MSGraphRequest -HttpMethod POST -Url "deviceManagement/windowsAutopilotSettings/sync"

Remove all values from used variables

$OUpath = $null
$ExportPath = $null
$InputFilePath = $null
$intuneDevices = $null
$computerNames = $null
$results = $null
$computer = $null
$PC = $null
$matchedDevice = $null
$computerSerial = $null
$autopilotDevices = $null
$matchedDevice2 = $null
$requestBody = $null

Delete the input file

Remove-Item -Path $InputFilePath -Force
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment