core/Resolve-Assignments.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
Fetches and resolves assignments for an Intune resource.

.DESCRIPTION
Retrieves assignments from a given URI and resolves group IDs to display names.
Handles special target types like allDevices and allLicensedUsers.

.PARAMETER AssignmentsUri
The full URI to the assignments endpoint (e.g., /beta/deviceManagement/configurationPolicies/id/assignments).

.PARAMETER Token
The Bearer token for Graph authentication as a SecureString. If not provided, falls back to $env:GRAPH_TOKEN.

.PARAMETER ScopeTagMap
Optional hashtable for scope tag ID to name mapping.

.EXAMPLE
$assignments = Resolve-Assignments -AssignmentsUri $uri -Token $token
#>

function Resolve-Assignments {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$AssignmentsUri,

        [SecureString]$Token = $null,

        [hashtable]$ScopeTagMap = @{}
    )

    #region Validation
    if ($null -eq $Token -and $env:GRAPH_TOKEN) {
        $Token = ConvertTo-SecureString -String $env:GRAPH_TOKEN -AsPlainText -Force
    }
    if ($null -eq $Token) {
        throw 'No token provided and $env:GRAPH_TOKEN is not set'
    }
    #endregion Validation

    #region Fetch Assignments
    Write-Verbose "Fetching assignments from: $AssignmentsUri"
    
    try {
        $assignmentsResponse = Invoke-GraphRequest2 -Uri $AssignmentsUri -Token $Token
    } catch {
        Write-Verbose "Failed to fetch assignments: $_"
        return @()
    }
    #endregion Fetch Assignments

    #region Resolve Assignments
    $resolvedAssignments = [System.Collections.Generic.List[object]]@()

    if ($assignmentsResponse -and $assignmentsResponse.Count -gt 0) {
        foreach ($assignment in $assignmentsResponse) {

            # --- resolve target ---
            $target = [ordered]@{}
            foreach ($prop in $assignment.target.PSObject.Properties) {
                if ($prop.Name -notin @('id', 'groupId')) {
                    $target[$prop.Name] = $prop.Value
                }
            }

            # resolve groupId → groupName + groupType
            if ($assignment.target.PSObject.Properties['groupId'] -and $assignment.target.groupId) {
                $groupId = $assignment.target.groupId
                Write-Verbose "Resolving group ID: $groupId"
                try {
                    $groupInfo = Invoke-GraphRequest2 -Uri "/v1.0/groups/$groupId`?`$select=displayName,groupTypes,securityEnabled,membershipRule" -Token $Token
                    $target['groupName'] = $groupInfo.displayName
                    $target['groupId'] = $groupId
                    # determine group type (Dynamic vs Static, Security vs M365)
                    if ($groupInfo.membershipRule) {
                        $target['groupType'] = 'DynamicMembership'
                    } elseif ($groupInfo.groupTypes -contains 'Unified') {
                        $target['groupType'] = 'Microsoft365'
                    } else {
                        $target['groupType'] = 'StaticMembership'
                    }
                } catch {
                    Write-Verbose "Failed to resolve group $groupId, using GUID"
                    $target['groupId'] = $groupId
                }
            }

            # --- build output assignment (mirrors Python output structure) ---
            $out = [ordered]@{
                intent = $assignment.intent
                source = $assignment.source
                target = $target
            }

            # include settings if present (e.g. Win32 app assignment settings)
            if ($assignment.PSObject.Properties['settings'] -and $assignment.settings) {
                $out['settings'] = $assignment.settings
            }

            $resolvedAssignments.Add([PSCustomObject]$out) > $null
        }
    }

    return $resolvedAssignments.ToArray()
    #endregion Resolve Assignments
}

Export-ModuleMember -Function Resolve-Assignments