Views/WhatsApplied.ps1

function Show-InTUIWhatsAppliedView {
    <#
    .SYNOPSIS
        Unified view showing all policies, profiles, and apps applied to a device or user.
    #>

    [CmdletBinding()]
    param()

    $exitView = $false

    while (-not $exitView) {
        Clear-Host
        Show-InTUIHeader
        Show-InTUIBreadcrumb -Path @('Home', "What's Applied?")

        $choices = @('By Device', 'By User', '─────────────', 'Back to Home')
        $selection = Show-InTUIMenu -Title "[blue]What's Applied?[/]" -Choices $choices

        switch ($selection) {
            'By Device' {
                $searchTerm = Read-InTUITextInput -Message "[blue]Enter device name to search[/]"
                if ([string]::IsNullOrWhiteSpace($searchTerm)) { continue }

                $safeTerm = ConvertTo-InTUISafeFilterValue -Value $searchTerm
                $devices = Show-InTUILoading -Title "[blue]Searching devices...[/]" -ScriptBlock {
                    Get-InTUIPagedResults -Uri '/deviceManagement/managedDevices' -Beta -PageSize 20 `
                        -Filter "contains(deviceName,'$safeTerm')" `
                        -Select 'id,deviceName,operatingSystem,userPrincipalName'
                }

                if ($null -eq $devices -or $devices.Results.Count -eq 0) {
                    Show-InTUIWarning "No devices found matching '$searchTerm'."
                    Read-InTUIKey
                    continue
                }

                $deviceChoices = @()
                foreach ($d in $devices.Results) {
                    $deviceChoices += "[white]$(ConvertTo-InTUISafeMarkup -Text $d.deviceName)[/] [grey]| $($d.operatingSystem) | $($d.userPrincipalName ?? 'Unassigned')[/]"
                }
                $choiceMap = Get-InTUIChoiceMap -Choices $deviceChoices
                $menuChoices = @($choiceMap.Choices + '─────────────' + 'Cancel')

                $deviceSelection = Show-InTUIMenu -Title "[blue]Select a device[/]" -Choices $menuChoices

                if ($deviceSelection -ne 'Cancel' -and $deviceSelection -ne '─────────────') {
                    $idx = $choiceMap.IndexMap[$deviceSelection]
                    if ($null -ne $idx -and $idx -lt $devices.Results.Count) {
                        $selectedDevice = $devices.Results[$idx]
                        Show-InTUIDeviceWhatsApplied -DeviceId $selectedDevice.id -DeviceName $selectedDevice.deviceName
                    }
                }
            }
            'By User' {
                $searchTerm = Read-InTUITextInput -Message "[blue]Enter user name or UPN to search[/]"
                if ([string]::IsNullOrWhiteSpace($searchTerm)) { continue }

                $safeTerm = ConvertTo-InTUISafeFilterValue -Value $searchTerm
                $users = Show-InTUILoading -Title "[blue]Searching users...[/]" -ScriptBlock {
                    Get-InTUIPagedResults -Uri '/users' -PageSize 20 `
                        -Filter "startsWith(displayName,'$safeTerm') or startsWith(userPrincipalName,'$safeTerm')" `
                        -Select 'id,displayName,userPrincipalName'
                }

                if ($null -eq $users -or $users.Results.Count -eq 0) {
                    Show-InTUIWarning "No users found matching '$searchTerm'."
                    Read-InTUIKey
                    continue
                }

                $userChoices = @()
                foreach ($u in $users.Results) {
                    $userChoices += "[white]$(ConvertTo-InTUISafeMarkup -Text $u.displayName)[/] [grey]| $($u.userPrincipalName)[/]"
                }
                $choiceMap = Get-InTUIChoiceMap -Choices $userChoices
                $menuChoices = @($choiceMap.Choices + '─────────────' + 'Cancel')

                $userSelection = Show-InTUIMenu -Title "[blue]Select a user[/]" -Choices $menuChoices

                if ($userSelection -ne 'Cancel' -and $userSelection -ne '─────────────') {
                    $idx = $choiceMap.IndexMap[$userSelection]
                    if ($null -ne $idx -and $idx -lt $users.Results.Count) {
                        $selectedUser = $users.Results[$idx]
                        Show-InTUIUserWhatsApplied -UserId $selectedUser.id -UserName $selectedUser.displayName
                    }
                }
            }
            'Back to Home' {
                $exitView = $true
            }
            default { continue }
        }
    }
}

function Show-InTUIDeviceWhatsApplied {
    <#
    .SYNOPSIS
        Shows all configuration, compliance, and app assignments applied to a specific device.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$DeviceId,

        [Parameter()]
        [string]$DeviceName = 'Device'
    )

    Clear-Host
    Show-InTUIHeader
    Show-InTUIBreadcrumb -Path @('Home', "What's Applied?", $DeviceName)

    Write-InTUILog -Message "Loading What's Applied for device" -Context @{ DeviceId = $DeviceId; DeviceName = $DeviceName }

    $data = Show-InTUILoading -Title "[blue]Loading applied policies and apps...[/]" -ScriptBlock {
        $configStates = Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices/$DeviceId/deviceConfigurationStates" -Beta
        $complianceStates = Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices/$DeviceId/deviceCompliancePolicyStates" -Beta
        $deviceInfo = Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices/${DeviceId}?`$select=userId" -Beta

        $appIntents = $null
        if ($deviceInfo.userId) {
            $appIntents = Invoke-InTUIGraphRequest -Uri "/users/$($deviceInfo.userId)/mobileAppIntentAndStates" -Beta
        }

        @{
            ConfigStates    = $configStates
            ComplianceStates = $complianceStates
            AppIntents      = $appIntents
        }
    }

    # Configuration Profiles
    $configItems = if ($data.ConfigStates.value) { @($data.ConfigStates.value) } else { @() }
    if ($configItems.Count -gt 0) {
        $configRows = @()
        foreach ($state in $configItems) {
            $stateColor = switch ($state.state) {
                'compliant'    { 'green' }
                'notApplicable' { 'grey' }
                default        { 'yellow' }
            }
            $configRows += , @(
                ($state.displayName ?? 'N/A'),
                "[$stateColor]$($state.state ?? 'N/A')[/]",
                ($state.platformType ?? 'N/A')
            )
        }
        Render-InTUITable -Title "Configuration Profiles ($($configItems.Count))" -Columns @('Profile Name', 'State', 'Platform') -Rows $configRows -BorderColor Blue
    }
    else {
        Show-InTUIPanel -Title "[blue]Configuration Profiles[/]" -Content "[grey]No configuration profile states found.[/]" -BorderColor Blue
    }

    # Compliance Policies
    $complianceItems = if ($data.ComplianceStates.value) { @($data.ComplianceStates.value) } else { @() }
    if ($complianceItems.Count -gt 0) {
        $compRows = @()
        foreach ($state in $complianceItems) {
            $stateColor = Get-InTUIComplianceColor -State $state.state
            $compRows += , @(
                ($state.displayName ?? 'N/A'),
                "[$stateColor]$($state.state ?? 'N/A')[/]"
            )
        }
        Render-InTUITable -Title "Compliance Policies ($($complianceItems.Count))" -Columns @('Policy Name', 'State') -Rows $compRows -BorderColor Green
    }
    else {
        Show-InTUIPanel -Title "[green]Compliance Policies[/]" -Content "[grey]No compliance policy states found.[/]" -BorderColor Green
    }

    # App Assignments
    if ($data.AppIntents -and $data.AppIntents.value) {
        $allApps = @()
        foreach ($intentState in $data.AppIntents.value) {
            if ($intentState.mobileAppList) {
                $allApps += @($intentState.mobileAppList)
            }
        }

        if ($allApps.Count -gt 0) {
            $appRows = @()
            foreach ($app in $allApps) {
                $installColor = Get-InTUIInstallStateColor -State $app.installState
                $appRows += , @(
                    ($app.displayName ?? 'N/A'),
                    "[$installColor]$($app.installState ?? 'N/A')[/]",
                    ($app.mobileAppIntent ?? 'N/A')
                )
            }
            Render-InTUITable -Title "App Assignments ($($allApps.Count))" -Columns @('App Name', 'Install State', 'Intent') -Rows $appRows -BorderColor Cyan
        }
        else {
            Show-InTUIPanel -Title "[cyan]App Assignments[/]" -Content "[grey]No app assignment data found.[/]" -BorderColor Cyan
        }
    }
    else {
        Show-InTUIPanel -Title "[cyan]App Assignments[/]" -Content "[grey]No app intent data available (no user association).[/]" -BorderColor Cyan
    }

    Read-InTUIKey
}

function Show-InTUIUserWhatsApplied {
    <#
    .SYNOPSIS
        Shows all configuration, compliance, and app assignments targeting a user via group membership.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$UserId,

        [Parameter()]
        [string]$UserName = 'User'
    )

    Clear-Host
    Show-InTUIHeader
    Show-InTUIBreadcrumb -Path @('Home', "What's Applied?", $UserName)

    Write-InTUILog -Message "Loading What's Applied for user" -Context @{ UserId = $UserId; UserName = $UserName }

    $data = Show-InTUILoading -Title "[blue]Loading user assignments...[/]" -ScriptBlock {
        # Get user's group memberships
        $groups = Invoke-InTUIGraphRequest -Uri "/users/$UserId/memberOf?`$select=id,displayName" -All

        # Get configuration policies with assignments
        $catalogPolicies = Invoke-InTUIGraphRequest -Uri '/deviceManagement/configurationPolicies?$expand=assignments&$top=100' -Beta
        $legacyPolicies = Invoke-InTUIGraphRequest -Uri '/deviceManagement/deviceConfigurations?$expand=assignments&$top=100' -Beta

        # Get compliance policies with assignments
        $compliancePolicies = Invoke-InTUIGraphRequest -Uri '/deviceManagement/deviceCompliancePolicies?$expand=assignments&$top=100' -Beta

        # Get apps with assignments
        $apps = Invoke-InTUIGraphRequest -Uri '/deviceAppManagement/mobileApps?$expand=assignments&$top=100' -Beta

        @{
            Groups             = $groups
            CatalogPolicies    = $catalogPolicies
            LegacyPolicies     = $legacyPolicies
            CompliancePolicies = $compliancePolicies
            Apps               = $apps
        }
    }

    $groupIds = @()
    $groupList = if ($data.Groups -is [array]) { $data.Groups } elseif ($data.Groups.value) { @($data.Groups.value) } else { @() }
    foreach ($g in $groupList) {
        $groupIds += $g.id
    }

    # Well-known group IDs for All Users / All Devices
    $allUsersId = 'acacacac-9df4-4c7d-9d50-4ef0226f57a9'
    $allDevicesId = 'adadadad-808e-44e2-905a-0b7873a8a531'

    # Helper: check if any assignment targets this user's groups
    $matchAssignments = {
        param($assignments)
        foreach ($assignment in $assignments) {
            $target = $assignment.target
            $type = $target.'@odata.type'
            if ($type -eq '#microsoft.graph.allLicensedUsersAssignmentTarget' -or $type -eq '#microsoft.graph.allDevicesAssignmentTarget') {
                return $true
            }
            if ($target.groupId -and ($groupIds -contains $target.groupId -or $target.groupId -eq $allUsersId -or $target.groupId -eq $allDevicesId)) {
                return $true
            }
        }
        return $false
    }

    # Match configuration profiles
    $matchedConfigs = @()
    foreach ($source in @($data.CatalogPolicies, $data.LegacyPolicies)) {
        $items = if ($source.value) { @($source.value) } else { @() }
        foreach ($policy in $items) {
            $assignments = if ($policy.assignments) { @($policy.assignments) } else { @() }
            if ($assignments.Count -gt 0 -and (& $matchAssignments $assignments)) {
                $matchedConfigs += $policy
            }
        }
    }

    if ($matchedConfigs.Count -gt 0) {
        $configRows = @()
        foreach ($policy in $matchedConfigs) {
            $name = $policy.displayName ?? $policy.name ?? 'N/A'
            $configRows += , @(
                (ConvertTo-InTUISafeMarkup -Text $name),
                "$(@($policy.assignments).Count) groups"
            )
        }
        Render-InTUITable -Title "Configuration Profiles ($($matchedConfigs.Count))" -Columns @('Profile Name', 'Assignments') -Rows $configRows -BorderColor Blue
    }
    else {
        Show-InTUIPanel -Title "[blue]Configuration Profiles[/]" -Content "[grey]No matching configuration profiles found.[/]" -BorderColor Blue
    }

    # Match compliance policies
    $matchedCompliance = @()
    $compItems = if ($data.CompliancePolicies.value) { @($data.CompliancePolicies.value) } else { @() }
    foreach ($policy in $compItems) {
        $assignments = if ($policy.assignments) { @($policy.assignments) } else { @() }
        if ($assignments.Count -gt 0 -and (& $matchAssignments $assignments)) {
            $matchedCompliance += $policy
        }
    }

    if ($matchedCompliance.Count -gt 0) {
        $compRows = @()
        foreach ($policy in $matchedCompliance) {
            $compRows += , @(
                (ConvertTo-InTUISafeMarkup -Text ($policy.displayName ?? 'N/A')),
                "$(@($policy.assignments).Count) groups"
            )
        }
        Render-InTUITable -Title "Compliance Policies ($($matchedCompliance.Count))" -Columns @('Policy Name', 'Assignments') -Rows $compRows -BorderColor Green
    }
    else {
        Show-InTUIPanel -Title "[green]Compliance Policies[/]" -Content "[grey]No matching compliance policies found.[/]" -BorderColor Green
    }

    # Match apps
    $matchedApps = @()
    $appItems = if ($data.Apps.value) { @($data.Apps.value) } else { @() }
    foreach ($app in $appItems) {
        $assignments = if ($app.assignments) { @($app.assignments) } else { @() }
        if ($assignments.Count -gt 0 -and (& $matchAssignments $assignments)) {
            $matchedApps += $app
        }
    }

    if ($matchedApps.Count -gt 0) {
        $appRows = @()
        foreach ($app in $matchedApps) {
            $appRows += , @(
                (ConvertTo-InTUISafeMarkup -Text ($app.displayName ?? 'N/A')),
                "$(@($app.assignments).Count) assignments"
            )
        }
        Render-InTUITable -Title "App Assignments ($($matchedApps.Count))" -Columns @('App Name', 'Assignments') -Rows $appRows -BorderColor Cyan
    }
    else {
        Show-InTUIPanel -Title "[cyan]App Assignments[/]" -Content "[grey]No matching app assignments found.[/]" -BorderColor Cyan
    }

    Write-InTUIText ""
    Write-InTUIText "[grey]Groups evaluated: $($groupIds.Count) | Profiles: $($matchedConfigs.Count) | Compliance: $($matchedCompliance.Count) | Apps: $($matchedApps.Count)[/]"

    Read-InTUIKey
}