Start-IntuneBackup.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
Orchestrates a full Intune backup.

.DESCRIPTION
Calls all Backup-* functions in parallel (or sequentially with -Sequential) and
writes JSON output to BackupPath.

.PARAMETER BackupPath
The output root directory where backup files will be written.

.PARAMETER Token
Bearer token for Microsoft Graph as a SecureString. Falls back to $env:GRAPH_TOKEN.

.PARAMETER Exclude
Array of module names to skip (e.g., @('Filters', 'ScopeTags')).

.PARAMETER IncludeActivationLock
When set, also runs Backup-ActivationLock.

.PARAMETER IncludeAutopilotDevices
When set, also runs Backup-AutopilotDevices.

.PARAMETER ThrottleLimit
Maximum number of parallel threads. Default is 10.

.PARAMETER Sequential
When set, modules run sequentially instead of in parallel. Useful for testing.

.PARAMETER SkipAssignmentReport
When set, skips generating the Assignment Report. Useful when only policy backups are needed.

.PARAMETER ResolveScopeTagIds
When set, translates roleScopeTagIds from numeric IDs to display names in exported JSON.

.EXAMPLE
Start-IntuneBackup -BackupPath C:\Backup -Token $token
#>

function Start-IntuneBackup {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$BackupPath,

        [SecureString]$Token = $null,

        [string[]]$Exclude = @(),

        [switch]$IncludeActivationLock,

        [switch]$IncludeAutopilotDevices,

        [int]$ThrottleLimit = 10,

        [switch]$Sequential,

        [switch]$ResolveScopeTagIds
    )

    # Validate token
    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'
    }

    # Create backup path if it doesn't exist
    if (-not (Test-Path -Path $BackupPath)) {
        New-Item -ItemType Directory -Path $BackupPath -Force | Out-Null
    }

    # Fetch scope tag map once (only when scope tag ID resolution is requested)
    $scopeTagMap = if ($ResolveScopeTagIds) { Get-ScopeTags -Token $Token } else { @{} }

    # Build modules list (35 standard non-opt-in modules)
    $modules = @(
        @{ Name = 'APNs'; Function = 'Backup-APNs' },
        @{ Name = 'AppConfiguration'; Function = 'Backup-AppConfiguration' },
        @{ Name = 'AppProtection'; Function = 'Backup-AppProtection' },
        @{ Name = 'AppleEnrollmentProfiles'; Function = 'Backup-AppleEnrollmentProfiles' },
        @{ Name = 'Applications'; Function = 'Backup-Applications' },
        @{ Name = 'Compliance'; Function = 'Backup-Compliance' },
        @{ Name = 'CompliancePartner'; Function = 'Backup-CompliancePartner' },
        @{ Name = 'ComplianceScripts'; Function = 'Backup-ComplianceScripts' },
        @{ Name = 'ConditionalAccess'; Function = 'Backup-ConditionalAccess' },
        @{ Name = 'CustomAttributes'; Function = 'Backup-CustomAttributes' },
        @{ Name = 'DeviceCategories'; Function = 'Backup-DeviceCategories' },
        @{ Name = 'DeviceCompliance'; Function = 'Backup-DeviceCompliance' },
        @{ Name = 'DeviceConfigurations'; Function = 'Backup-DeviceConfigurations' },
        @{ Name = 'DeviceManagementSettings'; Function = 'Backup-DeviceManagementSettings' },
        @{ Name = 'EnrollmentConfigurations'; Function = 'Backup-EnrollmentConfigurations' },
        @{ Name = 'EnrollmentStatusPage'; Function = 'Backup-EnrollmentStatusPage' },
        @{ Name = 'Filters'; Function = 'Backup-Filters' },
        @{ Name = 'GroupPolicyConfigurations'; Function = 'Backup-GroupPolicyConfigurations' },
        @{ Name = 'ManagedGooglePlay'; Function = 'Backup-ManagedGooglePlay' },
        @{ Name = 'ManagementIntents'; Function = 'Backup-ManagementIntents' },
        @{ Name = 'ManagementPartner'; Function = 'Backup-ManagementPartner' },
        @{ Name = 'NotificationTemplates'; Function = 'Backup-NotificationTemplates' },
        @{ Name = 'PowerShellScripts'; Function = 'Backup-PowerShellScripts' },
        @{ Name = 'ProactiveRemediation'; Function = 'Backup-ProactiveRemediation' },
        @{ Name = 'RemoteAssistancePartner'; Function = 'Backup-RemoteAssistancePartner' },
        @{ Name = 'ReusableSettings'; Function = 'Backup-ReusableSettings' },
        @{ Name = 'Roles'; Function = 'Backup-Roles' },
        @{ Name = 'ScopeTags'; Function = 'Backup-ScopeTags' },
        @{ Name = 'SettingsCatalog'; Function = 'Backup-SettingsCatalog' },
        @{ Name = 'ShellScripts'; Function = 'Backup-ShellScripts' },
        @{ Name = 'VolumePurchaseProgram'; Function = 'Backup-VolumePurchaseProgram' },
        @{ Name = 'WindowsDriverUpdates'; Function = 'Backup-WindowsDriverUpdates' },
        @{ Name = 'WindowsEnrollmentProfiles'; Function = 'Backup-WindowsEnrollmentProfiles' },
        @{ Name = 'WindowsFeatureUpdates'; Function = 'Backup-WindowsFeatureUpdates' },
        @{ Name = 'WindowsQualityUpdates'; Function = 'Backup-WindowsQualityUpdates' },
        @{ Name = 'AssignmentReport';      Function = 'Build-AssignmentReport' }
    )

    # Add opt-in modules
    if ($IncludeActivationLock) {
        $modules += @{ Name = 'ActivationLock'; Function = 'Backup-ActivationLock' }
    }
    if ($IncludeAutopilotDevices) {
        $modules += @{ Name = 'AutopilotDevices'; Function = 'Backup-AutopilotDevices' }
    }

    # Filter out excluded modules
    $modulesToRun = $modules | Where-Object { $_.Name -notin $Exclude }

    if ($Sequential) {
        # Sequential mode — enables mock interception in tests
        foreach ($mod in $modulesToRun) {
            & $mod.Function -BackupPath $BackupPath -Token $Token -ScopeTagMap $scopeTagMap
        }
    } else {
        # Parallel mode (production default)
        # SecureString cannot cross runspace boundaries — decrypt here and re-encrypt per runspace
        $modulePath = "$PSScriptRoot\IntuneBackup.psm1"
        $plainToken = ConvertFrom-SecureString -SecureString $Token -AsPlainText
        $path = $BackupPath
        $tags = $scopeTagMap

        $modulesToRun | ForEach-Object -Parallel {
            Import-Module $using:modulePath -Force
            $secureToken = ConvertTo-SecureString -String $using:plainToken -AsPlainText -Force
            $func = $_
            & $func.Function -BackupPath $using:path -Token $secureToken -ScopeTagMap $using:tags
        } -ThrottleLimit $ThrottleLimit
    }

    Write-Host "Backup complete. $($modulesToRun.Count) module(s) backed up to $BackupPath"
}

Export-ModuleMember -Function Start-IntuneBackup