functions/entitlementManagement/accessPackages/Export-TmfAccessPackage.ps1

<#
.SYNOPSIS
Exports accessPackages, accessPackageResources and accessPackageAssignmentPolicies into TMF configuration.
.DESCRIPTION
Retrieves access packages and referenced resources/assignmentPolicies and outputs TMF objects. Returns objects unless -OutPath supplied.
.PARAMETER SpecificResources
Optional list of setting IDs or display names (comma separated accepted) to filter.
.PARAMETER OutPath
Root folder to write export; when omitted objects are returned. (Legacy alias: -OutPutPath)
.PARAMETER Append
Add content to existing file
.PARAMETER ForceBeta
Use beta Graph endpoint for retrieval.
.PARAMETER Cmdlet
Internal pipeline parameter; do not supply manually.
.EXAMPLE
Export-TmfAccessPackage -OutPutPath C:\temp\tmf
.EXAMPLE
Export-TmfAccessPackage -SpecificResources AccessPackageName
#>

function Export-TmfAccessPackage {
    [CmdletBinding()] param(
        [string[]]$SpecificResources,
        [Alias('OutPutPath')] [string]$OutPath,
        [System.Management.Automation.PSCmdlet]$Cmdlet = $PSCmdlet
    )
    begin {
        Test-GraphConnection -Cmdlet $Cmdlet
        $resourceName = 'accessPackages'
        $base = ($script:graphBaseUrl -replace '/beta$', '/v1.0')
        $tenant = (Invoke-MgGraphRequest -Method GET -Uri ("$($script:graphBaseUrl)/organization?`$select=displayName,id")).value
        $export = @()
        function Convert-Package {
            param($p)
            # Map expanded accessPackageResources into simplified objects
            $roleScopes = @()
            if ($p.resourceRoleScopes) {
                foreach ($rs in $p.resourceRoleScopes) {
                    $role = $rs.role
                    $scope = $rs.scope
                    $roleScopes += [ordered]@{
                        resourceRole       = $role.displayName
                        originSystem       = $role.originSystem
                        resourceType       = switch ($role.originSystem) {
                                             "AADGroup" {"AADGroup"}
                                             "AADApplication" {"Application"}
                                             "SharePointOnline" {"Sharepoint Online Site"}
                        }
                        resourceIdentifier = if ($role.originSystem -ne "SharePointOnline") {
                                                (Resolve-DirectoryObject -InputReference $scope.originId -ReturnObjects -DontFailIfNotExisting).displayName
                                             }
                                             else {
                                                $scope.originId
                                             }                        
                    }
                }
            }
            # Map expanded accessPackageAssignmentPolicies into simplified objects
            $assignmentPolicies = @()
            if ($p.assignmentPolicies) {
                foreach ($asp in $p.assignmentPolicies) {
                    $specificAllowedTargets = @()
                    foreach ($allowedTarget in $asp.specificAllowedTargets) {
                        $specificAllowedTargets += @{
                            reference = $allowedTarget.description
                            type = $allowedTarget."@odata.type".replace("#microsoft.graph.","")
                            description = $allowedTarget.description
                        }
                    }
                    if ($asp.requestApprovalSettings.stages) {
                        $stages = @()
                        foreach ($stage in $asp.requestApprovalSettings.stages) {
                            $primaryApprovers = @()
                            $fallbackPrimaryApprovers = @()
                            $escalationApprovers = @()
                            $fallbackEscalationApprovers = @()
                            if ($stage.primaryApprovers) {
                                foreach ($Approver in $stage.primaryApprovers) {
                                    switch ($Approver."@odata.type") {
                                        "#microsoft.graph.singleUser" {
                                            $primaryApprovers += @{
                                                reference = Resolve-User -InputReference $Approver.UserId -UserPrincipalName -DontFailIfNotExisting
                                                type = "singleUser"
                                            }
                                        }
                                        "#microsoft.graph.singleServicePrincipal" {
                                            $primaryApprovers += @{
                                                reference = Resolve-ServicePrincipal -InputReference $Approver.ServicePrincipalId -DisplayName -DontFailIfNotExisting
                                                type = "singleServicePrincipal"
                                            }
                                        }
                                        "#microsoft.graph.groupMembers" {
                                            $primaryApprovers += @{
                                                reference = Resolve-Group -InputReference $Approver.GroupId -DisplayName -DontFailIfNotExisting
                                                type = "groupMembers"
                                            }
                                        }
                                    }
                                    
                                }
                            }
                            if ($stage.fallbackPrimaryApprovers) {
                                foreach ($Approver in $stage.fallbackPrimaryApprovers) {
                                    switch ($Approver."@odata.type") {
                                        "#microsoft.graph.singleUser" {
                                            $fallbackPrimaryApprovers += @{
                                                reference = Resolve-User -InputReference $Approver.UserId -UserPrincipalName -DontFailIfNotExisting
                                                type = "singleUser"
                                            }
                                        }
                                        "#microsoft.graph.singleServicePrincipal" {
                                            $fallbackPrimaryApprovers += @{
                                                reference = Resolve-ServicePrincipal -InputReference $Approver.ServicePrincipalId -DisplayName -DontFailIfNotExisting
                                                type = "singleServicePrincipal"
                                            }
                                        }
                                        "#microsoft.graph.groupMembers" {
                                            $fallbackPrimaryApprovers += @{
                                                reference = Resolve-Group -InputReference $Approver.GroupId -DisplayName -DontFailIfNotExisting
                                                type = "groupMembers"
                                            }
                                        }
                                    }
                                    
                                }
                            }
                            if ($stage.escalationApprovers) {
                                foreach ($Approver in $stage.escalationApprovers) {
                                    switch ($Approver."@odata.type") {
                                        "#microsoft.graph.singleUser" {
                                            $escalationApprovers += @{
                                                reference = Resolve-User -InputReference $Approver.UserId -UserPrincipalName -DontFailIfNotExisting
                                                type = "singleUser"
                                            }
                                        }
                                        "#microsoft.graph.singleServicePrincipal" {
                                            $escalationApprovers += @{
                                                reference = Resolve-ServicePrincipal -InputReference $Approver.ServicePrincipalId -DisplayName -DontFailIfNotExisting
                                                type = "singleServicePrincipal"
                                            }
                                        }
                                        "#microsoft.graph.groupMembers" {
                                            $escalationApprovers += @{
                                                reference = Resolve-Group -InputReference $Approver.UserId -DisplayName -DontFailIfNotExisting
                                                type = "groupMembers"
                                            }
                                        }
                                    }
                                    
                                }
                            }
                            if ($stage.fallbackEscalationApprovers) {
                                foreach ($Approver in $stage.fallbackEscalationApprovers) {
                                    switch ($Approver."@odata.type") {
                                        "#microsoft.graph.singleUser" {
                                            $fallbackEscalationApprovers += @{
                                                reference = Resolve-User -InputReference $Approver.UserId -UserPrincipalName -DontFailIfNotExisting
                                                type = "singleUser"
                                            }
                                        }
                                        "#microsoft.graph.singleServicePrincipal" {
                                            $fallbackEscalationApprovers += @{
                                                reference = Resolve-ServicePrincipal -InputReference $Approver.ServicePrincipalId -DisplayName -DontFailIfNotExisting
                                                type = "singleServicePrincipal"
                                            }
                                        }
                                        "#microsoft.graph.groupMembers" {
                                            $fallbackEscalationApprovers += @{
                                                reference = Resolve-Group -InputReference $Approver.UserId -DisplayName -DontFailIfNotExisting
                                                type = "groupMembers"
                                            }
                                        }
                                    }
                                    
                                }
                            }
                            $stage.primaryApprovers = $primaryApprovers
                            $stage.fallbackPrimaryApprovers = $fallbackPrimaryApprovers
                            $stage.escalationApprovers = $escalationApprovers
                            $stage.fallbackEscalationApprovers = $fallbackEscalationApprovers

                            $stages += $stage
                            
                        }
                        $asp.requestApprovalSettings.stages = $stages
                    }
                    if (-not $specificAllowedTargets) {
                        $assignmentPolicies += [ordered]@{
                            displayname             = $asp.displayName
                            description             = $asp.description
                            allowedTargetScope      = $asp.allowedTargetScope
                            expiration              = $asp.expiration
                            requestorSettings       = $asp.requestorSettings
                            requestApprovalSettings = $asp.requestApprovalSettings
                            present                 = $true
                        }
                    }
                    else {
                        $assignmentPolicies += [ordered]@{
                            displayname             = $asp.displayName
                            description             = $asp.description
                            allowedTargetScope      = $asp.allowedTargetScope
                            specificAllowedTargets  = $specificAllowedTargets
                            expiration              = $asp.expiration
                            requestorSettings       = $asp.requestorSettings
                            requestApprovalSettings = $asp.requestApprovalSettings
                            present                 = $true
                        }
                    }                    
                }
            }
            # Get catalogName
            $catalogName = $null
            if ($p.catalog -and $p.catalog.displayName) {
                $catalogName = $p.catalog.displayName 
            } elseif ($p.catalog.Id) {
                $catalogName = $p.catalog.Id 
            }
            [ordered]@{
                displayName                     = $p.displayName
                description                     = $p.description
                isHidden                        = $p.isHidden
                catalog                         = $catalogName
                accessPackageResources          = $roleScopes
                assignmentPolicies              = $assignmentPolicies
                present                         = $true
            }
        }
        function Get-AllPackages {
            $list = @()
            $expand = 'assignmentPolicies,resourceRoleScopes($expand=*),catalog'
            try {
                $resp = Invoke-MgGraphRequest -Method GET -Uri "$base/identityGovernance/entitlementManagement/accessPackages?`$top=50&`$expand=$expand" -ErrorAction Stop
                if ($resp.'@odata.nextLink') {
                    do {
                        $list += $resp.value; $resp = Invoke-MgGraphRequest -Method GET -Uri $resp.'@odata.nextLink' 
                    } while ($resp.'@odata.nextLink') 
                } else {
                    $list += $resp.value 
                }
                return $list
            } catch {
                $initialError = $_.Exception.Message
                Write-PSFMessage -Level Verbose -FunctionName 'Export-TmfAccessPackage' -Message "Expanded retrieval failed ($initialError). Falling back to minimal retrieval then per-package expansion."
                # Fallback: retrieve packages without expand
                try {
                    $resp = Invoke-MgGraphRequest -Method GET -Uri "$base/identityGovernance/entitlementManagement/accessPackages?`$top=50" -ErrorAction Stop
                } catch {
                    Write-PSFMessage -Level Warning -FunctionName 'Export-TmfAccessPackage' -Message "Fallback minimal retrieval failed: $($_.Exception.Message)"; return $list
                }
                $minimal = @(); if ($resp.'@odata.nextLink') {
                    do {
                        $minimal += $resp.value; $resp = Invoke-MgGraphRequest -Method GET -Uri $resp.'@odata.nextLink' 
                    } while ($resp.'@odata.nextLink') 
                } else {
                    $minimal += $resp.value 
                }
                foreach ($pkg in $minimal) {
                    $detail = $null
                    try {
                        $detail = Invoke-MgGraphRequest -Method GET -Uri ("$base/identityGovernance/entitlementManagement/accessPackages/{0}?`$expand={1}" -f $pkg.id, $expand) -ErrorAction Stop
                        $list += $detail
                    } catch {
                        Write-PSFMessage -Level Verbose -FunctionName 'Export-TmfAccessPackage' -Message ("Per-package expand failed for {0}: {1}. Using minimal object." -f $pkg.displayName, $_.Exception.Message)
                        $list += $pkg
                    }
                }
                return $list
            }
        }
        $all = Get-AllPackages
    }
    process {
        if ($SpecificResources) {
            $ids = $SpecificResources | ForEach-Object { $_ -split ',' } | ForEach-Object Trim | Where-Object { $_ } | Select-Object -Unique
            foreach ($id in $ids) {
                $match = $all | Where-Object displayName -EQ $id; if ($match) {
                    $match | ForEach-Object { $export += Convert-Package $_ } 
                } else {
                    Write-PSFMessage -Level Warning -FunctionName 'Export-TmfAccessPackage' -String 'TMF.Export.NotFound' -StringValues $id, $resourceName, $tenant.displayName 
                } 
            }
        } else {
            foreach ($p in $all) {
                $export += Convert-Package $p 
            } 
        }
    }
    end {
        if ($OutPath) {
            if ($export) {
                if ($Append) {
                    Write-TmfExportFile -OutPath $OutPath -ParentPath 'entitlementManagement' -ResourceName $resourceName -Data $export -Append
                }
                else {
                    Write-TmfExportFile -OutPath $OutPath -ParentPath 'entitlementManagement' -ResourceName $resourceName -Data $export
                }
            }            
        } else {
            return $export 
        }
    }
}