backup/Backup-DeviceConfigurations.ps1

#Requires -Version 7.0
function Backup-DeviceConfigurations {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string]$BackupPath,
        [Parameter(Mandatory)] [SecureString]$Token,
        [hashtable]$ScopeTagMap = @{}
    )

    try {
        $folder = Join-Path $BackupPath 'Device Configurations'
        $mobileConfigFolder = Join-Path $folder 'mobileconfig'
        $uri = '/beta/deviceManagement/deviceConfigurations'
        $items = Invoke-GraphRequest2 -Uri $uri -Token $Token

        foreach ($item in $items) {
            $odataType = $item.'@odata.type'
            # handle macOS and iOS custom configurations
            if ($odataType -eq '#microsoft.graph.macOSCustomConfiguration' -or $odataType -eq '#microsoft.graph.iosCustomConfiguration') {
                if ($item.payload) {
                    try {
                        $payloadBytes = [System.Convert]::FromBase64String($item.payload)
                        $payloadContent = [System.Text.Encoding]::UTF8.GetString($payloadBytes)
                        $fileName = ConvertTo-SanitizatedFileName -fileName "$($item.displayName).mobileconfig"
                        $mobileConfigPath = Join-Path $mobileConfigFolder $fileName
                        New-Item -ItemType Directory -Path $mobileConfigFolder -Force | Out-Null
                        Set-Content -Path $mobileConfigPath -Value $payloadContent -Encoding UTF8
                    }
                    catch {
                        Write-Verbose "failed to decode payload for $($item.displayName): $_"
                    }
                }
            }

            $assignments = Resolve-Assignments -AssignmentsUri "/beta/deviceManagement/deviceConfigurations/$($item.id)/assignments" -Token $Token
            if ($assignments) {
                $item | Add-Member -MemberType NoteProperty -Name 'assignments' -Value $assignments -Force
            }

            # don't save JSON for mobileconfig types, or save it with null payload
            if ($odataType -eq '#microsoft.graph.macOSCustomConfiguration' -or $odataType -eq '#microsoft.graph.iosCustomConfiguration') {
                $item | Add-Member -MemberType NoteProperty -Name 'payload' -Value $null -Force
            }

            # construct filename
            $type = $odataType -replace '#microsoft\.graph\.', ''
            $fileName = "$($item.displayName)_$type"

            $clean = Remove-VolatileKeys -InputObject $item
            Save-BackupItem -Item $clean -Folder $folder -FileName $fileName -ScopeTagMap $ScopeTagMap
        }
        Write-Verbose "backed up $($items.Count) device configurations to $folder"
    }
    catch {
        Write-Error "failed to backup device configurations: $_"
        return
    }
}

Export-ModuleMember -Function Backup-DeviceConfigurations