A few code snippets for Azure AD dynamic groups. One for creating standard groups for your environment. Another for finding duplicates.
I’ve implemented InTune for our corporate environment and handed it off to someone else to manage. This person did a great job of managing things despite the fact that InTune underwent several backend migrations and updates that either broke or left unpredictable software distribution groups in the environment. I’ve finally found a few cycles to round back on this one and found some funny things.
Essentially, there were around 90 dynamic groups auto-created called “Subsidiary*” that were almost all duplicates of the same dynamic filter including all computers in the environment. It was bizarre and confusing but also understandable (as we didn’t heed the InTune upgrade warnings and take necessary actions at the time). Anyway, this led me to want to create some standard dynamic groups as well as find and remove the duplicates.
For adding the new groups you can use this gist. This is easily modified to add or remove groups of your choosing. This didn’t really warrant a new module or anything so there are some built in variables that control whether existing dynamic groups are updated or left alone when this script runs. This would be a decent candidate for scheduling to be run via Azure Automation on a regular basis.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
<# Creates or updates existing a handful of AzureAD dynamic groups for use in Azure AD Requires the AzureADPreview module to work correctly! More on dynamic membership in Azure AD: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-groups-dynamic-membership-azure-portal #> Remove-Module AzureAD -Force -ErrorAction:SilentlyContinue Import-Module AzureADPreview Connect-AzureAD # Update this JSON to suit your needs or add/remove groups. $DynamicGroups = ('{ "Groups":[{ "Type":"device", "Name":"enabled_allpcs", "Filter":"(device.managementType eq \"PC\") and (device.accountEnabled eq true)", "Description":"All Enabled PC Devices" }, { "Type":"device", "Name":"enabled_ios", "Filter":"((device.deviceOSType contains \"iPhone\") or (device.deviceOSType contains \"iPad\")) and (device.accountEnabled eq true)", "Description":"All Enabled iOS Devices" }, { "Type":"device", "Name":"enabled_rootedios", "Filter":"(device.deviceOSType contains \"iPhone\" or device.deviceOSType contains \"iPad\") and device.accountEnabled eq true and device.isRooted eq true", "Description":"All Enabled iOS Devices" }, { "Type":"device", "Name":"enabled_android", "Filter":"device.deviceOSType contains \"Android\" and device.accountEnabled eq true", "Description":"All Enabled Android Devices" }, { "Type":"device", "Name":"enabled_rootedandroid", "Filter":"device.deviceOSType -contains \"Android\" and device.accountEnabled eq true and device.isRooted eq true", "Description":"All Enabled and Rooted Android Devices" }, { "Type":"device", "Name":"enabled_windows", "Filter":"device.deviceOSType contains \"Windows\" and device.accountEnabled eq true", "Description":"All Enabled Windows Devices" }, { "Type":"device", "Name":"disabled", "Filter":"device.accountEnabled eq false", "Description":"All Disabled Devices" }, { "Type":"device", "Name":"personal", "Filter":"device.deviceOwnership -eq \"Personal\"", "Description":"All personal devices" }, { "Type":"device", "Name":"MDM", "Filter":"device.managementType -eq \"MDM\"", "Description":"All MDM devices" }, { "Type":"user", "Name":"all", "Filter":"(user.assignedPlans -any assignedPlan.service -startsWith "SCO" -and assignedPlan.capabilityStatus -eq \"Enabled\")", "Description":"All personal devices" } ]}' | ConvertFrom-Json).Groups # {{Type}} will get replaced by the group type in the name. Not required in this string $GroupNamePrefix = 'azure_dyn_{{Type}}_' # If a group with the same name already exists try to update its filter if it is dynamic $OverWriteExisting = $true # Create or update dynamic groups Foreach ($NewGroup in $DynamicGroups) { $GroupName = ($GroupNamePrefix -replace '{{Type}}',$NewGroup.Type) + $NewGroup.Name $ExistingGroup = Get-AzureADMSGroup -Filter "DisplayName eq '$GroupName'" if ($null -ne $ExistingGroup) { Write-Output "Found existing group: $GroupName" if ($OverWriteExisting) { Write-Output " Attempt to overwrite group filter: TRUE" if ($ExistingGroup.GroupTypes -eq 'DynamicMembership') { Write-Output " Dynamic group: TRUE" if ($ExistingGroup.MembershipRule -ne $NewGroup.Filter) { Write-Output " Filters differ: TRUE" try { $null = Set-AzureADMSGroup -Id $ExistingGroup.Id -MembershipRule $NewGroup.Filter -Description $NewGroup.Description } catch { Write-Warning " Unable to update $GroupName!" } } else { Write-Output " Filters differ: FALSE (nothing to do!)" } } else { Write-Output " Dynamic group: FALSE" Write-Warning " Unable to update group as it is not dynamic!" } } else { Write-Output " Attempt to overwrite group filter: FALSE (nothing ot do!)" } } else { Write-Output "New Dynamic Group: $GroupName" try { $null = New-AzureADMSGroup -DisplayName $GroupName -MailNickname $GroupName -MembershipRule $NewGroup.Filter -Description $NewGroup.Description -MailEnabled:$false -MembershipRuleProcessingState 'On' -GroupTypes 'DynamicMembership' -SecurityEnabled:$true } catch { Write-Warning " Unable to create the dynamic group!" } } } |
For finding your duplicate dynamic groups use this gist. This will not find all duplicate filters but it will find the most common ones and is a good starting point.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<# Finds a good portion of possible dupliate dynamic groups in Azure AD. Will not find super complicated member filter duplicates but should find most common filters (like all PCs and the zillions of 'Subsidiary*' groups that InTune upgrades have created on the back end) Requires the AzureADPreview module to work correctly! More on dynamic membership in Azure AD: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-groups-dynamic-membership-azure-portal #> Remove-Module AzureAD -Force -ErrorAction:SilentlyContinue Import-Module AzureADPreview Connect-AzureAD function Normalize-DynamicGroupFilter ($Filter) { # Somewhat normalizes the Odata filter on dynamic member groups for comparison sake if ($null -ne $filter) { $filter -replace '\(','' ` -replace '\)','' ` -replace '\ -eq',' eq' ` -replace '\ -any',' any' ` -replace '\ -ne',' ne' } else { $null } } Function Get-AzureADDuplicateDynamicGroups { # Gets a list of dynamic groups that have duplicate filters $CurrentDyngroups = Get-AzureADMSGroup -All:$True | Where {($_.GroupTypes -eq 'DynamicMembership')} Foreach ($DynGroup in $CurrentDynGroups) { $DynGroup | Add-Member -MemberType NoteProperty -Name 'NormalizedFilter' -Value (Normalize-DynamicGroupFilter $DynGroup.MembershipRule) } $CurrentDynGroups | Group-Object NormalizedFilter } Get-AzureADDuplicateDynamicGroups | Foreach { if ($_.Count -gt 1) { Write-Output "Normalized Filter = $($_.Name) : Groups found = $($_.Count)" Write-Output '' $_.Group } } |
Reminder that the dynamic group cmdlets are only functional in the AzureADPreview module. You may have to explicitly load this module so AzureAD doesn’t interfere with things and cause a bunch of errors to get thrown (this is seen in the code at the beginning where I force remove AzureAD and load AzureADPreview).
Comments (0)