Windows Autopilot Offline
- Pascal

- 21 jan 2025
- 6 minuten om te lezen
Bijgewerkt op: 7 mei

Introductie
Windows Autopilot biedt organisaties een efficiënte manier om nieuwe apparaten direct vanuit de doos te configureren en gereed te maken voor gebruik. Hoewel de offline modus met een Autopilot JSON-bestand ideaal lijkt voor scenario's zonder internettoegang, brengt het ook enkele belangrijke aandachtspunten met zich mee. In deze blog duiken we dieper in op het gebruik van het Windows Autopilot Offline JSON-bestand, de voordelen ervan, maar vooral de valkuilen en best practices om problemen te voorkomen. Zo zorg je ervoor dat jouw implementatie soepel verloopt en optimaal presteert.
Windows Autopilot Offline Scenarios: Wanneer kies je voor een Offline JSON-bestand?
Het gebruik van een Windows Autopilot Offline JSON-bestand kan in specifieke situaties een uitstekende keuze zijn. Het biedt een oplossing voor uitdagingen waarbij de standaard online methodiek niet direct toepasbaar is. Denk bijvoorbeeld aan de volgende scenario’s:
Geen afspraken met een leverancier: Wanneer er geen afspraken zijn gemaakt met een hardware leverancier om Autopilot Hashes vooraf te uploaden naar de tenant.
Gebrek aan rechten: Bij nieuwe of bestaande apparaten die niet zijn geregistreerd in Microsoft Intune Autopilot, en waarbij de uitrol wordt uitgevoerd door iemand zonder rechten om de hashes online naar de tenant te uploaden.
Versnellen van het proces: Om eindgebruikers sneller van start te laten gaan door direct een uitrol te doen met het juiste Autopilot-profiel, zonder te wachten totdat een apparaat volledig geregistreerd is in Microsoft Intune Autopilot.
Andere specifieke redenen: Zoals implementaties in omgevingen met beperkte internettoegang of wanneer tijds kritische uitrol vereist is.
Waarom voorzichtig zijn met het gebruik van Windows Autopilot Offline JSON?
Een Windows Autopilot Offline JSON-bestand bevat waardevolle informatie zoals de Tenant ID en domeinsuffix. Hoewel informatie zoals de Tenant ID ook via andere bronnen te achterhalen is (bijvoorbeeld via websites zoals WhatIsMyTenantID), brengt het gebruik van deze JSON-bestanden specifieke risico’s met zich mee.
De risico's: Onbevoegde toegang en validatie-uitdagingen
Onbevoegde apparaat registratie: Als een Autopilot Offline JSON-bestand in verkeerde handen valt, bestaat het risico dat onbevoegde apparaten zich als legitieme bedrijfsapparaten registreren in Microsoft Intune.
Moeilijke onderscheiding: Een apparaat dat via een gestolen of misbruikt JSON-bestand wordt geregistreerd, kan lastig te onderscheiden zijn van een geldig apparaat, vooral zonder strikte validatieprocessen.
Beveiliging van de omgeving: Hoewel gebruikers nog steeds moeten inloggen met geldige Microsoft 365-accounts, kan een kwaadwillende met toegang tot het JSON-bestand en inloggegevens extra schade aanrichten. Dit kan leiden tot potentieel gevaarlijke "supply chain"-achtige aanvallen.
Maatregelen voor extra beveiliging bij Windows Autopilot Offline JSON
Hieronder bespreken we enkele methoden om de uitrol via Autopilot Offline extra te beveiligen en risico’s te minimaliseren.
Beveiliging via Intune instellingen
Blokkeer persoonlijke apparaten in Intune:
Configureer Microsoft Intune zodat persoonlijke apparaten zich niet kunnen inschrijven.
Hoewel dit geen directe impact heeft op apparaten die een Offline Autopilot-profiel gebruiken (deze worden automatisch als 'Corporate' gemarkeerd), voorkomt het wel dat onbevoegde persoonlijke apparaten zich aanmelden buiten Autopilot-scenario’s.
Instelling: Ga in Intune naar Enrollment restrictions en blokkeer persoonlijke apparaten.

Block Personal Owned Devices
Conditional Access policies
Met Conditional Access kan je aanvullende beveiligingslagen toepassen, specifiek gericht op apparaat registratie.
Register or join devices configureren: Stel Conditional Access in voor de User Actions optie Register or join devices en vereis altijd Multi-Factor Authentication (MFA).
Dit dwingt gebruikers om MFA te doorlopen voordat een apparaat geregistreerd wordt, waardoor het risico op onbevoegde apparaat registraties significant wordt verminderd.
Waarom belangrijk? Dit voorkomt dat kwaadwillende eenvoudig toegang krijgen met slechts het JSON-bestand en inloggegevens.
Blokkeren van Offline Autopilot-profielen: Omdat een Offline Autopilot-profiel een specifieke naam genereert, kan je Conditional Access configureren om:
Het gebruik van dit profiel volledig te blokkeren als het niet nodig is.
Alleen apparaten die dit profiel gebruiken vanaf vertrouwde locaties (bijv. een kantoor met vast IP-adres) te accepteren.
Windows Autopilot downloaden
Op de Microsoft-website wordt gedetailleerd uitgelegd hoe je de Windows Autopilot JSON-bestanden kunt downloaden via deze link.
Let op: voor het correct downloaden van de JSON-bestanden is PowerShell 7 vereist. Je kunt dit installeren via het volgende commando met Winget:
winget install --id Microsoft.PowerShell --source winget
Gebruik vervolgens onderstaand PowerShell-script om de benodigde modules te installeren en de JSON-bestanden voor Autopilot te downloaden met PowerShell 7:
Powershell Script |
# Setup-AutopilotAppRegistration.ps1 # Run as Global Administrator — creates 'WinAutoPilotRegistration' app in Entra ID # Generated by Win11ImagingTool $ErrorActionPreference = 'Stop' #region — Install Microsoft.Graph module if needed if (-not (Get-Module Microsoft.Graph.Applications -ListAvailable)) { Write-Host 'Microsoft.Graph module niet gevonden — installeren...' -ForegroundColor Cyan Install-Module Microsoft.Graph -Scope CurrentUser -Force -AllowClobber } Write-Host 'Verbinden met Microsoft Graph...' -ForegroundColor Cyan Connect-MgGraph -Scopes 'Application.ReadWrite.All', 'Directory.ReadWrite.All' $displayName = 'WinAutoPilotRegistration' # Controleer of de app al bestaat $existing = Get-MgApplication -Filter "displayName eq '$displayName'" -ErrorAction SilentlyContinue $isNew = $null -eq $existing if ($existing) { Write-Host "App '$displayName' bestaat al (ID: $($existing.Id)) — wordt bijgewerkt." -ForegroundColor Yellow $app = $existing } else { Write-Host "App '$displayName' aanmaken..." -ForegroundColor Cyan $app = New-MgApplication -DisplayName $displayName Write-Host "App aangemaakt (ID: $($app.Id))." -ForegroundColor Green } # ── API permissions instellen ──────────────────────────────────────────────── Write-Host '' Write-Host 'API permissions opzoeken...' -ForegroundColor Cyan $permNames = @('DeviceManagementServiceConfig.ReadWrite.All', 'DeviceManagementManagedDevices.ReadWrite.All') $resourceAccess = @() try { # Methode 1: Find-MgGraphPermission (Graph module v2+, meest betrouwbaar) foreach ($perm in $permNames) { $found = Find-MgGraphPermission -Name $perm -PermissionType Application -ErrorAction Stop | Where-Object { $_.Name -eq $perm } if (-not $found) { throw "Exacte match niet gevonden voor: $perm" } Write-Host " OK $($found.Name) [$($found.Id)]" -ForegroundColor Green $resourceAccess += @{ id = $found.Id.ToString(); type = 'Role' } } } catch { Write-Host " Find-MgGraphPermission niet beschikbaar of mislukt: $($_.Exception.Message)" -ForegroundColor Yellow Write-Host ' Alternatieve methode: service principal AppRoles opvragen...' -ForegroundColor Cyan $resourceAccess = @() $graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'" -Property AppRoles foreach ($perm in $permNames) { $role = $graphSp.AppRoles | Where-Object { $_.Value -eq $perm } if ($role) { Write-Host " OK $($role.Value) [$($role.Id)]" -ForegroundColor Green $resourceAccess += @{ id = $role.Id.ToString(); type = 'Role' } } else { Write-Host " NIET GEVONDEN: $perm" -ForegroundColor Red } } } if ($resourceAccess.Count -ne $permNames.Count) { Write-Host "FOUT: slechts $($resourceAccess.Count) van $($permNames.Count) permissies gevonden." -ForegroundColor Red Write-Host 'Controleer je Graph module versie en probeer opnieuw.' -ForegroundColor Red exit 1 } # JSON body samenstellen en tonen (voor debug) $body = @{ requiredResourceAccess = @(@{ resourceAppId = '00000003-0000-0000-c000-000000000000' resourceAccess = $resourceAccess }) } | ConvertTo-Json -Depth 10 Write-Host '' Write-Host 'PATCH body:' -ForegroundColor DarkGray Write-Host $body -ForegroundColor DarkGray # PATCH uitvoeren met foutafhandeling Write-Host '' Write-Host 'API permissions bijwerken...' -ForegroundColor Cyan try { Invoke-MgGraphRequest -Method PATCH ` -Uri "https://graph.microsoft.com/v1.0/applications/$($app.Id)" ` -Body $body -ContentType 'application/json' Write-Host 'API permissions ingesteld.' -ForegroundColor Green } catch { Write-Host "PATCH mislukt: $($_.Exception.Message)" -ForegroundColor Red if ($_.ErrorDetails.Message) { Write-Host $_.ErrorDetails.Message -ForegroundColor Red } exit 1 } # Verificatie: app opnieuw uitlezen en tonen Start-Sleep -Seconds 2 Write-Host '' Write-Host 'Verificatie (ingestelde permissions):' -ForegroundColor Cyan $check = Invoke-MgGraphRequest -Method GET ` -Uri "https://graph.microsoft.com/v1.0/applications/$($app.Id)?`$select=requiredResourceAccess" $check.requiredResourceAccess | ForEach-Object { Write-Host " ResourceApp: $($_.resourceAppId)" $_.resourceAccess | ForEach-Object { Write-Host " id=$($_.id) type=$($_.type)" } } # ── Client secret aanmaken ─────────────────────────────────────────────────── Write-Host '' Write-Host 'Client secret aanmaken...' -ForegroundColor Cyan $secretName = "Win11ImagingTool-$(Get-Date -f 'yyyyMMdd-HHmm')" try { $secret = Add-MgApplicationPassword -ApplicationId $app.Id -PasswordCredential @{ DisplayName = $secretName EndDateTime = (Get-Date).AddYears(2) } Write-Host "Secret aangemaakt: $secretName" -ForegroundColor Green } catch { Write-Host "Secret aanmaken mislukt: $($_.Exception.Message)" -ForegroundColor Red exit 1 } $tenantId = (Get-MgContext).TenantId $appId = $app.AppId Write-Host '' Write-Host '════════════════════════════════════════' -ForegroundColor Green Write-Host ' App Registration gereed' -ForegroundColor Green Write-Host '════════════════════════════════════════' -ForegroundColor Green Write-Host " Tenant ID : $tenantId" Write-Host " App ID : $appId" Write-Host " Secret : $($secret.SecretText)" Write-Host '' Write-Host 'LET OP: Sla het secret NU op — het kan niet opnieuw worden opgevraagd!' -ForegroundColor Red Write-Host "Volgende stap: Entra ID > App Registrations > $displayName > API Permissions > Grant admin consent" -ForegroundColor Yellow Write-Host '' $csvPath = Join-Path ([Environment]::GetFolderPath('Desktop')) 'WinAutoPilotRegistration_Credentials.csv' [PSCustomObject]@{ TenantId = $tenantId AppId = $appId Secret = $secret.SecretText Note = 'Win11ImagingTool > Autopilot > Auto-Registration' } | Export-Csv -Path $csvPath -NoTypeInformation Write-Host "Credentials opgeslagen: $csvPath" -ForegroundColor Green Disconnect-MgGraph |

Entra Dynamische Groepen
Bij het gebruik van Windows Autopilot Offline-profielen, is het goed om te weten dat, zoals vermeld onder de sectie Conditional Access, een specifiek Enrollment Profile Name wordt gehanteerd. Deze waarde kan ook worden gebruikt in combinatie met Entra Dynamische groepen. Het is echter belangrijk te weten dat Windows Autopilot Offline altijd de naam OfflineAutopilotprofile-<correlator ID> gebruikt. De correlator ID, die overeenkomt met de ZtdCorrelationId, is terug te vinden in het Autopilot JSON-bestand en is gekoppeld aan het Autopilot-profiel.
Als je gebruik maakt van Entra Dynamische groepen waarbij de query is geconfigureerd op de naam van het Autopilot-profiel, moet je er rekening mee houden dat ook de waarde OfflineAutopilotprofile-<correlator ID> in de query wordt opgenomen.














Opmerkingen