Forum Discussion

Flo-KKIT's avatar
Flo-KKIT
Copper Contributor
Oct 23, 2023

Bulk update of AutoPilot group tags via Powershell

As the title says, I want to use a command to customize the group tags of many AutoPilot devices. However, I don't want to use a file as a basis first, as described in many tutorials, but access directly the AutoPilot devices in Intune.

 

In other words, change all devices with a group tag named "A" to a new tag named "B".

Do you guys have a solution on how I can do this? I've only read something about the Graph API so far?

 

Thanks a lot!

4 Replies

    • Andrew_Beard's avatar
      Andrew_Beard
      Copper Contributor

      I found this method stopped working for me around the end of September 2023. Before that it worked fine for me to use a script to automatically add Group Tags to Autopilot devices in Intune based on their on-prem AD OU placement.

      I am happy to share the full script I was using that has stopped working if anyone is interested. I have reached out to the author of the original script to see if he has any ideas. I think personally that some Beta feature of the previous MSGraph scripting has been changed and no longer works as it used to.
      I have also attempted to use the PowerShell module https://www.powershellgallery.com/packages/WindowsAutoPilotIntune/5.6
      but that also has no success at least how I have tried when using MSGraph direct login (without registered app ID and secret etc.).
      If anyone has any help to offer I would be most grateful as I do this type of thing many times whilst working as a contract senior IT technician for many different companies on the same cloud enabled Hybrid-Join journey.

      • Andrew_Beard's avatar
        Andrew_Beard
        Copper Contributor
        # GroupTagBulkListWinAutopilotDevices-v4.ps1
        
        # Run this script with local admin rights, preferably on a Windows server.
        
        CLS
        $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
        }
        
        # Install the required WindowsAutoPilotIntune module if not already installed
        if (-not (Get-Module -ListAvailable -Name WindowsAutoPilotIntune)) {
            Install-Module -Name WindowsAutoPilotIntune -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 WindowsAutoPilotIntune module is already imported
        if (-not (Get-Module -Name WindowsAutoPilotIntune -ListAvailable)) {
            Import-Module -Name WindowsAutoPilotIntune -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 = "Ltop"
        # $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 "=============================================================================="
            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 (-not $matchedDevice) {
                Write-Output "Matching Failed"
                }
        
            if ($matchedDevice) {
                Write-Output "Matching Success"
                $results += [PSCustomObject]@{
                    ComputerName  = $PC
                    SerialNumber = $matchedDevice.serialNumber
                }
                $computerSerial = $matchedDevice.serialNumber
                Write-Output "SN: $computerSerial"
        
                # Get all autopilot devices for Invoke-MSGraphRequest method
                $autopilotDevices = Invoke-MSGraphRequest -HttpMethod GET -Url "deviceManagement/windowsAutopilotDeviceIdentities" | Get-MSGraphAllPages
                # Single out the autopilot device we are dealing with that has a name matching the serial number of the Intune/AD device.
                $matchedDevice2 = $autopilotDevices | Where-Object { $_.serialNumber -eq $computerSerial }
                
                    if ($matchedDevice2) {
                                $matchedDevice2.groupTag = $GrpTag
        
                # Request Body required for Invoke-MSGraphRequest parameter.
                $requestBody = @"
                {
                    groupTag: "`"$($matchedDevice2.groupTag)`"",
                }
        "@
                
                # Quoted Group Tag required for Set-AutopilotDevice command parameter.
                $QuotedGrpTag = '"' + $GrpTag + '"'
        
                Write-Output "Updating entity: $($matchedDevice2.id) | groupTag: $($matchedDevice2.groupTag)"
                # Attempt 2 different methods to set the Group Tag for the Serial Number Autopilot object
                Set-AutopilotDevice -id $computerSerial -groupTag $QuotedGrpTag
                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
        $GrpTag = $null
        $QuotedGrpTag = $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

Resources