Bulk Enrol Device to Driver and Firmware Servicing

The driver and firmware serving solution currently allows you to onboard up to 2000 devices at once using a CSV file, and even then most of the leg work need to be completed by the Admin to export, populate a CSV and then upload it and so on. It isn’t yet possible to map an AAD Group to the deployment audience either which again, isn’t always ideal if you have devices in their thousands and you want to enrol them into the service to bring back some more control.

With all that said and done, it is completely possible to automate this process with the Graph API and PowerShell. In this article we will focus on how to achieve just that with very little effort required from admins.

While this solution will check the current update audience for a device, it will not check all of your audiences.

This can leave devices ending up in multiple policies if they exist in multiple groups, and this is not a recommended practice, although it is allowed.


  • Permissions to connect to the Graph API with the following scopes
    • WindowsUpdates.ReadWrite.All
    • Group.Read.All
  • Permissions to Azure AD with the Ability to connect via PowerShell
  • Microsoft.Graph PowerShell Module

The script snippets and process will all be driven around a script called Update-BulkMembers.ps1 which is store on my GitHub repo, this can be accessed using the button below;

GitHub Resource

Executing the Script

There are two things needed to execute the script;

  • Azure AD Group ID
  • Update Audience ID (for assistance on finding this, look at THIS POST)

It is super simple to achieve the end goal by just executing the scrip with the following command;

Update-BulkMembers.ps1 -aadGroupID <AADGroupID> -audienceId <updateAudienceID>

This will then get all group members, break them up in to chunks of 2000 devices and then onboard them into the services and populate the audience. However, if you want to know how it works, stick around and lets break it down.

Breaking down the script

We will skip past the first few bits which handle parameters, module installation and authentication and skip straight to the goodies!

The first part of the code (below), will get the ObjectID’s of the members of the Azure AD Group, and then display the count of devices, before then breaking it down into chunks of 2000 ids, and then getting all of the members of the current audience.

#Get Group Members IDs
$GroupMemberIDs = (Get-MgGroupMember -GroupId $aadGroupID -All).id
"$aadgroupID $($GroupMemberIDs.Count) members"

#Break the id's into chunks of 2000
$chunks = [System.Collections.ArrayList]::new()
for ($i = 0; $i -lt $GroupMemberIDs.Count; $i += 2000) {
    if (($GroupMemberIDs.Count - $i) -gt 1999 ) {
        $chunks.add($GroupMemberIDs[$i..($i + 1999)])
    else {
        $chunks.add($GroupMemberIDs[$i..($GroupMemberIDs.Count - 1)])

$updateAudienceMembers = Invoke-GetRequest `
    -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$audienceId')/members" -All

Following on from this, we enter the foreach loop for the chunks that were created in the previous snippet. Inside this foreach loop, it will get the DeviceID for each of the members and add them to the $azureDeviceIDs variable as follows;

$AzureDeviceIDs = @()
$GroupMemberIDs | foreach-object {
    $DeviceID = (Get-AzureADDevice -ObjectID $_).DeviceID
    $AzureDeviceIDs += $DeviceID

Following that, we create two post body objects, one for the enrollment into the service, the second for adding the device to the audience.
$enrollParamBody = @{
    updateCategory = "driver"
    assets = @(

$audienceParamBody = @{
    addMembers = @(

We then do a foreach loop of the Azure AD DeviceID`s, and check if they exist in the policy audience, and also check if they are enrolled into the service. If they are NOT if will add them to the respective post bodies for invocation a right at the end.

foreach ($id in $azureDeviceIDs) {
    IF (-Not($updateAudienceMembers.id -contains $id)) {
        $memberObject = @{
            "@odata.type" = "#microsoft.graph.windowsUpdates.azureADDevice"
            id            = $id
        $audienceParamBody.addMembers += $memberObject

    IF(-Not($updateAudienceMembers.id -contains $id) -or ($updateAudienceMembers | Where-Object {$_.id -match $id}).enrollments.updateCategory -notcontains "driver"){
        $memberObject = @{
            "@odata.type" = "#microsoft.graph.windowsUpdates.azureADDevice"
            id            = $id
        $enrollParamBody.assets += $memberObject

Last, but not least we post the relevant bodies to the relevant endpoints and we are a wrap!
#Explicitly Enrol Devices
Invoke-MgGraphRequest `
    -Method POST `
    -Uri "https://graph.microsoft.com/beta/admin/windows/updates/updatableAssets/enrollAssets" `
    -Body $enrollParamBody 

#Post Audience Members
Invoke-MgGraphRequest `
    -Method POST `
    -Uri "https://graph.microsoft.com/beta/admin/windows/updates/deploymentAudiences('$audienceId')/updateAudience" `
    -Body ( $audienceParamBody | ConvertTo-Json -Depth 5)

Closing Thoughts

I hope this will help you further onboard devices at scale to really put the services to the test before we get the intune capabilities or even the ability to use native Azure AD groups.

Please reach out with any feedback you may have too :).

comments powered by Disqus