Public/New-IntuneDynamicGroup.ps1
|
function New-IntuneDynamicGroup { <# .SYNOPSIS Creates a dynamic Azure AD group for Intune .DESCRIPTION Creates a dynamic group with the specified membership rule. If a group with the same name exists, returns the existing group. .PARAMETER DisplayName The display name for the group .PARAMETER Description Description of the group .PARAMETER MembershipRule OData membership rule for dynamic membership .PARAMETER MembershipRuleProcessingState Processing state for the rule (On or Paused) .EXAMPLE New-IntuneDynamicGroup -DisplayName "Windows 11 Devices" -MembershipRule "(device.operatingSystem -eq 'Windows') and (device.operatingSystemVersion -startsWith '10.0.22')" #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory = $true)] [string]$DisplayName, [Parameter()] [string]$Description = "", [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidateScript({ $_ -match '^\(' }, ErrorMessage = "MembershipRule must start with a parenthesis")] [string]$MembershipRule, [Parameter()] [ValidateSet('On', 'Paused')] [string]$MembershipRuleProcessingState = 'On' ) try { # Compute final display name with prefix for both lookup and creation $finalDisplayName = "$($script:ImportPrefix)$DisplayName" $safeFinalName = $finalDisplayName -replace "'", "''" $safeOrigName = $DisplayName -replace "'", "''" # Check for both prefixed and unprefixed names (backward compat with pre-prefix groups) $allMatches = Get-GraphPagedResults -Uri "beta/groups?`$select=id,displayName,description&`$filter=displayName eq '$safeFinalName' or displayName eq '$safeOrigName'" # Prefer prefixed+tagged match, then any tagged match, then exact prefixed name (to avoid duplicates) $existingGroup = $null if ($allMatches) { foreach ($match in $allMatches) { $isTagged = Test-HydrationKitObject -Description $match.description -ObjectName $match.displayName if ($match.displayName -eq $finalDisplayName -and $isTagged) { $existingGroup = $match break } if ($isTagged -and -not $existingGroup) { $existingGroup = $match } } # Fallback: if no tagged match, still skip exact prefixed name to avoid duplicates if (-not $existingGroup) { $existingGroup = $allMatches | Where-Object { $_.displayName -eq $finalDisplayName } | Select-Object -First 1 } } if ($existingGroup) { return New-HydrationResult -Name $existingGroup.displayName -Id $existingGroup.id -Type 'DynamicGroup' -Action 'Skipped' -Status 'Group already exists' } # Create new dynamic group if ($PSCmdlet.ShouldProcess($finalDisplayName, "Create dynamic group")) { $fullDescription = New-HydrationDescription -ExistingText $Description $groupBody = @{ displayName = $finalDisplayName description = $fullDescription mailEnabled = $false mailNickname = ($DisplayName -replace '[^a-zA-Z0-9]', '') securityEnabled = $true groupTypes = @('DynamicMembership') membershipRule = $MembershipRule membershipRuleProcessingState = $MembershipRuleProcessingState } $newGroup = Invoke-MgGraphRequest -Method POST -Uri "beta/groups" -Body $groupBody -ErrorAction Stop return New-HydrationResult -Name $newGroup.displayName -Id $newGroup.id -Type 'DynamicGroup' -Action 'Created' -Status 'New group created' } else { return New-HydrationResult -Name $DisplayName -Type 'DynamicGroup' -Action 'WouldCreate' -Status 'DryRun' } } catch { Write-Error "Failed to create group '$DisplayName': $_" return New-HydrationResult -Name $DisplayName -Type 'DynamicGroup' -Action 'Failed' -Status $_.Exception.Message } } |