Public/Test-IntuneGroupMembership.ps1

function Test-IntuneGroupMembership {
    [CmdletBinding()]
    param(
        [Parameter()][string]$UserPrincipalNames,
        [Parameter()][string]$DeviceNames,
        [Parameter()][string]$SimulateTargetGroup,
        [Parameter()][string]$GroupNames,
        [Parameter()][switch]$ExportToCSV,
        [Parameter()][string]$ExportPath,
        [Parameter()][string]$ScopeTagFilter
    )

    Write-Host "Group Membership Impact Analysis selected" -ForegroundColor Green

    # Get User Principal Name and/or Device Name. At least one must be supplied.
    $simUpnInput    = $UserPrincipalNames
    $simDeviceInput = $DeviceNames

    if (-not $simUpnInput -and -not $simDeviceInput) {
        Write-Host "Enter a User Principal Name, a Device name, or both (leave one blank to skip)." -ForegroundColor Cyan
        Write-Host " User Principal Name: " -NoNewline -ForegroundColor Cyan
        $simUpnInput = Read-Host
        Write-Host " Device Name: " -NoNewline -ForegroundColor Cyan
        $simDeviceInput = Read-Host
    }

    if ([string]::IsNullOrWhiteSpace($simUpnInput) -and [string]::IsNullOrWhiteSpace($simDeviceInput)) {
        Write-Host "No User or Device provided. Please supply at least one." -ForegroundColor Red
        return
    }

    $simUpn = $null
    if (-not [string]::IsNullOrWhiteSpace($simUpnInput)) {
        $simUpn = ($simUpnInput -split ',')[0].Trim()
        if ($simUpn -notmatch '^[^@\s]+@[^@\s]+\.[^@\s]+$') {
            Write-Host "Invalid UPN format: '$simUpn'. Expected: user@domain.com" -ForegroundColor Red
            return
        }
    }

    $simDeviceName = if (-not [string]::IsNullOrWhiteSpace($simDeviceInput)) { ($simDeviceInput -split ',')[0].Trim() } else { $null }

    # Get Target Group - SimulateTargetGroup takes precedence over GroupNames
    $simGroupInput = if ($SimulateTargetGroup) { $SimulateTargetGroup }
    elseif ($GroupNames) { $GroupNames }
    else {
        Write-Host "Please enter the Target Group name or Object ID: " -ForegroundColor Cyan
        Write-Host "Example: 'Marketing Team' or '12345678-1234-1234-1234-123456789012'" -ForegroundColor Gray
        Read-Host
    }

    if ([string]::IsNullOrWhiteSpace($simGroupInput)) {
        Write-Host "No group provided. Please try again." -ForegroundColor Red
        return
    }

    $simGroupInput = ($simGroupInput -split ',')[0].Trim()

    # Resolve user (optional)
    $simUserInfo = $null
    if ($simUpn) {
        Write-Host "Looking up user: $simUpn" -ForegroundColor Yellow
        $simUserInfo = Get-UserInfo -UserPrincipalName $simUpn
        if (-not $simUserInfo.Success) {
            Write-Host "User not found: $simUpn" -ForegroundColor Red
            return
        }
    }

    # Resolve device (optional)
    $simDeviceInfo = $null
    if ($simDeviceName) {
        Write-Host "Looking up device: $simDeviceName" -ForegroundColor Yellow
        $simDeviceInfo = Get-DeviceInfo -DeviceName $simDeviceName
        if (-not $simDeviceInfo.Success) {
            Write-Host "Device not found: $simDeviceName" -ForegroundColor Red
            return
        }
        if ($simDeviceInfo.MultipleFound) {
            Write-Host "Multiple devices match name '$simDeviceName'. Use a more specific name." -ForegroundColor Red
            foreach ($d in $simDeviceInfo.AllDevices) {
                Write-Host " - $($d.displayName) (ID: $($d.id), OS: $($d.operatingSystem))" -ForegroundColor Yellow
            }
            return
        }
    }

    # Determine simulation perspective
    $hasUserPersp   = [bool]$simUserInfo
    $hasDevicePersp = [bool]$simDeviceInfo
    $includeReasons = @()
    if ($hasUserPersp)   { $includeReasons += "All Users" }
    if ($hasDevicePersp) { $includeReasons += "All Devices" }

    # Resolve target group
    Write-Host "Looking up group: $simGroupInput" -ForegroundColor Yellow
    if ($simGroupInput -match '^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$') {
        $simGroupInfo = Get-GroupInfo -GroupId $simGroupInput
        if (-not $simGroupInfo.Success) {
            Write-Host "No group found with ID: $simGroupInput" -ForegroundColor Red
            return
        }
        $simTargetGroupId = $simGroupInfo.Id
        $simTargetGroupName = $simGroupInfo.DisplayName
    }
    else {
        # Single quotes escaped for the OData filter (F9)
        $escapedSimGroupName = $simGroupInput -replace "'", "''"
        $simGroupUri = "$script:GraphEndpoint/v1.0/groups?`$filter=displayName eq '$escapedSimGroupName'"
        $simGroupResponse = Invoke-MgGraphRequest -Uri $simGroupUri -Method Get

        if ($simGroupResponse.value.Count -eq 0) {
            Write-Host "No group found with name: $simGroupInput" -ForegroundColor Red
            return
        }
        elseif ($simGroupResponse.value.Count -gt 1) {
            Write-Host "Multiple groups found with name: $simGroupInput. Please use the Object ID instead:" -ForegroundColor Red
            foreach ($g in $simGroupResponse.value) {
                Write-Host " - $($g.displayName) (ID: $($g.id))" -ForegroundColor Yellow
            }
            return
        }

        $simTargetGroupId = $simGroupResponse.value[0].id
        $simTargetGroupName = $simGroupResponse.value[0].displayName
    }

    Write-Host "Target group: $simTargetGroupName (ID: $simTargetGroupId)" -ForegroundColor Green

    # Get current group memberships (union of user and device, depending on what was supplied)
    $simCurrentGroupIds = @()
    try {
        if ($hasUserPersp) {
            $simCurrentGroupIds += @(Get-GroupMemberships -ObjectId $simUserInfo.Id -ObjectType "User" | Where-Object { $_.id } | ForEach-Object { $_.id })
        }
        if ($hasDevicePersp) {
            $simCurrentGroupIds += @(Get-GroupMemberships -ObjectId $simDeviceInfo.Id -ObjectType "Device" | Where-Object { $_.id } | ForEach-Object { $_.id })
        }
        $simCurrentGroupIds = @($simCurrentGroupIds | Select-Object -Unique)
    }
    catch {
        Write-Host "Error fetching group memberships: $($_.Exception.Message)" -ForegroundColor Red
        return
    }

    # Check if subject is already in the target group
    $alreadyMember = $simCurrentGroupIds -contains $simTargetGroupId
    if ($alreadyMember) {
        Write-Host "`nNote: Subject is already a member of '$simTargetGroupName'. Showing policies received via this group." -ForegroundColor Yellow
    }

    # Get target group's parent groups (transitive)
    $simTargetParentGroups = Get-TransitiveGroupMembership -GroupId $simTargetGroupId
    $simTargetAllGroupIds = @($simTargetGroupId)
    if ($simTargetParentGroups) { $simTargetAllGroupIds += $simTargetParentGroups.id }

    # Build simulated group set (current + target + target's parents, deduplicated)
    $simSimulatedGroupIds = @($simCurrentGroupIds + $simTargetAllGroupIds | Select-Object -Unique)

    Write-Host "Analyzing impact..." -ForegroundColor Yellow

    # The legacy 18-step walk matches the UserContext registry entries (same display names, no
    # Settings Catalog ES filter): reorder to the legacy sequence and make Autopilot/ESP fetchable.
    $categoriesById = @{}
    foreach ($category in (Get-IntuneCategoryDefinition -Audience 'UserContext')) { $categoriesById[$category.Id] = $category }
    $categoriesById['DeploymentProfiles'].BucketOnly = $false
    $categoriesById['ESPProfiles'].BucketOnly = $false
    $categories = @(
        @('DeviceConfigurations', 'SettingsCatalog', 'CompliancePolicies', 'AppProtectionPolicies', 'AppConfigurationPolicies',
            'Applications', 'PlatformScripts', 'HealthScripts', 'ESAntivirus', 'ESDiskEncryption', 'ESFirewall',
            'ESEndpointDetection', 'ESAttackSurface', 'ESAccountProtection', 'DeploymentProfiles', 'ESPProfiles',
            'CloudPCProvisioningPolicies', 'CloudPCUserSettings') | ForEach-Object { $categoriesById[$_] })

    # Legacy conflict-row category labels: registry export labels except these five
    $conflictLabelOverrides = @{
        SettingsCatalog             = 'Settings Catalog'
        PlatformScripts             = 'Platform Script'
        HealthScripts               = 'Proactive Remediation Script'
        CloudPCProvisioningPolicies = 'Cloud PC Provisioning'
        CloudPCUserSettings         = 'Cloud PC User Setting'
    }

    $conflictPolicies = [System.Collections.ArrayList]::new()
    # One shared cache: each entity set (configurationPolicies, intents, ...) hits Graph once per run
    $entityCache = @{}
    $appProgress = @{ Current = 0; Total = $null; FilteredTotal = $null }

    $processEntity = {
        param($ctx)

        $entity = $ctx.Entity

        if ($ctx.Category.Kind -eq 'MobileApps') {
            if ($null -eq $appProgress.Total) {
                # Legacy progress counted against the unfiltered app list
                $allCachedApps = @($entityCache[$ctx.Category.EntityType])
                $appProgress.Total = $allCachedApps.Count
                $appProgress.FilteredTotal = @($allCachedApps | Where-Object { -not ($_.isFeatured -or $_.isBuiltIn) }).Count
            }
            $appProgress.Current++
            Write-Host "`rFetching Application $($appProgress.Current) of $($appProgress.Total)" -NoNewline

            # Legacy per-app walk: current vs simulated inclusion/exclusion tracked side by side
            $currentExcluded = $currentIncluded = $simExcluded = $simIncluded = $false
            $appIntent = $simWinningTarget = $null

            foreach ($assignment in $ctx.RawAssignments) {
                $targetType = $assignment.target.'@odata.type'
                $targetGroupId = $assignment.target.groupId

                if ($targetType -eq '#microsoft.graph.exclusionGroupAssignmentTarget') {
                    if ($simCurrentGroupIds -contains $targetGroupId) { $currentExcluded = $true }
                    if ($simSimulatedGroupIds -contains $targetGroupId) { $simExcluded = $true }
                }
                elseif ($targetType -in @('#microsoft.graph.allLicensedUsersAssignmentTarget', '#microsoft.graph.allDevicesAssignmentTarget')) {
                    $allTargetReason = if ($targetType -eq '#microsoft.graph.allLicensedUsersAssignmentTarget') { "All Users" } else { "All Devices" }
                    if ($includeReasons -contains $allTargetReason) {
                        $currentIncluded = $true
                        $simIncluded = $true
                        $appIntent = $assignment.intent
                        if (-not $simWinningTarget) { $simWinningTarget = $assignment.target }
                    }
                }
                elseif ($targetType -eq '#microsoft.graph.groupAssignmentTarget') {
                    if ($simCurrentGroupIds -contains $targetGroupId) { $currentIncluded = $true; $appIntent = $assignment.intent }
                    if ($simSimulatedGroupIds -contains $targetGroupId) {
                        $simIncluded = $true
                        $appIntent = $assignment.intent
                        if (-not $simWinningTarget) { $simWinningTarget = $assignment.target }
                    }
                }
            }

            $currentHasApp = $currentIncluded -and -not $currentExcluded
            $simHasApp = $simIncluded -and -not $simExcluded

            if ($simHasApp -and -not $currentHasApp) {
                $filterSuffix = ''
                if ($simWinningTarget) {
                    $filterSuffix = Format-AssignmentFilter -FilterId $simWinningTarget.deviceAndAppManagementAssignmentFilterId -FilterType $simWinningTarget.deviceAndAppManagementAssignmentFilterType
                }
                $appWithReason = $entity.PSObject.Copy()
                $appWithReason | Add-Member -NotePropertyName 'AssignmentReason' -NotePropertyValue "Group Assignment$filterSuffix" -Force
                $appWithReason | Add-Member -NotePropertyName 'AssignmentIntent' -NotePropertyValue $appIntent -Force
                switch ($appIntent) {
                    "required" { $ctx.Buckets['AppsRequired'].Add($appWithReason) }
                    "available" { $ctx.Buckets['AppsAvailable'].Add($appWithReason) }
                    "uninstall" { $ctx.Buckets['AppsUninstall'].Add($appWithReason) }
                }
            }
            elseif ($currentExcluded -and $simExcluded) {
                # Check if target group specifically includes this app while user is excluded
                foreach ($assignment in $ctx.RawAssignments) {
                    if ($assignment.target.'@odata.type' -eq '#microsoft.graph.groupAssignmentTarget' -and
                        $simTargetAllGroupIds -contains $assignment.target.groupId -and
                        $simCurrentGroupIds -notcontains $assignment.target.groupId) {
                        $appName = if ($entity.displayName) { $entity.displayName } else { $entity.name }
                        [void]$conflictPolicies.Add([PSCustomObject]@{ Category = "Application ($($assignment.intent))"; PolicyName = $appName; PolicyId = $entity.id; ConflictType = "Currently excluded; target group includes it" })
                        break
                    }
                }
            }

            if ($appProgress.Current -eq $appProgress.FilteredTotal) {
                # Legacy final overwrite before the next category header
                Write-Host "`rFetching Application $($appProgress.Total) of $($appProgress.Total)" -NoNewline
                Start-Sleep -Milliseconds 100
                Write-Host ""
            }
            return
        }

        $assignments = $ctx.Assignments
        if ($null -ne $ctx.RawAssignments) {
            # App Protection and intent-phase Endpoint Security detail rows historically carried
            # Reason/GroupId only, so the delta reason gets no filter suffix. App Protection
            # additionally never matched All Devices targets.
            $assignments = @($assignments | ForEach-Object { [PSCustomObject]@{ Reason = $_.Reason; GroupId = $_.GroupId } })
            if ($ctx.Category.Kind -eq 'AppProtection') {
                $assignments = @($assignments | Where-Object { $_.Reason -ne 'All Devices' })
            }
        }

        $delta = Resolve-SimulatedAssignmentDelta -Assignments $assignments -CurrentGroupIds $simCurrentGroupIds -SimulatedGroupIds $simSimulatedGroupIds -TargetGroupIds $simTargetAllGroupIds -IncludeReasons $includeReasons
        if ($delta.IsNewPolicy) {
            $entity | Add-Member -NotePropertyName 'AssignmentReason' -NotePropertyValue $delta.AssignmentReason -Force
            $ctx.Buckets[$ctx.Category.BucketKeys[0]].Add($entity)
        }
        elseif ($delta.IsConflict) {
            $conflictLabel = if ($conflictLabelOverrides.ContainsKey($ctx.Category.Id)) { $conflictLabelOverrides[$ctx.Category.Id] } else { $ctx.Category.ExportCategory }
            $policyName = if ($entity.displayName) { $entity.displayName } else { $entity.name }
            [void]$conflictPolicies.Add([PSCustomObject]@{ Category = $conflictLabel; PolicyName = $policyName; PolicyId = $entity.id; ConflictType = "Currently excluded; target group includes it" })
        }
    }

    $scanResult = Invoke-IntuneCategoryScan -Categories $categories -ProcessEntity $processEntity -ShowProgress -EntityCache $entityCache
    $deltaPolicies = $scanResult.Buckets

    # Apply scope tag filter if specified
    if ($ScopeTagFilter) {
        foreach ($key in @($deltaPolicies.Keys)) {
            $deltaPolicies[$key] = @(Filter-ByScopeTag -Items $deltaPolicies[$key] -FilterTag $ScopeTagFilter -ScopeTagLookup $script:ScopeTagLookup)
        }
    }

    # ===== DISPLAY RESULTS =====
    Write-Host ""
    Write-Host (Get-Separator -Character "=") -ForegroundColor Yellow
    Write-Host " SIMULATION RESULTS - GROUP MEMBERSHIP IMPACT ANALYSIS" -ForegroundColor Yellow
    Write-Host " (no changes were made)" -ForegroundColor DarkGray
    Write-Host (Get-Separator -Character "=") -ForegroundColor Yellow
    if ($hasUserPersp)   { Write-Host " User: $simUpn" -ForegroundColor White }
    if ($hasDevicePersp) { Write-Host " Device: $($simDeviceInfo.DisplayName) (ID: $($simDeviceInfo.Id))" -ForegroundColor White }
    Write-Host " Target Group: $simTargetGroupName (ID: $simTargetGroupId)" -ForegroundColor White
    if ($alreadyMember) {
        Write-Host " Status: Subject is ALREADY a member of this group" -ForegroundColor Yellow
    }
    Write-Host (Get-Separator -Character "=") -ForegroundColor Yellow

    # Category display mapping (legacy section order and labels)
    $categoryDisplay = [ordered]@{
        DeviceConfigs = "Device Configurations"; SettingsCatalog = "Settings Catalog Policies"
        CompliancePolicies = "Compliance Policies"; AppProtectionPolicies = "App Protection Policies"
        AppConfigurationPolicies = "App Configuration Policies"; AppsRequired = "Required Apps"
        AppsAvailable = "Available Apps"; AppsUninstall = "Uninstall Apps"
        PlatformScripts = "Platform Scripts"; HealthScripts = "Proactive Remediation Scripts"
        AntivirusProfiles = "Endpoint Security - Antivirus"; DiskEncryptionProfiles = "Endpoint Security - Disk Encryption"
        FirewallProfiles = "Endpoint Security - Firewall"; EndpointDetectionProfiles = "Endpoint Security - EDR"
        AttackSurfaceProfiles = "Endpoint Security - ASR"; AccountProtectionProfiles = "Endpoint Security - Account Protection"
        DeploymentProfiles = "Autopilot Deployment Profiles"; ESPProfiles = "Enrollment Status Page Profiles"
        CloudPCProvisioningPolicies = "Windows 365 Cloud PC Provisioning"; CloudPCUserSettings = "Windows 365 Cloud PC User Settings"
    }

    # Legacy cell truncation: over Max chars becomes (Max - 3) chars plus "..."
    $truncate = { param($Text, $Max) if ($Text.Length -gt $Max) { $Text.Substring(0, $Max - 3) + "..." } else { $Text } }

    $totalNewPolicies = 0
    foreach ($catKey in $categoryDisplay.Keys) {
        $items = $deltaPolicies[$catKey]
        if ($items.Count -gt 0) {
            $catLabel = $categoryDisplay[$catKey]
            $totalNewPolicies += $items.Count
            Write-Host "`n------- NEW: $catLabel ($($items.Count)) -------" -ForegroundColor Green
            $headerFormat = "{0,-50} {1,-40} {2,-30}" -f "Policy Name", "Policy ID", "Assignment Reason"
            $separator = Get-Separator
            Write-Host $separator
            Write-Host $headerFormat -ForegroundColor Yellow
            Write-Host $separator

            foreach ($item in $items) {
                $itemName = if (-not [string]::IsNullOrWhiteSpace($item.displayName)) { $item.displayName } else { $item.name }
                if (-not $itemName) { $itemName = "Unnamed" }
                $itemId = if ($item.id) { $item.id } else { "Unknown" }
                $reason = if ($item.AssignmentReason) { $item.AssignmentReason } else { "Unknown" }
                Write-Host ("{0,-50} {1,-40} {2,-30}" -f (& $truncate $itemName 47), (& $truncate $itemId 37), (& $truncate $reason 27)) -ForegroundColor White
            }
            Write-Host $separator
        }
    }

    # Display conflicts
    if ($conflictPolicies.Count -gt 0) {
        Write-Host "`n------- CONFLICTS (Exclusion Overrides) -------" -ForegroundColor Red
        Write-Host "Note: In Intune, exclusions take priority over inclusions." -ForegroundColor Yellow
        $headerFormat = "{0,-50} {1,-35} {2,-35}" -f "Policy Name", "Category", "Conflict"
        $separator = Get-Separator
        Write-Host $separator
        Write-Host $headerFormat -ForegroundColor Yellow
        Write-Host $separator

        foreach ($conflict in $conflictPolicies) {
            Write-Host ("{0,-50} {1,-35} {2,-35}" -f (& $truncate $conflict.PolicyName 47), (& $truncate $conflict.Category 32), (& $truncate $conflict.ConflictType 32)) -ForegroundColor Red
        }
        Write-Host $separator
    }

    # Build a display label for the simulation subject(s)
    $subjectLabel = if ($hasUserPersp -and $hasDevicePersp) {
        "User '$simUpn' + Device '$($simDeviceInfo.DisplayName)'"
    } elseif ($hasUserPersp) {
        "User '$simUpn'"
    } else {
        "Device '$($simDeviceInfo.DisplayName)'"
    }

    # Summary
    Write-Host "`n=== Impact Summary ===" -ForegroundColor Cyan
    if ($alreadyMember) {
        Write-Host "$subjectLabel is already a member of '$simTargetGroupName'." -ForegroundColor Yellow
        Write-Host "The following shows policies currently received via this group:" -ForegroundColor Yellow
    }
    else {
        Write-Host "Adding $subjectLabel to '$simTargetGroupName' would result in:" -ForegroundColor White
    }

    $categoryCount = ($categoryDisplay.Keys | Where-Object { $deltaPolicies[$_].Count -gt 0 }).Count
    $conflictCount = $conflictPolicies.Count

    if ($totalNewPolicies -eq 0 -and $conflictCount -eq 0) {
        Write-Host " No new policy assignments and no conflicts." -ForegroundColor Yellow
    }
    else {
        $parts = @()
        if ($totalNewPolicies -gt 0) {
            $parts += "$totalNewPolicies new $(if ($totalNewPolicies -eq 1) { 'policy' } else { 'policies' }) across $categoryCount $(if ($categoryCount -eq 1) { 'category' } else { 'categories' })"
        }
        if ($conflictCount -gt 0) {
            $parts += "$conflictCount $(if ($conflictCount -eq 1) { 'conflict' } else { 'conflicts' })"
        }
        Write-Host " Impact: $($parts -join ', ')" -ForegroundColor $(if ($conflictCount -gt 0) { "Yellow" } else { "Green" })
    }

    # Export
    $exportData = [System.Collections.ArrayList]::new()
    $null = $exportData.Add([PSCustomObject]@{
        Category         = "Simulation Info"
        Item             = "$subjectLabel -> Group: $simTargetGroupName (ID: $simTargetGroupId)"
        ScopeTags        = ""
        AssignmentReason = if ($alreadyMember) { "Already a member" } else { "Impact Analysis" }
    })

    # Legacy CSV order and labels for the NEW-policy rows
    $exportSections = [ordered]@{
        'NEW: Device Configuration' = 'DeviceConfigs'; 'NEW: Settings Catalog Policy' = 'SettingsCatalog'
        'NEW: Compliance Policy' = 'CompliancePolicies'; 'NEW: App Protection Policy' = 'AppProtectionPolicies'
        'NEW: App Configuration Policy' = 'AppConfigurationPolicies'; 'NEW: Required App' = 'AppsRequired'
        'NEW: Available App' = 'AppsAvailable'; 'NEW: Uninstall App' = 'AppsUninstall'
        'NEW: Platform Script' = 'PlatformScripts'; 'NEW: Proactive Remediation Script' = 'HealthScripts'
        'NEW: Endpoint Security - Antivirus' = 'AntivirusProfiles'; 'NEW: Endpoint Security - Disk Encryption' = 'DiskEncryptionProfiles'
        'NEW: Endpoint Security - Firewall' = 'FirewallProfiles'; 'NEW: Endpoint Security - EDR' = 'EndpointDetectionProfiles'
        'NEW: Endpoint Security - ASR' = 'AttackSurfaceProfiles'; 'NEW: Endpoint Security - Account Protection' = 'AccountProtectionProfiles'
        'NEW: Autopilot Deployment Profile' = 'DeploymentProfiles'; 'NEW: Enrollment Status Page Profile' = 'ESPProfiles'
        'NEW: Cloud PC Provisioning Policy' = 'CloudPCProvisioningPolicies'; 'NEW: Cloud PC User Setting' = 'CloudPCUserSettings'
    }
    foreach ($section in $exportSections.GetEnumerator()) {
        Add-ExportData -ExportData $exportData -Category $section.Key -Items $deltaPolicies[$section.Value] -AssignmentReason { param($item) $item.AssignmentReason }
    }

    foreach ($conflict in $conflictPolicies) {
        $null = $exportData.Add([PSCustomObject]@{
            Category         = "CONFLICT: $($conflict.Category)"
            Item             = "$($conflict.PolicyName) (ID: $($conflict.PolicyId))"
            ScopeTags        = ""
            AssignmentReason = $conflict.ConflictType
        })
    }

    Export-ResultsIfRequested -ExportData $exportData -DefaultFileName "IntuneGroupMembershipImpact.csv" -ForceExport:$ExportToCSV -CustomExportPath $ExportPath -ExportToCSV:$ExportToCSV -ParameterMode:$parameterMode
}