Public/New-IntuneMermaidGraph.ps1

<#
.SYNOPSIS
Generates a Mermaid.js flowchart for Intune applications or profiles.

.DESCRIPTION
This function generates a Mermaid.js flowchart based on the specified type (Applications or Profiles).
It retrieves data from Microsoft Graph API and organizes it into a flowchart format that can be rendered using Mermaid.js.
The flowchart can be grouped by resource name or assignment groups, and can be filtered by operating system and resource type.

.PARAMETER Type
Specifies the type of resource on Intune to generate the flowchart for.
Valid values are "Applications" and "Profiles". Default is "Applications".

.PARAMETER Direction
Specifies the direction of the flowchart.
Valid values are "TB" (top to bottom), "TD" (top down), "BT" (bottom to top), "LR" (left to right), and "RL" (right to left).
Default is "TB".

.PARAMETER DisplayIcons
Specifies whether to display icons for applications in the flowchart.
When set to $True, application icons will be embedded in the flowchart as base64-encoded images.
Default is $True.

.PARAMETER GroupBy
Specifies the grouping criteria for the flowchart.
If you would like to see the flowchart ordered by Applications/Profiles or by Entra ID Groups they are assigned to.
Valid values are "Name" (for Applications/Profiles) and "Assignments" (for Entra ID groups).
Default is "Name".

.PARAMETER OperatingSystem
Specifies the operating systems to include in the flowchart.
Valid values are "Windows", "macOS", "iOS", and "Android".
Default includes all operating systems.

.PARAMETER PolicyType
Dynamic parameter that appears only when Type is "Profiles".
Allows filtering of configuration profiles by their policy type.
Includes values like "Device restrictions", "Endpoint protection", "Administrative templates", etc.

.PARAMETER ApplicationType
Dynamic parameter that appears only when Type is "Applications".
Allows filtering of applications by their application type.
Includes values like "Windows app (Win32)", "iOS store app", "Android store app", etc.

.EXAMPLE
# Generate a Mermaid.js flowchart for applications grouped by assignments with icons displayed.
New-IntuneMermaidGraph -Type "Applications" -GroupBy "Assignments" -DisplayIcons $True

.EXAMPLE
# Generate a Mermaid.js flowchart for profiles in a left-to-right layout.
New-IntuneMermaidGraph -Type "Profiles" -Direction "LR"

.EXAMPLE
# Generate a flowchart for Windows applications only, with no icons.
New-IntuneMermaidGraph -Type "Applications" -OperatingSystem "Windows" -DisplayIcons $False

.EXAMPLE
# Generate a flowchart for specific application types only.
New-IntuneMermaidGraph -Type "Applications" -ApplicationType "Windows app (Win32)","Microsoft 365 Apps (Windows 10 and later)"

.EXAMPLE
# Generate a flowchart for iOS device restriction profiles only.
New-IntuneMermaidGraph -Type "Profiles" -OperatingSystem "iOS" -PolicyType "Device restrictions"

.EXAMPLE
# Generate a flowchart of profiles grouped by assignment groups for Android and iOS only.
New-IntuneMermaidGraph -Type "Profiles" -GroupBy "Assignments" -OperatingSystem @("Android", "iOS")

.NOTES
- Requires the Microsoft Graph PowerShell module and proper authentication.
- Ensure you have the necessary permissions to access the Microsoft Graph API (DeviceManagementApps.Read.All, GroupMember.Read.All, DeviceManagementConfiguration.Read.All).
- The function caches Entra ID group display names to improve performance when retrieving multiple assignments.
- Application and profile styles are modified based on their notes/description field values.
- Output can be used directly with Mermaid.js visualization tools or libraries.

#>

function New-IntuneMermaidGraph {
    param (
        [Parameter(Mandatory = $false)]
        [ValidateSet("Applications", "Profiles")]
        [string]$Type = "Applications",
                
        [Parameter(Mandatory = $false)]
        [ValidateSet("Name", "Assignments")]
        [string]$GroupBy = "Name",

        [Parameter(Mandatory = $false)]
        [ValidateSet("Windows", "macOS", "iOS", "Android")]
        [array]$OperatingSystem = @("Windows", "macOS", "iOS", "Android"),

        [Parameter(Mandatory = $false)]
        [ValidateSet("TB", "TD", "BT", "LR", "RL")]
        [string]$Direction = "TB",
        
        [Parameter(Mandatory = $false)]
        [bool]$DisplayIcons = $True

    )

    DynamicParam {
        # Create dictionary for dynamic parameters
        $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

        # Only add PolicyType parameter if Type is "Profiles"
        if ($Type -eq "Profiles") {
            $policyTypeAttribute = New-Object System.Management.Automation.ParameterAttribute
            $policyTypeAttribute.Mandatory = $false
            $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute(
                "Administrative templates", "App configuration", "Custom", "Derived credentials",
                "Device features", "Device firmware", "Device restrictions", "Delivery optimization",
                "Domain join", "Edition upgrade", "Education", "Email", "Endpoint protection",
                "Expedited check-in", "Extensions", "Hardware configurations", "IKEv2 VPN",
                "Identity protection", "Information protection", "Kiosk", "Microsoft Defender for Endpoint",
                "Network boundary", "OMA-CP", "PFX certificate", "PKCS certificate",
                "Policy override", "Preference file", "Presets", "SCEP certificate", 
                "Secure assessment (Education)", "Settings Catalog", "Shared multi-user device", "Teams device restrictions",
                "Trusted certificate", "Unsupported", "Update Configuration", "Update rings for Windows updates",
                "VPN", "Wi-Fi", "Wi-Fi import", "Windows health monitoring", "Wired network"
            )    
            $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($policyTypeAttribute)
            $attributeCollection.Add($validateSetAttribute)
            $policyTypeParam = New-Object System.Management.Automation.RuntimeDefinedParameter(
                'PolicyType', [array], $attributeCollection
            )
            $policyTypeParam.Value = @()
            $paramDictionary.Add('PolicyType', $policyTypeParam)
        }
        
        # Only add ApplicationType parameter if Type is "Applications"
        if ($Type -eq "Applications") {
            $appTypeAttribute = New-Object System.Management.Automation.ParameterAttribute
            $appTypeAttribute.Mandatory = $false
            
            $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute(
                "Android Enterprise system app", "Managed Google Play store app",
                "Android line-of-business app", "Android store app", "Built-In Android app",
                "iOS/iPadOS web clip", "iOS line-of-business app", "iOS store app",
                "iOS volume purchase program app", "macOS app (DMG)", "macOS line-of-business app",
                "Microsoft Defender ATP (macOS)", "Microsoft Edge (macOS)", "macOS Office Suite",
                "macOS app (PKG)", "macOS volume purchase program app", "macOS web clip",
                "Managed iOS store app", "Microsoft 365 Apps (Windows 10 and later)", "Web link",
                "Windows catalog app (Win32)", "Windows app (Win32)", "Microsoft Store app (new)",
                "Microsoft Edge (Windows 10 and later)", "Windows MSI line-of-business app",
                "Microsoft Store app (legacy)", "Windows Universal AppX line-of-business app", 
                "Windows web link"
            )
            $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($appTypeAttribute)
            $attributeCollection.Add($validateSetAttribute)
            $appTypeParam = New-Object System.Management.Automation.RuntimeDefinedParameter(
                'ApplicationType', [array], $attributeCollection
            )
            $appTypeParam.Value = @()
            $paramDictionary.Add('ApplicationType', $appTypeParam)
        }
        return $paramDictionary
    }

    Begin {
        $requiredScopeCategories = @{
            "Groups" = @("GroupMember.Read.All", "Group.Read.All", "Group.ReadWrite.All", "Directory.Read.All", "Directory.ReadWrite.All")
            "Apps" = @("DeviceManagementApps.Read.All", "DeviceManagementApps.ReadWrite.All")
            "Configuration" = @("DeviceManagementConfiguration.Read.All", "DeviceManagementConfiguration.ReadWrite.All")
        }
        
        # Ensure required authentication header variable exists
        if ($null -eq (Get-MgContext)) { 
            $requiredScopesMessage = $requiredScopeCategories.Keys | ForEach-Object { 
                "$($requiredScopeCategories[$_][0])"
            }
            throw "Microsoft Graph authentication required. Please run `"Connect-MgGraph -scopes $($requiredScopesMessage -join ', ')`" at a minimum."
        }
        else {
            $currentScopes = (Get-MgContext).Scopes
            $missingCategories = @()

            foreach ($category in $requiredScopeCategories.Keys) {
                $categoryScopes = $requiredScopeCategories[$category]
                $hasRequiredScope = $false
                
                foreach ($scope in $categoryScopes) {
                    if ($scope -in $currentScopes) {
                        $hasRequiredScope = $true
                        break
                    }
                }
                
                if (-not $hasRequiredScope -and (Get-MgContext).AuthType -ne 'UserProvidedAccessToken') {
                    $missingCategories += $category
                }
            }
            
            if ($missingCategories.Count -gt 0) {
                $missingScopesMessage = $missingCategories | ForEach-Object { 
                    "${_}: $($requiredScopeCategories[$_] -join ' / ')"
                }
                throw "Microsoft Graph token found, but missing required scopes. Please ensure you have at least one permission from each category:`n$($missingScopesMessage -join "`n")"
            }
        }

        # Set script variable for error action preference
        $ErrorActionPreference = "Stop"
        
        # Create a cache for group display names to reduce API calls
        $script:groupCache = @{}
        
        # Helper function to get group display name with caching
        function Get-GroupDisplayName {
            param (
                [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
                [string[]]$groupId
            )
            
            begin {
                # Initialize the cache if it doesn't exist yet
                if (-not $script:groupCache) {
                    $script:groupCache = @{}
                }
            
                # Collection for group IDs that need to be retrieved
                $groupsToRetrieve = @()
            }
            
            process {
                foreach ($id in $groupId) {
                    if (-not $script:groupCache.ContainsKey($id)) {
                        $groupsToRetrieve += $id
                    }
                }
            }
            
            end {
                # If there are groups to retrieve, fetch them in parallel if possible
                if ($groupsToRetrieve.Count -gt 0) {
                    if ($PSVersionTable.PSEdition -eq 'Core' -and $groupsToRetrieve.Count -gt 1) {
                        # PowerShell Core - use ForEach-Object -Parallel for multiple groups
                        $maxConcurrentJobs = [Math]::Min(20, $groupsToRetrieve.Count) # Limit concurrent jobs
                        $retrievedGroups = $groupsToRetrieve | ForEach-Object -ThrottleLimit $maxConcurrentJobs -Parallel {
                            try {
                                $groupData = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/directoryObjects/$_"
                                [PSCustomObject]@{
                                    Id          = $_
                                    DisplayName = $groupData.displayName
                                    Success     = $true
                                }
                            }
                            catch {
                                [PSCustomObject]@{
                                    Id          = $_
                                    DisplayName = "Group deleted from Microsoft Entra ID"
                                    Success     = $false
                                }
                            }
                        }
                
                        # Update cache with batch results
                        foreach ($group in $retrievedGroups) {
                            $script:groupCache[$group.Id] = @{
                                DisplayName = $group.DisplayName.replace(".", "") # Remove periods from display name
                                Shortname   = $group.DisplayName  # Store the unmodified name
                            }
                        }
                    }
                    else {
                        # Sequential processing for Windows PowerShell or single group
                        foreach ($id in $groupsToRetrieve) {
                            try {
                                $groupData = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/directoryObjects/$id"
                                $script:groupCache[$id] = @{
                                    DisplayName = $groupData.displayName.replace(".", "") # Remove periods from display name
                                    Shortname   = $groupData.displayName  # Store the unmodified name
                                }
                            }
                            catch {
                                $script:groupCache[$id] = @{
                                    DisplayName = "Group deleted from Microsoft Entra ID"
                                    Shortname   = "Group deleted from Microsoft Entra ID"
                                }
                            }
                        }
                    }
                }
            
                # Return requested group info
                if ($groupId.Count -eq 1) {
                    return $script:groupCache[$groupId[0]]
                }
                else {
                    $result = @{}
                    foreach ($id in $groupId) {
                        $result[$id] = $script:groupCache[$id]
                    }
                    return $result
                }
            }
        }
        
        # Helper function to get data with pagination
        function Get-GraphDataWithPagination {
            param (
                [string]$Uri
            )
            $allData = @()
            $nextLink = $Uri
            do {
                $response = Invoke-MgGraphRequest -Method GET -Uri $nextLink -Headers @{Authorization = "Bearer $((Get-MgContext).AccessToken)" }
                $allData += $response.value
                $nextLink = $response.'@odata.nextLink'
            } while ($nextLink)
            return $allData
        }
        
        # Helper function to get filter display names
        function Get-FilterDisplayNames {
            param (
                [array]$Items
            )
            
            $filterDisplayNames = @{}
            $uniqueFilterIds = $Items.assignments.target.deviceAndAppManagementAssignmentFilterId | Select-Object -Unique
            foreach ($filterId in $uniqueFilterIds) {
                if ($filterId) {
                    try {
                        $filter = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters/$filterId"
                        $filterDisplayNames[$filterId] = $filter.displayName.replace("(", "").replace(")", "")
                    }
                    catch {
                        $filterDisplayNames[$filterId] = "Filter not found"
                    }
                }
            }
            return $filterDisplayNames
        }
        
        # Helper function to get assignment target name
        function Get-AssignmentTargetName {
            param (
                [object]$Target
            )
            
            if ($Target.deviceAndAppManagementAssignmentFilterType -eq 'none' -or 
                $Target.deviceAndAppManagementAssignmentFilterType -eq 'Include' -or 
                $Target.deviceAndAppManagementAssignmentFilterType -eq 'Exclude') {
                if ($Target.groupId) {
                    $groupInfo = Get-GroupDisplayName -groupId $Target.groupId
                    return $groupInfo.Shortname
                }
                elseif ($Target.'@odata.type' -like "*allDevices*") {
                    return "All Devices"
                }
                elseif ($Target.'@odata.type' -like "*allLicensedUsers*") {
                    return "All Users"
                }
            }
            return "Unknown"
        }
        
        # Initialize the main flowchart with common components
        function Initialize-Flowchart {
            param (
                [string]$Direction
            )
            # Initialize the flowchart with the specified direction
            $flowchart = "flowchart $Direction"
            return $flowchart
        }
    }
    
    Process {
        switch ($Type) {
            "Applications" {
                Write-Verbose "Processing applications type"
                # Dictionary to map application odata types to friendly names
                $AppTypeMap = @{
                    '#microsoft.graph.androidEnterpriseSystemApp' = "Android Enterprise system app" 
                    '#microsoft.graph.androidForWorkApp'          = "Managed Google Play store app" 
                    '#microsoft.graph.androidManagedStoreApp'     = "Managed Google Play store app" 
                    '#microsoft.graph.androidLobApp'              = "Android line-of-business app" 
                    '#microsoft.graph.androidStoreApp'            = "Android store app" 
                    '#microsoft.graph.builtInAndroid'             = "Built-In Android app" 
                    '#microsoft.graph.iosIPadOSWebClip'           = "iOS/iPadOS web clip" 
                    '#microsoft.graph.iosLobApp'                  = "iOS line-of-business app" 
                    '#microsoft.graph.iosStoreApp'                = "iOS store app" 
                    '#microsoft.graph.iosVppApp'                  = "iOS volume purchase program app" 
                    '#microsoft.graph.macOSDmgApp'                = "macOS app (DMG)" 
                    '#microsoft.graph.macOSLobApp'                = "macOS line-of-business app" 
                    '#microsoft.graph.macOSMicrosoftDefenderApp'  = "Microsoft Defender ATP (macOS)" 
                    '#microsoft.graph.macOSMicrosoftEdgeApp'      = "Microsoft Edge (macOS)" 
                    '#microsoft.graph.macOSOfficeSuiteApp'        = "macOS Office Suite" 
                    '#microsoft.graph.macOSPkgApp'                = "macOS app (PKG)" 
                    '#microsoft.graph.macOsVppApp'                = "macOS volume purchase program app" 
                    '#microsoft.graph.macOSWebClip'               = "macOS web clip" 
                    '#microsoft.graph.managedIOSStoreApp'         = "Managed iOS store app" 
                    '#microsoft.graph.officeSuiteApp'             = "Microsoft 365 Apps (Windows 10 and later)" 
                    '#microsoft.graph.webApp'                     = "Web link" 
                    '#microsoft.graph.win32CatalogApp'            = "Windows catalog app (Win32)" 
                    '#microsoft.graph.win32LobApp'                = "Windows app (Win32)" 
                    '#microsoft.graph.winGetApp'                  = "Microsoft Store app (new)" 
                    '#microsoft.graph.windowsMicrosoftEdgeApp'    = "Microsoft Edge (Windows 10 and later)" 
                    '#microsoft.graph.windowsMobileMSI'           = "Windows MSI line-of-business app" 
                    '#microsoft.graph.windowsStoreApp'            = "Microsoft Store app (legacy)" 
                    '#microsoft.graph.windowsUniversalAppX'       = "Windows Universal AppX line-of-business app" 
                    '#microsoft.graph.windowsWebApp'              = "Windows web link" 
                }
                Write-Verbose "Application type mapping dictionary created"
                
                # Get all apps with assignments
                Write-Verbose "Retrieving all applications with assignments"
                $allAppsAndAssignments = Get-GraphDataWithPagination -Uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?`$filter=(isAssigned eq true)&`$expand=Assignments"
                Write-Verbose "Retrieved $($allAppsAndAssignments.Count) applications with assignments"
                
                # Replace @odata.type with friendly names
                Write-Verbose "Replacing @odata.type with friendly names"
                foreach ($app in $allAppsAndAssignments) {
                    $appType = $app.'@odata.type'
                    if ($AppTypeMap.ContainsKey($appType)) {
                        $app | Add-Member -MemberType NoteProperty -Name 'appTypeName' -Value $AppTypeMap[$appType] -Force
                    }
                    else {
                        $app | Add-Member -MemberType NoteProperty -Name 'appTypeName' -Value $appType -Force
                    }
                }
                Write-Verbose "Friendly names added to all applications"

                # Filter apps by ApplicationType if specified
                if ($PSBoundParameters.ContainsKey('ApplicationType') -and $PSBoundParameters.ApplicationType.Count -gt 0) {
                    Write-Verbose "Filtering applications by specified ApplicationType: $($PSBoundParameters.ApplicationType -join ', ')"
                    $allAppsAndAssignments = $allAppsAndAssignments | Where-Object { $_.appTypeName -in $PSBoundParameters.ApplicationType }
                    Write-Verbose "Filtered to $($allAppsAndAssignments.Count) applications"
                }
                # If display icons are enabled, retrieve icons for each application
                if ($DisplayIcons) {
                    Write-Verbose "Retrieving icons for each application"
                    # Check if running in PowerShell Core (supports parallel processing)
                    if ($PSVersionTable.PSEdition -eq 'Core') {
                        Write-Verbose "Using parallel processing with PowerShell Core to retrieve icons for each application"
                        $maxConcurrentJobs = 20  # Adjust based on your system's capabilities and API limits
                        $allAppsAndAssignments | ForEach-Object -ThrottleLimit $maxConcurrentJobs -Parallel {
                            try {
                                $appInfo = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($_.id)"
                                if ($appInfo.largeIcon.value) {
                                    $_ | Add-Member -MemberType NoteProperty -Name 'largeIcon' -Value $appInfo.largeIcon.value -Force
                                }
                            }
                            catch {
                                Write-Verbose "Failed to retrieve icon for application ID: $($_.id)" -Verbose
                            }
                        }   
                    }
                    else {
                        Write-Verbose "Using sequential processing with Windows PowerShell Desktop to retrieve icons for each application"
                        foreach ($app in $allAppsAndAssignments) {
                            try {
                                $appInfo = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($app.id)"
                                if ($appInfo.largeIcon.value) {
                                    $app | Add-Member -MemberType NoteProperty -Name 'largeIcon' -Value $appInfo.largeIcon.value -Force
                                }
                            }
                            catch {
                                Write-Verbose "Failed to retrieve icon for application ID: $($app.id)"
                            }
                        }
                    }
                }

                # Initialize the flowchart
                Write-Verbose "Initializing mermaid flowchart with direction: $Direction"
                $mermaidFlowchart = Initialize-Flowchart -Direction $Direction

                switch ($GroupBy) {
                    "Assignments" {                             
                        
                        # Split each assignment into its own application object with the same properties as the original application object
                        Write-Verbose "Split assignments into individual application objects"

                        # Store the original apps temporarily
                        $originalApps = $allAppsAndAssignments.Clone()
                        
                        # Clear the original collection
                        $allAppsAndAssignments = @()

                        # Create individual app objects per assignment
                        foreach ($app in $originalApps) {
                            if ($app.assignments -and $app.assignments.Count -gt 0) {
                                foreach ($assignment in $app.assignments) {
                                    $appCopy = $app.PsObject.Copy()
                                    $appCopy.assignments = @($assignment)
                                    $allAppsAndAssignments += $appCopy
                                }
                            }
                            else {
                                # Keep apps without assignments as they are
                                $allAppsAndAssignments += $app
                            }
                        }

                        #Order the applications by displayName
                        Write-Verbose "Ordering applications by displayName"
                        $allAppsAndAssignments = $allAppsAndAssignments | Sort-Object -Property displayName
                        Write-Verbose "Created $($allAppsAndAssignments.Count) individual application assignment objects"
                    
                        # Get filter display names
                        Write-Verbose "Getting filter display names"
                        $filterDisplayNames = Get-FilterDisplayNames -Items $allAppsAndAssignments
                        Write-Verbose "Retrieved $($filterDisplayNames.Count) filter display names"
                        
                        # Group applications by typeName
                        Write-Verbose "Grouping applications by type name"
                        $GroupTypes = $allAppsAndAssignments | Group-Object { $_.'appTypeName' }
                        Write-Verbose "Found $($GroupTypes.Count) application type groups"

                        # Extract all unique group IDs from assignments
                        $uniqueGroupIds = @()
                        foreach ($app in $allAppsAndAssignments) {
                            foreach ($assignment in $app.assignments) {
                                if ($assignment.target.groupId -and $assignment.target.groupId -notin $uniqueGroupIds) {
                                    $uniqueGroupIds += $assignment.target.groupId
                                }
                            }
                        }                    
                        
                        # Pre-populate group cache with all unique group IDs
                        if ($uniqueGroupIds -and $uniqueGroupIds.Count -gt 0) {
                            Write-Verbose "Pre-populating group cache with $($uniqueGroupIds.Count) unique group IDs"
                            [void](Get-GroupDisplayName -groupId $uniqueGroupIds)
                        }
                        else {
                            Write-Verbose "No entra group IDs found to pre-populate in cache"
                        }

                        # Group applications by assignments
                        Write-Verbose "Grouping applications by assignments"
                        $GroupedByAssignments = @()
                        foreach ($GroupType in $GroupTypes) {
                            $assignmentGroups = $GroupType.Group | Group-Object {
                                (
                                    $_.assignments | Sort-Object -Property target | ForEach-Object { 
                                        Get-AssignmentTargetName -Target $_.target
                                    }                           
                                ) -join '|'
                            }
                            $GroupedByAssignments += [PSCustomObject]@{
                                OdataType        = $GroupType.Name
                                AssignmentGroups = $assignmentGroups
                            }
                        }
                        Write-Verbose "Created $($GroupedByAssignments.Count) assignment groups"
                        
                        # Group applications by Operating System
                        Write-Verbose "Grouping applications by operating system"
                        $AssignmentsGroupedByOS = @()
                        foreach ($assignmentGroup in $GroupedByAssignments) {
                            $osType = switch -Regex ($assignmentGroup.'OdataType') {
                                'android|graph.webApp' { 'Android' }
                                'ios' { 'iOS' }
                                'macos' { 'macOS' }
                                Default { 'Windows' }
                            }
                            # Only include the OS if it's in the specified OperatingSystem parameter
                            if ($osType -in $OperatingSystem) {
                                $osGroup = $AssignmentsGroupedByOS | Where-Object { $_.Name -eq $osType }
                                if (-not $osGroup) {
                                    $osGroup = [PSCustomObject]@{
                                        Name  = $osType
                                        Group = @()
                                    }
                                    $AssignmentsGroupedByOS += $osGroup
                                }
                                $osGroup.Group += $assignmentGroup
                            }
                        }
                        Write-Verbose "Created $($AssignmentsGroupedByOS.Count) OS groups"
                        
                        # Generate flowchart by OS > App Type > Assignment Group > App
                        Write-Verbose "Generating flowchart by OS > App Type > Assignment Group > App"
                        foreach ($OS in $AssignmentsGroupedByOS) {
                            Write-Verbose "Processing OS: $($OS.Name)"
                            $mermaidFlowchart += "`n" + "subgraph `"$($OS.Name)`""
                            
                            foreach ($assignmentsGroups in $OS.group) {
                                Write-Verbose "Processing application type: $($assignmentsGroups.OdataType)"
                                # Add ApplicationType to the flowchart
                                $mermaidFlowchart += "`n" + "subgraph `"$($assignmentsGroups.OdataType)`""
                                
                                foreach ($groupinfo in $assignmentsGroups.AssignmentGroups) {
                                    Write-Verbose "Processing assignment group: $($groupinfo.name)"
                                    $groupPrefix = ([array]::IndexOf($GroupedByAssignments, $assignmentsGroups)).ToString() + 
                                                  ([array]::IndexOf($assignmentsGroups.AssignmentGroups, $groupinfo)).ToString()
                                    
                                    $mermaidFlowchart += "`n" + "subgraph `"$($groupinfo.name)$groupPrefix`"[`"$($groupinfo.name)`"]"
                                    $mermaidFlowchart += "`n" + "direction $Direction"
                                    
                                    foreach ($appinfo in $groupinfo.Group) {
                                        Write-Verbose "Processing application: $($appinfo.displayName)"
                                        $appIdSuffix = "$groupPrefix-" + ([array]::IndexOf($groupinfo.Group, $appinfo)).ToString()
                                        
                                        if ($DisplayIcons) {
                                            if ($appinfo.largeIcon) {
                                                Write-Verbose "Adding application with icon"
                                                $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType Application -appId $appinfo.id -appName $appinfo.displayName -appimage $(Convert-Base64Image -base64String $appinfo.largeIcon) -ID $appIdSuffix)
                                            }
                                            else {
                                                # Default app icon
                                                Write-Verbose "Adding application with default icon"
                                                $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType Application -appId $appinfo.id -appName $appinfo.displayName -appImage "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" -ID $appIdSuffix)
                                            }
                                        }
                                        else {
                                            Write-Verbose "Adding application without icon"
                                            $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType Application -appId $appinfo.id -appName $appinfo.displayName -ID $appIdSuffix)
                                        }
                                        
                                        Write-Verbose "Adding application assignments to node"
                                        $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType AppGroupedByAssignments -assignmentsInfo $appinfo.assignments -appId $appinfo.id -ID $appIdSuffix)
                                        
                                    }
                                    $mermaidFlowchart += "`n" + "end"
                                }
                                $mermaidFlowchart += "`n" + "end"
                            }
                            $mermaidFlowchart += "`n" + "end"
                        }
                        
                        # Output the Mermaid.js flowchart
                        Write-Verbose "Returning completed flowchart for assignments grouping"
                        return $mermaidFlowchart
                    }
                    
                    "Name" {
                        Write-Verbose "Processing applications grouped by name"
                        #Order the applications by displayName
                        Write-Verbose "Ordering applications by displayName"
                        $allAppsAndAssignments = $allAppsAndAssignments | Sort-Object -Property displayName
                        # Extract all unique group IDs from assignments
                        $uniqueGroupIds = @()
                        foreach ($app in $allAppsAndAssignments) {
                            foreach ($assignment in $app.assignments) {
                                if ($assignment.target.groupId -and $assignment.target.groupId -notin $uniqueGroupIds) {
                                    $uniqueGroupIds += $assignment.target.groupId
                                }
                            }
                        }                    
                        # Pre-populate group cache with all unique group IDs
                        Write-Verbose "Pre-populating group cache with $($uniqueGroupIds.Count) unique group IDs"
                        [void](Get-GroupDisplayName -groupId $uniqueGroupIds)

                        # Get filter display names
                        Write-Verbose "Getting filter display names"
                        $filterDisplayNames = Get-FilterDisplayNames -Items $allAppsAndAssignments
                        Write-Verbose "Retrieved $($filterDisplayNames.Count) filter display names"
                        
                        # Group applications by typeName
                        Write-Verbose "Grouping applications by type name"
                        $GroupTypes = $allAppsAndAssignments | Group-Object { $_.'appTypeName' }
                        Write-Verbose "Found $($GroupTypes.Count) application type groups"
                        
                        # Group applications by OS
                        Write-Verbose "Grouping applications by operating system"
                        $GroupedByOS = @()
                        foreach ($assignmentGroup in $GroupTypes) {
                            $osType = switch -Regex ($assignmentGroup.Name) {
                                'android|graph.webApp' { 'Android' }
                                'ios' { 'iOS' }
                                'macos' { 'macOS' }
                                Default { 'Windows' }
                            }
                            
                            # Only include the OS if it's in the specified OperatingSystem parameter
                            if ($osType -in $OperatingSystem) {
                                $osGroup = $GroupedByOS | Where-Object { $_.Name -eq $osType }
                                if (-not $osGroup) {
                                    $osGroup = [PSCustomObject]@{
                                        Name  = $osType
                                        Group = @()
                                    }
                                    $GroupedByOS += $osGroup
                                }
                                $osGroup.Group += $assignmentGroup
                            }
                        }
                        Write-Verbose "Created $($GroupedByOS.Count) OS groups"
                        
                        # Generate flowchart by OS > App Type > App
                        Write-Verbose "Generating flowchart by OS > App Type > App"
                        foreach ($OS in $GroupedByOS) {
                            Write-Verbose "Processing OS: $($OS.Name)"
                            $mermaidFlowchart += "`n" + "subgraph `"$($OS.Name)`""
                            
                            foreach ($AppType in $OS.group) {
                                Write-Verbose "Processing application type: $($AppType.Name)"
                                
                                $mermaidFlowchart += "`n" + "subgraph `"$($AppType.Name)`""
                                $mermaidFlowchart += "`n" + "direction $Direction"
                                
                                foreach ($appinfo in $AppType.Group) {
                                    Write-Verbose "Processing application: $($appinfo.displayName)"
                                    $appIdSuffix = "$(([array]::IndexOf($GroupedByOS, $OS)).ToString() + "-" +
                                                    ([array]::IndexOf($OS.group, $AppType)).ToString() + "-" +
                                                    ([array]::IndexOf($AppType.Group, $appinfo)).ToString())"

                                    
                                    if ($DisplayIcons) {
                                        if ($appinfo.largeIcon) {
                                            Write-Verbose "Adding application with icon"
                                            $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType Application -appId $appinfo.id -appName $appinfo.displayName -appimage $(Convert-Base64Image -base64String $appinfo.largeIcon) -ID $appIdSuffix)
                                        }
                                        else {
                                            # Default app icon
                                            Write-Verbose "Adding application with default icon"
                                            $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType Application -appId $appinfo.id -appName $appinfo.displayName -appImage "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" -ID $appIdSuffix)
                                        }
                                    }
                                    else {
                                        Write-Verbose "Adding application without icon"
                                        $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType Application -appId $appinfo.id -appName $appinfo.displayName -ID $appIdSuffix)
                                    }
                                    
                                    Write-Verbose "Adding application assignments to node"
                                    $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType AppGroupedByApplications -assignmentsInfo $appinfo.assignments -appId $appinfo.id -ID $appIdSuffix)
                                    
                                }
                                $mermaidFlowchart += "`n" + "end"
                            }
                            $mermaidFlowchart += "`n" + "end"
                        }                        
                        Write-Verbose "Returning completed flowchart for application name grouping"
                        return $mermaidFlowchart
                    }
                }
            }
            
            "Profiles" {
                Write-Verbose "Processing profiles type"
                # Dictionary to map profile odata types to friendly names
                $profileTypeMap = @{
                    '#microsoft.graph.androidCustomConfiguration'                                     = "Custom"
                    '#microsoft.graph.androidDeviceOwnerDerivedCredentialAuthenticationConfiguration' = "Derived credentials"
                    '#microsoft.graph.androidDeviceOwnerEnterpriseWiFiConfiguration'                  = "Wi-Fi"
                    '#microsoft.graph.androidDeviceOwnerGeneralDeviceConfiguration'                   = "Device restrictions"
                    '#microsoft.graph.androidDeviceOwnerImportedPFXCertificateProfile'                = "PFX certificate"
                    '#microsoft.graph.androidDeviceOwnerPkcsCertificateProfile'                       = "PKCS certificate"
                    '#microsoft.graph.androidDeviceOwnerScepCertificateProfile'                       = "SCEP certificate"
                    '#microsoft.graph.androidDeviceOwnerTrustedRootCertificate'                       = "Trusted certificate"
                    '#microsoft.graph.androidDeviceOwnerVpnConfiguration'                             = "VPN"
                    '#microsoft.graph.androidDeviceOwnerWiFiConfiguration'                            = "Wi-Fi"
                    '#microsoft.graph.androidEasEmailProfileConfiguration'                            = "Email"
                    '#microsoft.graph.androidEnterpriseWiFiConfiguration'                             = "Wi-Fi"
                    '#microsoft.graph.androidForWorkCustomConfiguration'                              = "Custom"
                    '#microsoft.graph.androidForWorkGmailEasConfiguration'                            = "Email"
                    '#microsoft.graph.androidForWorkGeneralDeviceConfiguration'                       = "Device restrictions"
                    '#microsoft.graph.androidForWorkImportedPFXCertificateProfile'                    = "PFX certificate"
                    '#microsoft.graph.androidForWorkNineWorkEasConfiguration'                         = "Email"
                    '#microsoft.graph.androidForWorkPkcsCertificateProfile'                           = "PKCS certificate"
                    '#microsoft.graph.androidForWorkScepCertificateProfile'                           = "SCEP certificate"
                    '#microsoft.graph.androidForWorkTrustedRootCertificate'                           = "Trusted certificate"
                    '#microsoft.graph.androidForWorkVpnConfiguration'                                 = "VPN"
                    '#microsoft.graph.androidForWorkWiFiConfiguration'                                = "Wi-Fi"
                    '#microsoft.graph.androidGeneralDeviceConfiguration'                              = "Device restrictions"
                    '#microsoft.graph.androidImportedPFXCertificateProfile'                           = "PFX certificate"
                    '#microsoft.graph.androidManagedStoreAppConfiguration'                            = "App configuration"
                    '#microsoft.graph.androidOmaCpConfiguration'                                      = "OMA-CP"
                    '#microsoft.graph.androidPkcsCertificateProfile'                                  = "PKCS certificate"
                    '#microsoft.graph.androidScepCertificateProfile'                                  = "SCEP certificate"
                    '#microsoft.graph.androidTrustedRootCertificate'                                  = "Trusted certificate"
                    '#microsoft.graph.androidVpnConfiguration'                                        = "VPN"
                    '#microsoft.graph.androidWiFiConfiguration'                                       = "Wi-Fi"
                    '#microsoft.graph.androidWorkProfileCustomConfiguration'                          = "Custom"
                    '#microsoft.graph.androidWorkProfileEnterpriseWiFiConfiguration'                  = "Wi-Fi"
                    '#microsoft.graph.androidWorkProfileGeneralDeviceConfiguration'                   = "Device restrictions"
                    '#microsoft.graph.androidWorkProfileGmailEasConfiguration'                        = "Email"
                    '#microsoft.graph.androidWorkProfileNineWorkEasConfiguration'                     = "Email"
                    '#microsoft.graph.androidWorkProfilePkcsCertificateProfile'                       = "PKCS certificate"
                    '#microsoft.graph.androidWorkProfileScepCertificateProfile'                       = "SCEP certificate"
                    '#microsoft.graph.androidWorkProfileTrustedRootCertificate'                       = "Trusted certificate"
                    '#microsoft.graph.androidWorkProfileVpnConfiguration'                             = "VPN"
                    '#microsoft.graph.androidWorkProfileWiFiConfiguration'                            = "Wi-Fi"
                    '#microsoft.graph.aospDeviceOwnerDeviceConfiguration'                             = "Device restrictions"
                    '#microsoft.graph.aospDeviceOwnerEnterpriseWiFiConfiguration'                     = "Wi-Fi"
                    '#microsoft.graph.aospDeviceOwnerPkcsCertificateProfile'                          = "PKCS certificate"
                    '#microsoft.graph.aospDeviceOwnerScepCertificateProfile'                          = "SCEP certificate"
                    '#microsoft.graph.aospDeviceOwnerTrustedRootCertificate'                          = "Trusted certificate"
                    '#microsoft.graph.aospDeviceOwnerWiFiConfiguration'                               = "Wi-Fi"
                    '#microsoft.graph.editionUpgradeConfiguration'                                    = "Edition upgrade"
                    '#microsoft.graph.hardwareConfigurations'                                         = "Hardware configurations"
                    '#microsoft.graph.iosCustomConfiguration'                                         = "Custom"
                    '#microsoft.graph.iosDerivedCredentialAuthenticationConfiguration'                = "Derived credentials"
                    '#microsoft.graph.iosDeviceFeaturesConfiguration'                                 = "Device features"
                    '#microsoft.graph.iosEasEmailProfileConfiguration'                                = "Email"
                    '#microsoft.graph.iosEduDeviceConfiguration'                                      = "Education"
                    '#microsoft.graph.iosEnterpriseWiFiConfiguration'                                 = "Wi-Fi"
                    '#microsoft.graph.iosExpeditedCheckinConfiguration'                               = "Expedited check-in"
                    '#microsoft.graph.iosGeneralDeviceConfiguration'                                  = "Device restrictions"
                    '#microsoft.graph.iosikEv2VpnConfiguration'                                       = "IKEv2 VPN"
                    '#microsoft.graph.iosImportedPFXCertificateProfile'                               = "PFX certificate"
                    '#microsoft.graph.iosPkcsCertificateProfile'                                      = "PKCS certificate"
                    '#microsoft.graph.iosPresetsProfile'                                              = "Presets"
                    '#microsoft.graph.iosScepCertificateProfile'                                      = "SCEP certificate"
                    '#microsoft.graph.iosTrustedRootCertificate'                                      = "Trusted certificate"
                    '#microsoft.graph.iosUpdateConfiguration'                                         = "Update Configuration"
                    '#microsoft.graph.iosVpnConfiguration'                                            = "VPN"
                    '#microsoft.graph.iosWiFiConfiguration'                                           = "Wi-Fi"
                    '#microsoft.graph.macOSCustomAppConfiguration'                                    = "Preference file"
                    '#microsoft.graph.macOSCustomConfiguration'                                       = "Custom"
                    '#microsoft.graph.macOSDeviceFeaturesConfiguration'                               = "Device features"
                    '#microsoft.graph.macOSEndpointProtectionConfiguration'                           = "Endpoint protection"
                    '#microsoft.graph.macOSEnterpriseWiFiConfiguration'                               = "Wi-Fi"
                    '#microsoft.graph.macOSExtensionsConfiguration'                                   = "Extensions"
                    '#microsoft.graph.macOSGeneralDeviceConfiguration'                                = "Device restrictions"
                    '#microsoft.graph.macOSImportedPFXCertificateProfile'                             = "PFX certificate"
                    '#microsoft.graph.macOSPkcsCertificateProfile'                                    = "PKCS certificate"
                    '#microsoft.graph.macOSScepCertificateProfile'                                    = "SCEP certificate"
                    '#microsoft.graph.macOSSoftwareUpdateConfiguration'                               = "Update Configuration"
                    '#microsoft.graph.macOSTrustedRootCertificate'                                    = "Trusted certificate"
                    '#microsoft.graph.macOSVpnConfiguration'                                          = "VPN"
                    '#microsoft.graph.macOSWiFiConfiguration'                                         = "Wi-Fi"
                    '#microsoft.graph.macOSWiredNetworkConfiguration'                                 = "Wired network"
                    '#microsoft.graph.sharedPCConfiguration'                                          = "Shared multi-user device"
                    '#microsoft.graph.unsupportedDeviceConfiguration'                                 = "Unsupported"
                    '#microsoft.graph.windows10AdministrativeTemplate'                                = "Administrative templates"
                    '#microsoft.graph.windows10CustomConfiguration'                                   = "Custom"
                    '#microsoft.graph.windows10DeviceFirmwareConfigurationInterface'                  = "Device firmware"
                    '#microsoft.graph.windows10EasEmailProfileConfiguration'                          = "Email"
                    '#microsoft.graph.windows10EndpointProtectionConfiguration'                       = "Endpoint protection"
                    '#microsoft.graph.windows10GeneralConfiguration'                                  = "Device restrictions"
                    '#microsoft.graph.windows10ImportedPFXCertificateProfile'                         = "PFX certificate"
                    '#microsoft.graph.windows10InformationProtectionConfiguration'                    = "Information protection"
                    '#microsoft.graph.windows10NetworkBoundaryConfiguration'                          = "Network boundary"
                    '#microsoft.graph.windows10PkcsCertificateProfile'                                = "PKCS certificate"
                    '#microsoft.graph.windows10PolicyOverrideConfiguration'                           = "Policy override"
                    '#microsoft.graph.windows10SecureAssessmentConfiguration'                         = "Secure assessment (Education)"
                    '#microsoft.graph.windows10TeamGeneralConfiguration'                              = "Teams device restrictions"
                    '#microsoft.graph.windows10VpnConfiguration'                                      = "VPN"
                    '#microsoft.graph.windows10XSCEPCertificateProfile'                               = "SCEP certificate"
                    '#microsoft.graph.windows10XTrustedRootCertificate'                               = "Trusted certificate"
                    '#microsoft.graph.windows10XVpnConfiguration'                                     = "VPN"
                    '#microsoft.graph.windows10XWifiConfiguration'                                    = "Wi-Fi"
                    '#microsoft.graph.windows81GeneralConfiguration'                                  = "Device restrictions"
                    '#microsoft.graph.windows81SCEPCertificateProfile'                                = "SCEP certificate"
                    '#microsoft.graph.windows81TrustedRootCertificate'                                = "Trusted certificate"
                    '#microsoft.graph.windows81VpnConfiguration'                                      = "VPN"
                    '#microsoft.graph.windows81WifiImportConfiguration'                               = "Wi-Fi import"
                    '#microsoft.graph.windowsDefenderAdvancedThreatProtectionConfiguration'           = "Microsoft Defender for Endpoint"
                    '#microsoft.graph.windowsDeliveryOptimizationConfiguration'                       = "Delivery optimization"
                    '#microsoft.graph.windowsDomainJoinConfiguration'                                 = "Domain join"
                    '#microsoft.graph.windowsHealthMonitoringConfiguration'                           = "Windows health monitoring"
                    '#microsoft.graph.windowsIdentityProtectionConfiguration'                         = "Identity protection"
                    '#microsoft.graph.windowsKioskConfiguration'                                      = "Kiosk"
                    '#microsoft.graph.windowsPhone81CustomConfiguration'                              = "Custom"
                    '#microsoft.graph.windowsPhone81GeneralConfiguration'                             = "Device restrictions"
                    '#microsoft.graph.windowsPhone81ImportedPFXCertificateProfile'                    = "PFX certificate"
                    '#microsoft.graph.windowsPhone81SCEPCertificateProfile'                           = "SCEP certificate"
                    '#microsoft.graph.windowsPhone81TrustedRootCertificate'                           = "Trusted certificate"
                    '#microsoft.graph.windowsPhone81VpnConfiguration'                                 = "VPN"
                    '#microsoft.graph.windowsPhoneEASEmailProfileConfiguration'                       = "Email"
                    '#microsoft.graph.windowsUpdateForBusinessConfiguration'                          = "Update rings for Windows updates"
                    '#microsoft.graph.windowsWifiConfiguration'                                       = "Wi-Fi"
                    '#microsoft.graph.windowsWifiEnterpriseEAPConfiguration'                          = "Wi-Fi"
                    '#microsoft.graph.windowsWiredNetworkConfiguration'                               = "Wired network"
                }
                Write-Verbose "Profile type mapping dictionary created"
                
                # Helper function to determine OS from profile type
                Write-Verbose "Defining Get-ProfileOS helper function"
                function Get-ProfileOS {
                    param (
                        [string]$OdataType,
                        [string]$Platform = $null
                    )
                    
                    if ($Platform) {
                        switch -Regex ($Platform) {
                            'android' { return 'Android' }
                            'ios' { return 'iOS' }
                            'macos' { return 'macOS' }
                            'windows10' { return 'Windows' }
                            Default { return $Platform }
                        }
                    }
                    
                    if (-not $OdataType) {
                        Write-Warning "OdataType is required if Platform is not specified."
                        return $null
                    }
                    
                    switch -Regex ($OdataType) {
                        '\.android' { return 'Android' }
                        '\.aosp' { return 'Android' }
                        '\.ios' { return 'iOS' }
                        '\.macos' { return 'macOS' }
                        '\.windows' { return 'Windows' }
                        '\.sharedPC' { return 'Windows' }
                        '\.editionUpgrade' { return 'Windows' }
                        Default { return 'Other' }
                    }
                }
                Write-Verbose "Get-ProfileOS helper function defined"
                                
                # Helper function to get profile type display name
                Write-Verbose "Defining Get-ProfileTypeDisplayName helper function"
                function Get-ProfileTypeDisplayName {
                    param (
                        [string]$OdataType,
                        [string]$TemplateDisplayName = $null
                    )
                    
                    if ($TemplateDisplayName) {
                        return $TemplateDisplayName
                    }
                    
                    if ($profileTypeMap.ContainsKey($OdataType)) {
                        return $profileTypeMap[$OdataType]
                    }
                    
                    return $OdataType
                }
                Write-Verbose "Get-ProfileTypeDisplayName helper function defined"
                
                # Get device configuration profiles expanding its assignments
                Write-Verbose "Retrieving device configuration profiles"
                $allConfigProfilesAndAssignments = Get-GraphDataWithPagination -Uri "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?`$expand=Assignments"
                Write-Verbose "Retrieved $($allConfigProfilesAndAssignments.Count) configuration profiles"

                # Remove any configuration profiles which do not have assignments
                Write-Verbose "Filtering out configuration profiles without assignments"
                $allConfigProfilesAndAssignments = $allConfigProfilesAndAssignments | Where-Object { $_.Assignments.Count -gt 0 }
                
                # Get configuration policies (Settings Catalog) with assignments
                Write-Verbose "Retrieving configuration policies (Settings Catalog)"
                $allConfigPoliciesAndAssignments = Get-GraphDataWithPagination -Uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies?`$filter=(isAssigned eq true)&`$expand=Assignments"
                Write-Verbose "Retrieved $($allConfigPoliciesAndAssignments.Count) configuration policies"
                
                # Initialize the flowchart
                Write-Verbose "Initializing mermaid flowchart with direction: $Direction"
                $mermaidFlowchart = Initialize-Flowchart -Direction $Direction
                
                # Process all profiles based on grouping
                switch ($GroupBy) {
                    "Assignments" {
                        Write-Verbose "Processing profiles grouped by assignments"

                        # Split each assignment into its own configuration profile object
                        Write-Verbose "Split assignments into individual configuration profile objects"

                        # Store the original configuration profile temporarily
                        $originalconfigurationprofiles = $allConfigProfilesAndAssignments.Clone()

                        # Clear the original collection
                        $allConfigProfilesAndAssignments = @()

                        # Create individual configuration profile objects per assignment while standardizing the output to later merge
                        foreach ($cfgpr in $originalconfigurationprofiles) {
                            if ($cfgpr.assignments) {
                                foreach ($assignment in $cfgpr.assignments) {
                                    # Create a copy of the configuration profile object with the assignment
                                    $profileCopy = [PSCustomObject]@{
                                        displayName = $cfgpr.displayName
                                        ProfileType = Get-ProfileTypeDisplayName -OdataType $cfgpr.'@odata.type'
                                        description = $cfgpr.description
                                        assignments = @($assignment)
                                        OS          = Get-ProfileOS -OdataType $cfgpr.'@odata.type'
                                        ID          = $cfgpr.id                                        
                                    }
                                    $allConfigProfilesAndAssignments += $profileCopy
                                }
                            }
                        }
                        Write-Verbose "Retrieved $($allConfigProfilesAndAssignments.Count) configuration profile assignments"

                        # Split each assignment into its own configuration policiy object
                        Write-Verbose "Split assignments into individual configuration policiy objects"

                        # Store the original configuration policy temporarily
                        $originalconfigurationpolicy = $allConfigPoliciesAndAssignments.Clone()

                        # Clear the original collection
                        $allConfigPoliciesAndAssignments = @()

                        # Create individual configuration policy objects per assignment while standardizing the output to later merge
                        foreach ($cfgpol in $originalconfigurationpolicy) {
                            if ($cfgpol.assignments) {
                                foreach ($assignment in $cfgpol.assignments) {
                                    $policyCopy = [PSCustomObject]@{
                                        displayName = $cfgpol.name
                                        ProfileType = Get-ProfileTypeDisplayName -TemplateDisplayName $cfgpol.templateReference.templateDisplayName -OdataType "Settings Catalog"
                                        description = $cfgpol.description
                                        assignments = @($assignment)
                                        OS          = Get-ProfileOS -Platform $cfgpol.platforms
                                        ID          = $cfgpol.id                                        
                                    }
                                    $allConfigPoliciesAndAssignments += $policyCopy
                                }
                            }
                        }
                        Write-Verbose "Retrieved $($allConfigPoliciesAndAssignments.Count) configuration policy assignments"
                        
                        # Combine all profiles and policies
                        Write-Verbose "Combining all policies and profiles"
                        $allPolicies = @() + $allConfigProfilesAndAssignments + $allConfigPoliciesAndAssignments
                        Write-Verbose "Total combined policies: $($allPolicies.Count)"
                        
                        # Get filter display names
                        Write-Verbose "Getting filter display names"
                        $filterDisplayNames = Get-FilterDisplayNames -Items $allPolicies
                        Write-Verbose "Retrieved $($filterDisplayNames.Count) filter display names"
                        
                        # Filter by allPolicies by PolicyType, if specified
                        if ($PSBoundParameters.ContainsKey('PolicyType')) {
                            Write-Verbose "Filtering policies by specified PolicyType: $($PSBoundParameters.PolicyType -join ', ')"
                            $allPolicies = $allPolicies | Where-Object { $_.ProfileType -in $PSBoundParameters.PolicyType }
                            Write-Verbose "Filtered to $($allPolicies.Count) policies"
                        }
                        
                        # Group all policies by type
                        Write-Verbose "Grouping policies by profile type"
                        $GroupTypes = $AllPolicies | Group-Object { $_.ProfileType }
                        Write-Verbose "Found $($GroupTypes.Count) policy type groups"

                        # Extract all unique group IDs from assignments
                        $uniqueGroupIds = @()
                        foreach ($pol in $allPolicies) {
                            foreach ($assignment in $pol.assignments) {
                                if ($assignment.target.groupId -and $assignment.target.groupId -notin $uniqueGroupIds) {
                                    $uniqueGroupIds += $assignment.target.groupId
                                }
                            }
                        }                    
                        # Pre-populate group cache with all unique group IDs
                        Write-Verbose "Pre-populating group cache with $($uniqueGroupIds.Count) unique group IDs"
                        [void](Get-GroupDisplayName -groupId $uniqueGroupIds)   

                        # Group policies by assignments
                        Write-Verbose "Grouping policies by assignments"
                        $groupedByAssignments = @()
                        foreach ($groupType in $groupTypes) {
                            $assignmentGroups = $groupType.Group | Group-Object {
                                (
                                    $_.assignments | Sort-Object -Property target | ForEach-Object { 
                                        Get-AssignmentTargetName -Target $_.target
                                    }                           
                                ) -join '|'
                            }                          
                            $groupedByAssignments += [PSCustomObject]@{
                                ProfileType      = $groupType.Name
                                AssignmentGroups = $assignmentGroups
                            }
                        }
                        Write-Verbose "Created $($groupedByAssignments.Count) assignment groups"
                        
                        # Group policies by Operating System
                        Write-Verbose "Grouping policies by operating system"
                        $assignmentsGroupedByOS = @()
                        foreach ($item in $groupedByAssignments) {
                            foreach ($groupItem in $item.AssignmentGroups) {
                                $osGroups = $groupItem.Group | Group-Object OS
                                
                                foreach ($osGroup in $osGroups) {
                                    # Only include the OS if it's in the specified OperatingSystem parameter
                                    if ($osGroup.Name -in $OperatingSystem) {
                                        $foundOsGroup = $assignmentsGroupedByOS | Where-Object { $_.Name -eq $osGroup.Name }
                                        if (-not $foundOsGroup) {
                                            $foundOsGroup = [PSCustomObject]@{
                                                Name  = $osGroup.Name
                                                Group = @()
                                            }
                                            $assignmentsGroupedByOS += $foundOsGroup
                                        }
                                        
                                        $foundProfileType = $foundOsGroup.Group | Where-Object { $_.ProfileType -eq $item.ProfileType }
                                        if (-not $foundProfileType) {
                                            $foundProfileType = [PSCustomObject]@{
                                                ProfileType      = $item.ProfileType
                                                AssignmentGroups = @()
                                            }
                                            $foundOsGroup.Group += $foundProfileType
                                        }
                                        
                                        $foundProfileType.AssignmentGroups += [PSCustomObject]@{
                                            name  = $groupItem.name
                                            Group = $osGroup.Group
                                        }
                                    }
                                }
                            }
                        }
                        Write-Verbose "Created $($assignmentsGroupedByOS.Count) OS groups"
                        
                        # Generate flowchart by OS > Profile Type > Assignment Group > Profile
                        Write-Verbose "Generating flowchart by OS > Profile Type > Assignment Group > Profile"
                        foreach ($OS in $assignmentsGroupedByOS) {
                            Write-Verbose "Processing OS: $($OS.Name)"
                            $mermaidFlowchart += "`n" + "subgraph `"$($OS.Name)`""
                            
                            foreach ($ProfileType in $OS.group) {
                                Write-Verbose "Processing profile type: $($ProfileType.ProfileType)"
                                $mermaidFlowchart += "`n" + "subgraph `"$($ProfileType.ProfileType) $($OS.Name)`"[`"$($ProfileType.ProfileType)`"]"
                                
                                foreach ($groupinfo in $ProfileType.AssignmentGroups) {
                                    Write-Verbose "Processing assignment group: $($groupinfo.name)"
                                    $groupPrefix = "$(([array]::IndexOf($assignmentsGroupedByOS, $OS)).ToString() + "-" +
                                                    ([array]::IndexOf($OS.group, $ProfileType)).ToString() + "-" +
                                                    ([array]::IndexOf($ProfileType.AssignmentGroups, $groupinfo)).ToString())"

                                    
                                    $mermaidFlowchart += "`n" + "subgraph `"$($groupinfo.name)$groupPrefix-0`"[`"$($groupinfo.name)`"]"
                                    $mermaidFlowchart += "`n" + "direction $Direction"
                                    
                                    foreach ($profileinfo in $groupinfo.Group) {
                                        Write-Verbose "Processing profile: $($profileinfo.displayName)"
                                        $profileIdSuffix = "$groupPrefix-$(([array]::IndexOf($groupinfo.Group, $profileinfo)).ToString())"
                                        
                                        $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType Profile -appId $profileinfo.id -appName $profileinfo.displayName -ID $profileIdSuffix)
                                        $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType ProfileGroupedByAssignments -assignmentsInfo $profileinfo.assignments -appId $profileinfo.id -ID $profileIdSuffix)
                                        
                                    }
                                    
                                    $mermaidFlowchart += "`n" + "end"
                                }
                                
                                $mermaidFlowchart += "`n" + "end"
                            }
                            
                            $mermaidFlowchart += "`n" + "end"
                        }
                        
                        Write-Verbose "Returning completed flowchart for profile assignments grouping"
                        return $mermaidFlowchart
                    }
                    
                    "Name" {
                        Write-Verbose "Processing profiles grouped by name"
                        # Get assignments for all configuration profiles

                        # Standardize the output of $allConfigProfilesAndAssignments to later merge
                        Write-Verbose "Standardizing the output of $allConfigProfilesAndAssignments to later merge"

                        # Store the original configuration profile temporarily
                        $originalconfigurationprofiles = $allConfigProfilesAndAssignments.Clone()

                        # Clear the original collection
                        $allConfigProfilesAndAssignments = @()
                        foreach ($cfgpr in $originalconfigurationprofiles) {
                            # Create a copy of the configuration profile object with the assignment
                            $profileCopy = [PSCustomObject]@{
                                displayName = $cfgpr.displayName
                                ProfileType = Get-ProfileTypeDisplayName -OdataType $cfgpr.'@odata.type'
                                description = $cfgpr.description
                                assignments = $cfgpr.assignments
                                OS          = Get-ProfileOS -OdataType $cfgpr.'@odata.type'
                                ID          = $cfgpr.id                                        
                            }
                            $allConfigProfilesAndAssignments += $profileCopy
                        }
                        Write-Verbose "Retrieved $($allConfigProfilesAndAssignments.Count) configuration profiles with assignments"                     

                        # Standardize the output of $allConfigPoliciesAndAssignments to later merge
                        Write-Verbose "Standardizing the output of $allConfigPoliciesAndAssignments to later merge"

                        # Store the original configuration policy temporarily
                        $originalconfigurationpolicy = $allConfigPoliciesAndAssignments.Clone()

                        # Clear the original collection
                        $allConfigPoliciesAndAssignments = @()
                        foreach ($cfgpol in $originalconfigurationpolicy) {
                            $policyCopy = [PSCustomObject]@{
                                displayName = $cfgpol.name
                                ProfileType = Get-ProfileTypeDisplayName -TemplateDisplayName $cfgpol.templateReference.templateDisplayName -OdataType "Settings Catalog"
                                description = $cfgpol.description
                                assignments = $cfgpol.assignments
                                OS          = Get-ProfileOS -Platform $cfgpol.platforms
                                ID          = $cfgpol.id                                        
                            }
                            $allConfigPoliciesAndAssignments += $policyCopy
                        }
                        Write-Verbose "Retrieved $($allConfigPoliciesAndAssignments.Count) configuration policy assignments"                        
                        
                        # Combine all profiles and policies
                        Write-Verbose "Combining all policies and profiles"
                        $allPolicies = @() + $allConfigProfilesAndAssignments + $allConfigPoliciesAndAssignments
                        Write-Verbose "Total combined policies: $($allPolicies.Count)"
                        
                        # Get filter display names
                        Write-Verbose "Getting filter display names"
                        $filterDisplayNames = Get-FilterDisplayNames -Items $allPolicies
                        Write-Verbose "Retrieved $($filterDisplayNames.Count) filter display names"
                        
                        # Extract all unique group IDs from assignments
                        $uniqueGroupIds = @()
                        foreach ($pol in $allPolicies) {
                            foreach ($assignment in $pol.assignments) {
                                if ($assignment.target.groupId -and $assignment.target.groupId -notin $uniqueGroupIds) {
                                    $uniqueGroupIds += $assignment.target.groupId
                                }
                            }
                        }                    
                        # Pre-populate group cache with all unique group IDs
                        Write-Verbose "Pre-populating group cache with $($uniqueGroupIds.Count) unique group IDs"
                        [void](Get-GroupDisplayName -groupId $uniqueGroupIds)  

                        # Filter by allPolicies by PolicyType, if specified
                        if ($PSBoundParameters.ContainsKey('PolicyType')) {
                            Write-Verbose "Filtering policies by specified PolicyType: $($PSBoundParameters.PolicyType -join ', ')"
                            $allPolicies = $allPolicies | Where-Object { $_.ProfileType -in $PSBoundParameters.PolicyType }
                            Write-Verbose "Filtered to $($allPolicies.Count) policies"
                        }
                        
                        # Group all policies by type
                        Write-Verbose "Grouping policies by profile type"
                        $GroupTypes = $AllPolicies | Group-Object { $_.ProfileType }
                        Write-Verbose "Found $($GroupTypes.Count) policy type groups"
                        
                        # Group all policies by OS
                        Write-Verbose "Grouping policies by operating system"
                        $profileGroupedByOS = @()
                        foreach ($profileTypeGroup in $groupTypes) {
                            foreach ($confprofile in $profileTypeGroup.Group) {
                                # Only include the OS if it's in the specified OperatingSystem parameter
                                if ($confprofile.OS -in $OperatingSystem) {
                                    $foundOsGroup = $profileGroupedByOS | Where-Object { $_.Name -eq $confprofile.OS }
                                    if (-not $foundOsGroup) {
                                        $foundOsGroup = [PSCustomObject]@{
                                            Name  = $confprofile.OS
                                            Group = @()
                                        }
                                        $profileGroupedByOS += $foundOsGroup
                                    }
                                    
                                    $foundProfileType = $foundOsGroup.Group | Where-Object { $_.ProfileType -eq $profileTypeGroup.Name }
                                    if (-not $foundProfileType) {
                                        $foundProfileType = [PSCustomObject]@{
                                            ProfileType      = $profileTypeGroup.Name
                                            AssignmentGroups = @()
                                        }
                                        $foundOsGroup.Group += $foundProfileType
                                    }
                                    
                                    $foundProfileType.AssignmentGroups += $confprofile
                                }
                            }
                        }
                        Write-Verbose "Created $($profileGroupedByOS.Count) OS groups"
                        
                        # Generate flowchart by OS > Profile Type > Profile
                        Write-Verbose "Generating flowchart by OS > Profile Type > Profile"
                        foreach ($OS in $profileGroupedByOS) {
                            Write-Verbose "Processing OS: $($OS.Name)"
                            $mermaidFlowchart += "`n" + "subgraph `"$($OS.Name)`""
                            
                            foreach ($ProfileType in $OS.group) {
                                Write-Verbose "Processing profile type: $($ProfileType.ProfileType)"
                                $mermaidFlowchart += "`n" + "subgraph `"$($ProfileType.ProfileType) $($OS.Name)`"[`"$($ProfileType.ProfileType)`"]"
                                $mermaidFlowchart += "`n" + "direction $Direction"
                                
                                foreach ($profileinfo in $ProfileType.AssignmentGroups) {
                                    Write-Verbose "Processing profile: $($profileinfo.displayName)"
                                    $profileIdSuffix = "$(([array]::IndexOf($profileGroupedByOS, $OS)).ToString() + "-" +
                                                       ([array]::IndexOf($OS.group, $ProfileType)).ToString() + "-" +
                                                       ([array]::IndexOf($ProfileType.AssignmentGroups, $profileinfo)).ToString())"

                                    
                                    $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType Profile -appId $profileinfo.id -appName $profileinfo.displayName -ID $profileIdSuffix)
                                    $mermaidFlowchart += "`n" + (New-MermaidNode -NodeType ProfileGroupedByProfile -assignmentsInfo $profileinfo.assignments -appId $profileinfo.id -ID $profileIdSuffix)
                                    
                                }
                                
                                $mermaidFlowchart += "`n" + "end"
                            }
                            
                            $mermaidFlowchart += "`n" + "end"
                        }
                        
                        Write-Verbose "Returning completed flowchart for profile name grouping"
                        return $mermaidFlowchart
                    }
                }
            }
        }
    }
}