PowerShell Intune: Bulk Assign App Policies via Graph SDK
─□✕

PowerShell Intune: Bulk Assign App Policies via Graph SDK

PowerShell Tips Editor 4 min read
PowerShell Intune: Bulk Assign App Policies via Graph SDK

Manually assigning Intune app configuration and compliance policies to device groups through the Endpoint Manager portal is workable for a handful of groups, but it becomes error-prone and time-consuming at scale. A single compliance policy may need to be assigned to 30 different Azure AD groups with different filter conditions, and doing that by hand invites omissions and inconsistency. The Microsoft Graph PowerShell SDK provides the exact same API the portal uses, allowing you to define assignments in a CSV, execute them in bulk, and generate a completion report that proves the configuration is correct.

Quick Answer

Authenticate with Connect-MgGraph using the DeviceManagementConfiguration.ReadWrite.All scope, retrieve the policy with Get-MgDeviceManagementDeviceCompliancePolicy, resolve group IDs with Get-MgGroup, build an assignment body, and call the policy assignment endpoint with New-MgDeviceManagementDeviceCompliancePolicyAssignment.

Required Graph Permissions for Intune Policy Management

The Graph SDK delegates permissions through the consent granted at connection time. For bulk Intune policy assignment you need at minimum DeviceManagementConfiguration.ReadWrite.All and Group.Read.All. Request them explicitly in the -Scopes parameter so consent is recorded correctly for both interactive and app-only flows.

# Install the required modules if not already present
Install-Module Microsoft.Graph.DeviceManagement,
              Microsoft.Graph.Groups -Scope CurrentUser -Force

# Connect with the required scopes
Connect-MgGraph -Scopes "DeviceManagementConfiguration.ReadWrite.All",
                         "DeviceManagementManagedDevices.ReadWrite.All",
                         "Group.Read.All" -NoWelcome

For unattended automation use a service principal with app-only permissions and Connect-MgGraph -ClientId ... -TenantId ... -CertificateThumbprint ... instead of delegated scopes.

Enumerating Existing Policies with Get-MgDeviceManagement

Compliance policies and app configuration policies live in different Graph endpoints. Compliance policies are under /deviceManagement/deviceCompliancePolicies; app configuration policies for managed devices are under /deviceManagement/mobileAppConfigurations. List them to confirm policy names before attempting assignment — policy IDs are GUIDs that do not carry human-readable context in error messages.

# List all compliance policies
$compliancePolicies = Get-MgDeviceManagementDeviceCompliancePolicy -All
$compliancePolicies | Select-Object Id, DisplayName, @{N="Type";E={ $_.OdataType }} |
    Format-Table -AutoSize
Id                                   DisplayName                    Type
--                                   -----------                    ----
a1b2c3d4-...                         Windows 11 Compliance Baseline #microsoft.graph.windows10CompliancePolicy
e5f6g7h8-...                         iOS Minimum OS Policy          #microsoft.graph.iosCompliancePolicy

Resolving Target Groups by Name with Get-MgGroup

Policy assignments require the Azure AD group object ID, not the display name. Resolve names to IDs before the assignment loop so you catch typos early and avoid partial assignments.

$groupNames = @("Grp-Devices-Windows", "Grp-Devices-iOS", "Grp-Kiosk-Devices")

$groupMap = @{}
foreach ($name in $groupNames) {
    $group = Get-MgGroup -Filter "displayName eq '$name'" -ErrorAction Stop
    if (-not $group) { throw "Group not found: $name" }
    $groupMap[$name] = $group.Id
    Write-Output "Resolved '$name' -> $($group.Id)"
}

Building Policy Assignment Objects with AssignmentTarget

Each assignment requires a body that specifies the target type. For a specific Azure AD group, the OData type is #microsoft.graph.groupAssignmentTarget. For all devices or all users, use #microsoft.graph.allDevicesAssignmentTarget or #microsoft.graph.allLicensedUsersAssignmentTarget respectively. Mixing these up is the most common cause of assignment failures.

function New-GroupAssignmentBody {
    param([string]$GroupId)
    return @{
        target = @{
            "@odata.type" = "#microsoft.graph.groupAssignmentTarget"
            groupId       = $GroupId
        }
    }
}

Assigning Policies in Bulk with New-MgDeviceManagementAssignment

Iterate over your policy-to-group mapping and create each assignment. Wrap the call in a try/catch so one failed assignment does not abort the entire run, and capture results for the completion report.

$policyId = ($compliancePolicies | Where-Object { $_.DisplayName -eq "Windows 11 Compliance Baseline" }).Id
$assignmentResults = [System.Collections.Generic.List[PSCustomObject]]::new()

foreach ($groupName in $groupNames) {
    try {
        $body = New-GroupAssignmentBody -GroupId $groupMap[$groupName]
        $assignment = New-MgDeviceManagementDeviceCompliancePolicyAssignment `
            -DeviceCompliancePolicyId $policyId `
            -BodyParameter $body `
            -ErrorAction Stop

        $assignmentResults.Add([PSCustomObject]@{
            Policy = "Windows 11 Compliance Baseline"
            Group  = $groupName
            Status = "Assigned"
            Id     = $assignment.Id
        })
        Write-Output "Assigned to $groupName"
    } catch {
        $assignmentResults.Add([PSCustomObject]@{
            Policy = "Windows 11 Compliance Baseline"
            Group  = $groupName
            Status = "FAILED: $($_.Exception.Message)"
            Id     = $null
        })
    }
}

Verifying Assignments and Generating a Completion Report

After the bulk run, retrieve current assignments from Graph to confirm each one is live, then export the completion report to CSV for change-management records. Cross-referencing the API response against your input list catches any silent failures the SDK may not have surfaced as exceptions.

Common Errors

  • Assignment target type mismatch: Passing #microsoft.graph.allDevicesAssignmentTarget when a policy expects a group target, or vice versa, returns a 400 Bad Request. Always verify the @odata.type value against the Graph documentation for the specific policy type.
  • Graph SDK beta endpoint required: Some Intune operations — particularly app protection policies and newer feature configurations — are only available on the /beta endpoint. Select the beta profile with Select-MgProfile -Name "beta" before calling those cmdlets, then switch back to v1.0 for stable operations.

Related Cmdlets / See Also

Wrapping Up

Bulk Intune policy assignment with the Graph SDK turns a repetitive portal task into a repeatable, auditable script. Resolve group IDs before the loop, use typed assignment targets, and capture all results for a completion report that doubles as your change-management evidence.

Send-Item -To