Updated My Script To “Add Scope Tags Based On Primary User”, Faster and better logging

When I released Version 1 of my script Intune-Set-ScopeTagBasedOnPrimaryUser, I was in a hurry and managed to get it up and running at one customer. I discovered some bugs in it and fixed that in my customer environment. But did not remember to update it on GitHub. Sorry for that. I had some error reports from community usage and finally I found a time to update the script completely. Now the published version is also tested and verified in 3 live environments.

Updates in version 2.0::

  • Up to 4x faster execution – Added graph batching both when getting devices and patching devices
  • More resilient error handling Updated with the same functions from my latest GitHub – Intune-Set-PrimaryUsers.ps1
  • Cleaner reporting and logging Nice fast logging function

This update isn’t just an incremental improvement, it’s a fundamental shift in logic, how the script interacts with Graph and how logging and reporting works.

👉 Grab the new version here: GitHub – Intune-Add-ScopeTagsBasedOnPrimaryUsers

Why setting Scope Tags on Device objects in Intune

Scope tags in Microsoft Intune provide a structured way to delegate administration and enforce governance across a single tenant. By applying scope tags to devices, policies, and applications, organizations can ensure that administrators only see and manage the resources relevant to their role, department, or region.

This approach delivers several key benefits:

  • Delegated Administration: Scope tags align with role-based access control (RBAC) to restrict visibility and actions to the appropriate subset of devices.
  • Security and Compliance: They reduce the risk of accidental or unauthorized changes by limiting access to sensitive configurations.
  • Operational Efficiency: Scope tags allow large organizations to segment devices logically (e.g., by geography, business unit, or function) without duplicating tenants or policies.
  • Governance and Auditability: Clear boundaries between teams improve accountability and make it easier to track administrative activity.
  • Scalability: As the environment grows, scope tags provide a flexible mechanism to expand management without losing control.

In practice, scope tags ensure that Intune remains a single, unified management platform while supporting distributed IT teams with precise, role-appropriate access. They are a cornerstone for delegated admin roles (RBAC) in Intune.

Why have i written this script?

Microsoft Intune does not natively support automatic assignment of scope tags to devices based on user attributes such as country, department, or business unit. By default, scope tags must be applied manually or through static assignment, which can be inefficient and prone to error in large environments.

To address this gap, I have created this script so that organizations can implement a scheduled automation that reads the primary user’s attributes (for example, the country field in Entra ID) and applies the corresponding scope tag to the device. This scripted automation delivers several advantages:

  • Delegated Administration at Scale: Ensures that devices are automatically tagged according to the user’s organizational context, so regional or departmental admins only see the devices relevant to them.
  • Consistency and Accuracy: Reduces human error by eliminating manual tagging, ensuring that scope tags always reflect the correct user attributes.
  • Operational Efficiency: Automates repetitive tasks, freeing administrators to focus on higher‑value governance and security work.
  • Dynamic Alignment: As users move between regions or departments, scheduled scripts can re‑evaluate and update scope tags, keeping device assignments current without manual intervention.
  • Governance and Auditability: Provides a clear, repeatable process for scope tag assignment, which supports compliance and makes administrative boundaries auditable.

In practice, this approach bridges a functional gap in Intune by combining directory attributes with automation. The scheduled script becomes a governance tool that enforces organizational boundaries consistently, ensuring that Intune remains a unified platform while supporting distributed IT teams with precise, role‑appropriate access.

What’s New in Version 2.0

Here’s a breakdown of the improvements:

 Performance

  • Batch queries for devices to enumerate scope tags
  • Batch queries to patch devices with correct scope tags
  • Execution time reduced by up to 25% in large tenants

Error Handling

  • Reused functions from Intune-Set-IntunePrimaryUsers that have better stability and error handling
  • Smarter error handler for Graph errors
  • Clearer error messages with actionable context

 Reporting

  • New Logging function that makes the logging more intelligent. You can log to GUI, Disk or even Event log (does not work in azure automation)
  • Detail report now in better table view for Azure Automation

Compatibility

  • Works also on iOS, MacOS and Android
  • Supports both Windows PowerShell 5.1 and PowerShell 7.x and Azure Automation
  • Requires only the Microsoft.Graph.Authentication module

How to customize the script

Script Parameters and Primary user attribute

First you need to customize the defaults in parameters to suit your environment. One important thing is to update the $ScopeTagAttribute with the name of the attribute to use on the primary user to determine what scope tags should be applied. this is default to the most common one: “country”. You can also decide if you want to keep the built-in scope tag “Default” with $KeepBuiltinTag.

#region ---------------------------------------------------[Modifiable Parameters and Defaults]------------------------------------
# Customizations
[CmdletBinding(SupportsShouldProcess)]
param(
    [Parameter(Mandatory = $false,          HelpMessage = "Name of the script action for logging. Default is 'Intune Primary User'")]
    [string]$ScriptActionName       = "Intune Set Scope Tags based on Primary User",

    [Parameter(Mandatory = $false,          HelpMessage = "Device operatingsystems to process ('All', 'Windows', 'Android', 'iOS', 'macOS'). Default is 'Windows'")]
    [ValidateSet('All', 'Windows', 'Android', 'iOS', 'macOS')]
    [string[]]$OperatingSystems     = @('Windows'),

    [Parameter(Mandatory = $false,          HelpMessage = "Attribute to use for the scope tag. Default is 'Country'")]
    [string]$ScopeTagAttribute      = "Country",

    [Parameter(Mandatory = $false,          HelpMessage = "Whether to keep the built-in scope tag (usually called Default). Default is true")]
    [bool]$KeepBuiltinTag           = $true,

    [Parameter(Mandatory = $false,          HelpMessage = "Filter to only include devicenames that starts with specific strings like ('Tbone', 'Desktop'). Default is blank")]
    [string[]]$IncludedDeviceNames  = @(),

    [Parameter(Mandatory = $false,          HelpMessage = "Filter to exclude devicenames that starts with specific strings like ('Tbone', 'Desktop'). Default is blank")]
    [string[]]$ExcludedDeviceNames  = @(),

    [Parameter(Mandatory = $false,          HelpMessage = "Return report with statistics on how many changed objects. Default is true")]
    [bool]$ReturnReport             = $true,

    [Parameter(Mandatory = $false,          HelpMessage = "Include detailed device changes in the report. Default is true")]
    [bool]$DetailedReport           = $true,

    [Parameter(Mandatory = $false,          HelpMessage = "Save report to disk. Default is false")]
    [bool]$ReportToDisk             = $false,    

    [Parameter(Mandatory = $false,          HelpMessage = "Path where to save the report. Default is TEMP directory for Azure Automation compatibility")]
    [string]$ReportPath             = "$env:TEMP",

    [Parameter(Mandatory = $false,          HelpMessage = "Enable verbose logging. Default is false")]
    [bool]$VerboseLogging           = $true,

    [Parameter(Mandatory = $false,          HelpMessage = "Wait time in milliseconds between throttled requests. Default is 1000")]
    [ValidateRange(100,5000)]
    [int]$WaitTime                  = 1000,

    [Parameter(Mandatory = $false,          HelpMessage = "Maximum number of retry attempts for failed requests. Default is 3")]
    [ValidateRange(1,10)]
    [int]$MaxRetry                  = 3,

    [Parameter(Mandatory = $false,          HelpMessage = "Maximum number of items to process in a single batch. Default is 20")]
    [ValidateRange(1,20)]
    [int]$BatchSize                  = 20,

    [Parameter(Mandatory = $false,          HelpMessage = "Testmode, same as -WhatIf. Default is false")]
    [bool]$Testmode                 = $true
    )
#endregion

Mapping table to scope tags

You also must update the table with user attribute value to scope tag name. so that the script know what scope tag to add on a device where e the primary user has this specific value

#region ---------------------------------------------------[Modifiable Variables and defaults]------------------------------------
# Define User attribute to scope tags mapping by table
$ScopeTagMappings = @{
#   User attribute Value= Scope Tag Name    
    'Sweden'            = 'SE'
    'Germany'           = 'DE'
    'France'            = 'FR'
    'Poland'            = 'PL'
    'United States'     = 'US'
    'China'             = 'CN'
    'Republic of Korea' = 'KR'
    'Japan'             = 'JP'
    'India'             = 'IN'
}
#endregion

New Logging function

The more logging the less performance! But although logging is excessive and makes execution time longer. We sometimes need to troubleshoot. I have been poking around with a logging function “Invoke-TboneLog” for a while now. And I have added it to this script. This function can write to GUI, Disk or Eventlog. But, it is completely optional. The function just proxy all write- functions and return the text with colour code and more info:

If removed, the script is using correct write- functions throughout the script. Just remove the lines with the function call for “Invoke-TboneLog” to skip using it.

New Reporting function

I have also added a generic reporting function to use for scripts making changes. it support summary and detailed report and makes a nice output both in gui or in azure automation.

Azure Automation Verbose Logging

Verbose logging can be enabled in my script, and supported by my logging function. But in Azure Automation, it also needs to be enabled in the Runbook:

How to Get Started

  1. 👉 Grab the new version here: GitHub – Intune-Add-ScopeTagsBasedOnPrimaryUsers
  2. To setup Azure Automation, Have a look at the old post Set Intune Primary User with Azure Automation
  3. Assign the required Graph API permissions:
    • DeviceManagementManagedDevices.ReadWrite.All
    • AuditLog.Read.All
    • User.Read.All
  4. Run in Test Mode first: .\Intune-Set-PrimaryUsers.ps1 -TestMode $true
  5. Review the output, then run in production mode.

So, go ahead and test it out and report issues on Github.

About The Author

Mr T-Bone

Torbjörn Tbone Granheden is a Solution Architect for Modern Workplace at Coligo AB. Most Valuable Professional (MVP) on Enterprise Mobility. Certified in most Microsoft technologies and over 23 years as Microsoft Certified Trainer (MCT)

You may also like...

Leave a Reply