Public/Remove-IntuneAppGroup.ps1
|
<# .SYNOPSIS Deletes Intune app assignment groups from Microsoft Entra ID. .DESCRIPTION The Remove-IntuneAppGroup function deletes the groups created by New-IntuneAppGroup. With -Name, the app groups and app collection groups of those applications are found the same way as Get-IntuneAppGroup -Name does; use -Intent to delete only some of them. With -Id (or by piping the output of Get-IntuneAppGroup or New-IntuneAppGroup), the groups with those object IDs are deleted. A group whose name does not start with 'Intune-AG-' or 'Intune-ACG-' is never deleted; an error is written instead. Deleted security groups cannot be restored. ConfirmImpact is High, so you are asked to confirm each group unless you use -Confirm:$false. Supports -WhatIf. Nothing is written to the pipeline. Requires the Microsoft Entra PowerShell module (Microsoft.Entra or Microsoft.Entra.Groups) and a session opened with Connect-Entra that can delete groups, for example the Group.ReadWrite.All scope. .PARAMETER Name One or more application names whose groups are deleted. Accepts pipeline input. .PARAMETER Intent With -Name, delete only the groups for these intents (Available, Required, Uninstall). By default the groups for every intent are deleted. .PARAMETER Id The object IDs of the groups to delete. Accepts pipeline input by property name (Id, ObjectId or GroupId), for example from Get-IntuneAppGroup. .INPUTS System.String Application names. System.Management.Automation.PSObject Objects with an Id property, such as the output of Get-IntuneAppGroup. .OUTPUTS None .EXAMPLE Remove-IntuneAppGroup -Name 'Company Portal' -WhatIf Shows which Company Portal groups would be deleted. .EXAMPLE Remove-IntuneAppGroup -Name 'Company Portal' -Intent Uninstall -Confirm:$false Deletes Intune-AG-CompanyPortal-Uninstall (and Intune-ACG-CompanyPortal-Uninstall) without asking. .EXAMPLE Get-IntuneAppGroup -All | Where-Object AppName -EQ 'OldApp' | Remove-IntuneAppGroup Deletes the groups returned by Get-IntuneAppGroup, asking for each one. .NOTES Author: Nigel Tatschner Company: TheCodeSaiyan .LINK Get-IntuneAppGroup .LINK New-IntuneAppGroup .LINK https://learn.microsoft.com/powershell/module/microsoft.entra.groups/remove-entragroup #> function Remove-IntuneAppGroup { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High', DefaultParameterSetName = 'ByName')] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = 'ByName')] [Alias('AppName')] [ValidateNotNullOrEmpty()] [string[]] $Name, [Parameter(ParameterSetName = 'ByName')] [ValidateSet('Available', 'Required', 'Uninstall')] [string[]] $Intent, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ById')] [Alias('ObjectId', 'GroupId')] [ValidateNotNullOrEmpty()] [string[]] $Id ) begin { $TelemetryArgs = @{ ModuleName = $MyInvocation.MyCommand.Module.Name ModuleVersion = [string]$MyInvocation.MyCommand.Module.Version CommandName = $MyInvocation.MyCommand.Name ExecutionID = [guid]::NewGuid().ToString() } Invoke-TelemetryCollection @TelemetryArgs -Stage Start -ClearTimer $lastError = $null $telemetrySent = $false $prerequisiteError = Get-EntraPrerequisiteError -CommandName $MyInvocation.MyCommand.Name -RequiredCommand 'Get-EntraGroup', 'Remove-EntraGroup' if ($prerequisiteError) { $telemetrySent = $true Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed $true -Exception $prerequisiteError $PSCmdlet.ThrowTerminatingError($prerequisiteError) } } process { $completed = $false try { $groups = New-Object -TypeName 'System.Collections.Generic.List[object]' if ($PSCmdlet.ParameterSetName -eq 'ById') { foreach ($groupId in $Id) { try { $group = Get-EntraGroup -GroupId $groupId -ErrorAction Stop } catch { $lastError = $_ Write-Error -ErrorRecord $_ continue } if (-not [regex]::IsMatch([string]$group.DisplayName, '^Intune-(AG|ACG)-', 'IgnoreCase, CultureInvariant')) { $exception = New-Object -TypeName System.ArgumentException -ArgumentList "The group '$($group.DisplayName)' ($groupId) is not an Intune app group and was not deleted.", 'Id' $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $exception, 'NotAnIntuneAppGroup', ([System.Management.Automation.ErrorCategory]::InvalidArgument), $groupId $lastError = $errorRecord $PSCmdlet.WriteError($errorRecord) continue } $groups.Add($group) } } else { foreach ($appName in $Name) { $pascalName = ConvertTo-TcsPascalCaseName -Text $appName if (-not $pascalName) { $exception = New-Object -TypeName System.ArgumentException -ArgumentList 'An application name cannot be blank or whitespace only.', 'Name' $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $exception, 'BlankName', ([System.Management.Automation.ErrorCategory]::InvalidArgument), $appName $lastError = $errorRecord $PSCmdlet.WriteError($errorRecord) continue } try { $found = @(Find-IntuneAppGroup -AppName $pascalName) } catch { $lastError = $_ Write-Error -ErrorRecord $_ continue } if ($Intent) { $found = @($found | Where-Object { $groupIntent = ([string]$_.DisplayName -split '-')[-1] $Intent -contains $groupIntent }) } if ($found.Count -eq 0) { $exception = New-Object -TypeName System.Management.Automation.ItemNotFoundException -ArgumentList "No Intune app groups were found for '$appName'." $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $exception, 'IntuneAppGroupNotFound', ([System.Management.Automation.ErrorCategory]::ObjectNotFound), $appName $lastError = $errorRecord $PSCmdlet.WriteError($errorRecord) continue } foreach ($group in $found) { $groups.Add($group) } } } foreach ($group in $groups) { if (-not $PSCmdlet.ShouldProcess("$($group.DisplayName) ($($group.Id))", 'Remove Entra ID security group')) { continue } try { Remove-EntraGroup -GroupId $group.Id -ErrorAction Stop Write-Verbose "Group `"$($group.DisplayName)`" removed." } catch { $lastError = $_ Write-Error -ErrorRecord $_ } } $completed = $true } catch { # Reached when the caller asked for errors to stop (-ErrorAction Stop) $lastError = $_ throw } finally { # The end block does not run after a terminating error or when the pipeline is stopped, # so End telemetry is sent here. if (-not $completed -and -not $telemetrySent) { $telemetrySent = $true if ($lastError) { Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed $true -Exception $lastError } else { Invoke-TelemetryCollection @TelemetryArgs -Stage End } } } } end { if (-not $telemetrySent) { $telemetrySent = $true if ($lastError) { Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed $true -Exception $lastError } else { Invoke-TelemetryCollection @TelemetryArgs -Stage End } } } } |