Private/New-MermaidNode.ps1

<#
.SYNOPSIS
Creates a Mermaid diagram node for an application or its assignments.

.DESCRIPTION
The New-MermaidNode function generates a Mermaid diagram node based on the specified node type.
It supports creating nodes for applications and their assignments, including handling images and assignment filters.

.PARAMETER NodeType
Specifies the type of node to create. Valid values are "Application" and "Assignments".

.PARAMETER appId
The unique identifier (GUID) of the application.

.PARAMETER appName
The name of the application. This parameter is mandatory when NodeType is "Application".

.PARAMETER appImage
The base64-encoded image of the application. This parameter is optional when NodeType is "Application".

.PARAMETER assignmentsInfo
An array of assignment information. This parameter is mandatory when NodeType is "Assignments".

.PARAMETER ID
The id number to be used in the diagram.

.RETURNS
A string representing the Mermaid diagram node.

.EXAMPLE
$node = New-MermaidNode -NodeType "Application" -appId (New-Guid) -appName "MyApp" -appImage $base64Image -AssignmentNumber 1
Creates a Mermaid diagram node for an application with an image.

.EXAMPLE
$assignments = @(
    @{ intent = "required"; target = @{ 'groupId' = "acacacac-9df4-4c7d-9d50-4ef0226f57a9"; '@odata.type' = "#microsoft.graph.groupAssignmentTarget" } }
)
$node = New-MermaidNode -NodeType "Assignments" -appId (New-Guid) -assignmentsInfo $assignments -AssignmentNumber 1
Creates a Mermaid diagram node for assignments.

.NOTES
This function requires the Microsoft Graph PowerShell SDK to be installed and authenticated.
#>

function New-MermaidNode {
    param (
        [ValidateSet("Application", "Profile", "AppGroupedByAssignments", "AppGroupedByApplications", "ProfileGroupedByAssignments", "ProfileGroupedByProfile")][string]$NodeType,
        [Parameter(Mandatory = $true)][guid]$appId,
        [Parameter(Mandatory = $true)]$ID,
        [Parameter(Mandatory = $true, ParameterSetName = "ApplicationSet")][array]$appName,
        [Parameter(Mandatory = $false, ParameterSetName = "ApplicationSet")][string]$appImage,
        [Parameter(Mandatory = $true, ParameterSetName = "AssignmentsSet")][array]$assignmentsInfo
    )

    # Create a cache for group display names if it doesn't exist
    if (-not (Get-Variable -Name groupCache -Scope Script -ErrorAction SilentlyContinue)) {
        $script:groupCache = @{}
    }
    
    # Helper function to get group display name with caching
    function Get-GroupDisplayName {
        param (
            [string]$groupId
        )
        
        if (-not $script:groupCache.ContainsKey($groupId)) {
            try {
                $groupInfo = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/directoryObjects/$groupId").displayName
                $script:groupCache[$groupId] = @{
                    DisplayName = $groupInfo.replace(".", "") # Remove periods from display name
                    Shortname   = $groupInfo  # Store the unmodified name
                }
            }
            catch {
                $script:groupCache[$groupId] = @{
                    DisplayName = "Group deleted from Microsoft Entra ID"
                    Shortname   = "Group deleted from Microsoft Entra ID"
                }
            }
        }
        
        return $script:groupCache[$groupId]
    }

    # Cache for filter display names
    if (-not (Get-Variable -Name filterCache -Scope Script -ErrorAction SilentlyContinue)) {
        $script:filterCache = @{}
    }

    # Helper function to get filter display name with caching
    function Get-FilterDisplayName {
        param (
            [string]$filterId
        )
        
        if (-not $script:filterCache.ContainsKey($filterId)) {
            try {
                $filterName = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters/${filterId}?`$select=displayName").displayName.replace("(", "").replace(")", "")
                $script:filterCache[$filterId] = $filterName
            }
            catch {
                $script:filterCache[$filterId] = "Filter not found"
            }
        }
        
        return $script:filterCache[$filterId]
    }

    switch ($NodeType) {
        "Application" {
            if ($appImage) {
                $mermaidDiagram += @"
subgraph $($appId)_$($ID)["$appName"]
    $($appId)_$($ID)-Name["<img src='data:;base64, $appImage' width='50' height='50'>"]
end
"@

            }
            else {
                $mermaidDiagram += @"
subgraph $($appId)_$($ID)["$appName"]
end
"@

            }
            return $mermaidDiagram
        }
        "Profile" {
            $mermaidDiagram += @"
            subgraph $($appId)_$($ID)["$appName"]
            end
"@

            return $mermaidDiagram
        }

        "AppGroupedByAssignments" {
            $outputLines = @()
            foreach ($assignment in $assignmentsInfo) {
                switch ($assignment.intent) {
                    "required" { $intent = "R" }
                    "available" { $intent = "A" }
                    "uninstall" { $intent = "U" }
                    default { $intent = $assignment.intent }
                }
                $odataType = $assignment.target.'@odata.type'

                # Use constants for special group IDs
                if (($assignment.id -split '_')[0] -eq "acacacac-9df4-4c7d-9d50-4ef0226f57a9") { $GroupName = "All Users" }
                elseif (($assignment.id -split '_')[0] -eq "adadadad-808e-44e2-905a-0b7873a8a531") { $GroupName = "All Devices" } 
                else { 
                    $targetGroupId = $assignment.target.groupId
                    $GroupName = (Get-GroupDisplayName -groupId $targetGroupId).Shortname
                }                

                if ($odataType -match '#microsoft\.graph\.(allDevicesAssignmentTarget|allLicensedUsersAssignmentTarget|groupAssignmentTarget)$') {
                    $mode = 'Included'
                    $smode = "I"
                }
                elseif ($odataType -match '#microsoft\.graph\.exclusionGroupAssignmentTarget$') {
                    $mode = 'Excluded'
                    $smode = "E"
                }
                else {
                    continue
                }

                $name = "$smode$intent$ID"
                $entry = "$($appId)_$($ID) -->|$mode| $name"
                $assignId = "a" + "$ID"

                if ($outputLines -notcontains $entry) {
                    $outputLines += $entry
                }
                $outputLines += "`n"
                $outputLines += "$name{$($assignment.intent)}-->$assignId"
                $outputLines += "`n"

                if ($assignment.target.deviceAndAppManagementAssignmentFilterId) {
                    $filterId = $assignment.target.deviceAndAppManagementAssignmentFilterId
                    $filterName = Get-FilterDisplayName -filterId $filterId
                    $filterStatus = $assignment.target.deviceAndAppManagementAssignmentFilterType
                    $outputLines += "$assignId[`"fa:fa-users $GroupName`"]-->|fa:fa-filter $filterStatus|$("f" + $ID)[$filterName]"
                }
                else {
                    $outputLines += "$assignId[`"fa:fa-users $GroupName`"]"
                }
                # Add a separator line between assignments
                $outputLines += "`n"
            }

            return $outputLines
        }
        "AppGroupedByApplications" {
            $outputLines = @()
            foreach ($assignment in $assignmentsInfo) {
                switch ($assignment.intent) {
                    "required" { $intent = "R" }
                    "available" { $intent = "A" }
                    "uninstall" { $intent = "U" }
                    default { $intent = $assignment.intent }
                }
                $odataType = $assignment.target.'@odata.type'

                if (($assignment.id -split '_')[0] -eq "acacacac-9df4-4c7d-9d50-4ef0226f57a9") { $GroupName = "All Users" }
                elseif (($assignment.id -split '_')[0] -eq "adadadad-808e-44e2-905a-0b7873a8a531") { $GroupName = "All Devices" } 
                else { 
                    $targetGroupId = $assignment.target.groupId
                    $GroupName = (Get-GroupDisplayName -groupId $targetGroupId).Shortname
                }                

                if ($odataType -match '#microsoft\.graph\.(allDevicesAssignmentTarget|allLicensedUsersAssignmentTarget|groupAssignmentTarget)$') {
                    $mode = 'Included'
                    $smode = "I"
                }
                elseif ($odataType -match '#microsoft\.graph\.exclusionGroupAssignmentTarget$') {
                    $mode = 'Excluded'
                    $smode = "E"
                }
                else {
                    continue
                }

                $name = "$smode$intent$ID" + ([array]::IndexOf($assignmentsInfo , $assignment)).ToString()
                $entry = "$($appId)_$($ID) -->|$mode| $name"
                $assignId = "a" + "$ID" + ([array]::IndexOf($assignmentsInfo , $assignment)).ToString()

                if ($outputLines -notcontains $entry) {
                    $outputLines += $entry
                }
                $outputLines += "`n"
                $outputLines += "$name{$($assignment.intent)}-->$assignId"
                $outputLines += "`n"

                if ($assignment.target.deviceAndAppManagementAssignmentFilterId) {
                    $filterId = $assignment.target.deviceAndAppManagementAssignmentFilterId
                    $filterName = Get-FilterDisplayName -filterId $filterId
                    $filterStatus = $assignment.target.deviceAndAppManagementAssignmentFilterType
                    $outputLines += "$assignId[`"fa:fa-users $GroupName`"]-->|fa:fa-filter $filterStatus|$("f" + $ID)[$filterName]"
                }
                else {
                    $outputLines += "$assignId[`"fa:fa-users $GroupName`"]"
                }
                # Add a separator line between assignments
                $outputLines += "`n"
            }

            return $outputLines
        }
        "ProfileGroupedByAssignments" {
            $outputLines = @()
            foreach ($assignment in $assignmentsInfo) {

                if ($assignment.target.'@odata.type' -eq "#microsoft.graph.allLicensedUsersAssignmentTarget") { $GroupName = "All Users" }
                elseif ($assignment.target.'@odata.type' -eq "#microsoft.graph.allDevicesAssignmentTarget") { $GroupName = "All Devices" } 
                else { 
                    $targetGroupId = $assignment.target.groupId
                    $GroupName = (Get-GroupDisplayName -groupId $targetGroupId).Shortname
                }                

                if ($assignment.target.'@odata.type' -match '#microsoft\.graph\.(allDevicesAssignmentTarget|allLicensedUsersAssignmentTarget|groupAssignmentTarget)$') {
                    $mode = 'Included'
                    $smode = "I"
                }
                elseif ($assignment.target.'@odata.type' -match '#microsoft\.graph\.exclusionGroupAssignmentTarget$') {
                    $mode = 'Excluded'
                    $smode = "E"
                }
                else {
                    continue
                }

                $name = "$smode$ID"
                $entry = "$($appId)_$($ID) -->|$mode| $name"

                if ($outputLines -notcontains $entry) {
                    $outputLines += $entry
                }
                $outputLines += "`n"

                if ($assignment.target.deviceAndAppManagementAssignmentFilterId) {
                    $filterId = $assignment.target.deviceAndAppManagementAssignmentFilterId
                    $filterName = Get-FilterDisplayName -filterId $filterId
                    $filterStatus = $assignment.target.deviceAndAppManagementAssignmentFilterType
                    $outputLines += "$name[`"fa:fa-users $GroupName`"]-->|fa:fa-filter $filterStatus|$("f" + $ID)[$filterName]"
                }
                else {
                    $outputLines += "$name[`"fa:fa-users $GroupName`"]"
                }
                # Add a separator line between assignments
                $outputLines += "`n"
            }

            return $outputLines
        }
        "ProfileGroupedByProfile" {
            $outputLines = @()
            foreach ($assignment in $assignmentsInfo) {

                if ($assignment.target.'@odata.type' -eq "#microsoft.graph.allLicensedUsersAssignmentTarget") { $GroupName = "All Users" }
                elseif ($assignment.target.'@odata.type' -eq "#microsoft.graph.allDevicesAssignmentTarget") { $GroupName = "All Devices" } 
                else { 
                    $targetGroupId = $assignment.target.groupId
                    $GroupName = (Get-GroupDisplayName -groupId $targetGroupId).Shortname
                }                

                if ($assignment.target.'@odata.type' -match '#microsoft\.graph\.(allDevicesAssignmentTarget|allLicensedUsersAssignmentTarget|groupAssignmentTarget)$') {
                    $mode = 'Included'
                    $smode = "I"
                }
                elseif ($assignment.target.'@odata.type' -match '#microsoft\.graph\.exclusionGroupAssignmentTarget$') {
                    $mode = 'Excluded'
                    $smode = "E"
                }
                else {
                    continue
                }

                $name = "$smode$ID" + ([array]::IndexOf($assignmentsInfo , $assignment)).ToString()
                $entry = "$($appId)_$($ID) -->|$mode| $name"

                if ($outputLines -notcontains $entry) {
                    $outputLines += $entry
                }
                $outputLines += "`n"

                if ($assignment.target.deviceAndAppManagementAssignmentFilterId) {
                    $filterId = $assignment.target.deviceAndAppManagementAssignmentFilterId
                    $filterName = Get-FilterDisplayName -filterId $filterId
                    $filterStatus = $assignment.target.deviceAndAppManagementAssignmentFilterType
                    $outputLines += "$name[`"fa:fa-users $GroupName`"]-->|fa:fa-filter $filterStatus|$("f" + $ID)[$filterName]"
                }
                else {
                    $outputLines += "$name[`"fa:fa-users $GroupName`"]"
                }
                # Add a separator line between assignments
                $outputLines += "`n"
            }

            return $outputLines
        }
    
    }
}