Iedere beheerder kent het wel.
Een gebruiker kan niet meer inloggen omdat:
- de telefoon met Microsoft Authenticator verdwenen is 📱
- MFA opnieuw ingesteld moet worden
- een nieuwe medewerker nog geen MFA heeft ingesteld
Dan begint vaak het bekende proces van MFA resetten of tijdelijke toegang regelen.
Gelukkig heeft Microsoft hiervoor een uitstekende oplossing: Temporary Access Pass (TAP).
Met een TAP kan een gebruiker tijdelijk inloggen en bijvoorbeeld MFA opnieuw instellen. Maar als beheerder wil je dit natuurlijk consistent, veilig en geautomatiseerd configureren.
Daarom heb ik een PowerShell script gemaakt dat de TAP-policy automatisch configureert via Microsoft Graph.
Inclusief:
- automatische installatie van Microsoft Graph
- parametercontrole
- logging
- transcript logging voor Intune
- verificatie van de configuratie
Wat is een Temporary Access Pass?
Een Temporary Access Pass is een tijdelijke toegangscode binnen Microsoft Entra ID waarmee een gebruiker kan:
- MFA opnieuw registreren
- een nieuwe authenticator-app instellen
- toegang krijgen tot het account zonder bestaande MFA
- een nieuw apparaat configureren
Je kunt het zien als een tijdelijke sleutel tot de voordeur van een account.
De TAP is bewust tijdelijk en kan worden ingesteld met:
- een minimale geldigheid
- een maximale geldigheid
- een code lengte
- eenmalig gebruik
Juist deze instellingen bepalen hoe veilig en gebruiksvriendelijk je omgeving is.
TAP + MFA flow
Onderstaand diagram laat zien hoe TAP werkt in combinatie met MFA.

Flow uitgelegd
1️⃣ Beheerder maakt een Temporary Access Pass
2️⃣ De gebruiker ontvangt de tijdelijke code
3️⃣ Gebruiker logt in met TAP
4️⃣ Microsoft Entra valideert de code
5️⃣ MFA registratie wordt gestart
6️⃣ TAP wordt ongeldig
Na deze stappen gebruikt de gebruiker weer normale MFA authenticatie.
Verbinding maken met Microsoft Graph
Het script gebruikt Microsoft Graph om de TAP-policy te wijzigen.
Dit gebeurt met:
Connect-MgGraph -Scopes "Policy.ReadWrite.AuthenticationMethod"
Wanneer het script voor het eerst wordt uitgevoerd, verschijnt er een Microsoft loginvenster.
Microsoft login scherm
Hier log je in met een account dat voldoende rechten heeft.
Bijvoorbeeld:
- Global Administrator
- Authentication Administrator
- Privileged Authentication Administrator

Toestemming geven aan Microsoft Graph
Omdat het script policies wijzigt, moet Microsoft Graph toestemming krijgen.
Het belangrijkste recht is:
Policy.ReadWrite.AuthenticationMethod
Dit geeft toegang tot authentication method policies, waaronder Temporary Access Pass.

Door op Accept te klikken krijgt de Microsoft Graph module toegang.
Overzicht van rechten
Microsoft Graph toont een lijst met rechten die gebruikt kunnen worden.

Dit lijkt een lange lijst, maar deze rechten horen bij de Microsoft Graph Command Line Tools.
Het script zelf gebruikt alleen:
Authentication Method Policy management
Waarom deze TAP instellingen?
In het script gebruik ik de volgende instellingen.
Default lifetime – 30 minuten
DefaultLifetimeInMinutes = 30
Dit betekent dat een TAP standaard 30 minuten geldig is.
Lang genoeg voor een gebruiker om:
- de code te ontvangen
- in te loggen
- MFA te registreren
Maar kort genoeg om risico te beperken.
Minimum lifetime – 10 minuten
MinimumLifetimeInMinutes = 10
Dit voorkomt dat iemand per ongeluk een TAP maakt die bijna direct verloopt.
Maximum lifetime – 60 minuten
MaximumLifetimeInMinutes = 60
Hiermee voorkom je dat TAP codes uren of dagen geldig blijven.
Een TAP moet tijdelijk blijven.
TAP lengte – 14 tekens
TapLength = 14
Dit geeft een goede balans tussen:
- veiligheid
- entropie
- gebruiksgemak
Eénmalig gebruik
UsableOnce = $true
Dit is een belangrijke beveiligingsmaatregel.
Na gebruik is de TAP direct ongeldig.
Security best practices voor TAP
Een paar praktische adviezen die ik zelf vaak gebruik in tenants.
Gebruik altijd “usable once”
Een TAP moet maar één keer gebruikt kunnen worden.
Dit voorkomt hergebruik van de code.
Houd de geldigheid kort
Aanbevolen waarden:
Default: 30 minuten
Maximum: 60 minuten
Hoe korter de geldigheid, hoe kleiner het risico.
Gebruik Conditional Access
Combineer TAP met:
- Conditional Access
- MFA registratie policies
- Identity Protection
Zo blijft TAP veilig.
Beperk wie TAP mag genereren
Gebruik alleen rollen zoals:
- Authentication Administrator
- Privileged Authentication Administrator
Zo voorkom je dat te veel mensen tijdelijke toegang kunnen genereren.
Logging en troubleshooting
Het script schrijft logs naar:
C:\Log
Hier wordt onder andere opgeslagen:
- script versie
- gebruiker
- apparaat
- IP adres
- TAP configuratie
Daarnaast wordt het transcript gekopieerd naar:
C:\ProgramData\Microsoft\IntuneManagementExtension\Logs
Hierdoor kun je het script ook gebruiken binnen Intune of automatisering.
Het PowerShell script
Hieronder staat het volledige script dat de TAP-policy configureert.
param (
[switch]$Silent,
[int]$DefaultLifetimeInMinutes = 30,
[int]$MinimumLifetimeInMinutes = 10,
[int]$MaximumLifetimeInMinutes = 60,
[int]$TapLength = 14,
[bool]$UsableOnce = $true
)
<#
.NOTES
===============================================================================
Created on: 2026-03-13
Created by: Vincent van Unen
Organization:
Filename: Set-TAPPolicy-v2.ps1
===============================================================================
.DESCRIPTION
Wijzigt de Temporary Access Pass-configuratie binnen Microsoft Entra ID
via Microsoft Graph. Indien Microsoft Graph nog niet is geïnstalleerd,
wordt deze automatisch geïnstalleerd voor de huidige gebruiker.
.VERSION
1.4.0
#>
#region Script metadata
$ScriptAuthor = "Vincent van Unen"
$ScriptVersion = "1.4.0"
$ScriptChangeDate = "2026-03-13"
$ScriptChangeLog = "Toegevoegd: controle en automatische installatie van Microsoft.Graph, parameterisering van TAP-instellingen"
$ScriptCurrentUser = $env:USERNAME
$ScriptRunningDevice = $env:COMPUTERNAME
$CurrentDate = Get-Date -Format 'yyyy-MM-dd'
$ScriptName = "Set-TAPPolicy-v2"
#endregion Script metadata
#region Paths
$TempPaths = @(
"C:\Temp",
"C:\Tmp",
"C:\Log"
)
foreach ($Path in $TempPaths) {
if (-not (Test-Path -Path $Path)) {
New-Item -ItemType Directory -Path $Path -Force | Out-Null
}
}
$LogDirectory = "C:\Log"
$LogFile = Join-Path $LogDirectory "$ScriptName-$CurrentDate.log"
$TranscriptFile = Join-Path $LogDirectory "$ScriptName-$CurrentDate-Transcript.log"
$IntuneLogPath = "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs"
#endregion Paths
#region Logging
function Write-ToLogFile {
[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[ValidateSet("INFO", "WARN", "ERROR", "FATAL", "DEBUG")]
[string]$Level = "INFO",
[Parameter(Mandatory = $true)]
[string]$Message,
[Parameter(Mandatory = $false)]
[string]$FilePath = $LogFile
)
try {
if ([string]::IsNullOrWhiteSpace($Message)) {
Add-Content -Path $FilePath -Value "" -ErrorAction Stop
}
else {
$Date = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss.fff')
Add-Content -Path $FilePath -Value "[$Date] [$Level] $Message" -ErrorAction Stop
}
}
catch {
Write-Warning "Schrijven naar logbestand is mislukt: $($_.Exception.Message)"
}
}
#endregion Logging
#region Helper functions
function Get-PublicIP {
[CmdletBinding()]
param ()
try {
return (Invoke-RestMethod -Uri "https://ifconfig.me/ip" -ErrorAction Stop).Trim()
}
catch {
Write-ToLogFile -Level "WARN" -Message "Publiek IP-adres kon niet worden opgehaald: $($_.Exception.Message)"
return "Onbekend"
}
}
function Get-PrivateIP {
[CmdletBinding()]
param ()
try {
$IpAddress = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction Stop |
Where-Object {
$_.IPAddress -notlike '169.254*' -and
$_.IPAddress -ne '127.0.0.1'
} |
Select-Object -ExpandProperty IPAddress -First 1
if ([string]::IsNullOrWhiteSpace($IpAddress)) {
return "Onbekend"
}
return $IpAddress
}
catch {
Write-ToLogFile -Level "WARN" -Message "Privé IP-adres kon niet worden opgehaald: $($_.Exception.Message)"
return "Onbekend"
}
}
function Install-MicrosoftGraphModule {
[CmdletBinding()]
param ()
try {
$ModuleExists = Get-Module -ListAvailable -Name Microsoft.Graph
if (-not $ModuleExists) {
Write-ToLogFile -Message "Microsoft.Graph module niet gevonden. Installatie wordt gestart."
if (-not (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue)) {
Write-ToLogFile -Message "NuGet package provider niet gevonden. Installatie wordt gestart."
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction Stop
}
$PsGallery = Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue
if ($PsGallery -and $PsGallery.InstallationPolicy -ne 'Trusted') {
Write-ToLogFile -Message "PSGallery wordt op Trusted gezet."
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -ErrorAction Stop
}
Install-Module Microsoft.Graph -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
Write-ToLogFile -Message "Microsoft.Graph module succesvol geïnstalleerd."
}
else {
Write-ToLogFile -Message "Microsoft.Graph module is al aanwezig."
}
Import-Module Microsoft.Graph.Authentication -ErrorAction Stop
Write-ToLogFile -Message "Microsoft.Graph.Authentication module succesvol geladen."
}
catch {
Write-ToLogFile -Level "ERROR" -Message "Installatie of import van Microsoft.Graph is mislukt: $($_.Exception.Message)"
throw
}
}
function Test-TapParameters {
[CmdletBinding()]
param ()
if ($MinimumLifetimeInMinutes -gt $DefaultLifetimeInMinutes) {
throw "MinimumLifetimeInMinutes mag niet groter zijn dan DefaultLifetimeInMinutes."
}
if ($MaximumLifetimeInMinutes -lt $DefaultLifetimeInMinutes) {
throw "MaximumLifetimeInMinutes mag niet kleiner zijn dan DefaultLifetimeInMinutes."
}
if ($TapLength -lt 8 -or $TapLength -gt 48) {
throw "TapLength moet tussen 8 en 48 liggen."
}
if ($MinimumLifetimeInMinutes -lt 10) {
throw "MinimumLifetimeInMinutes moet minimaal 10 zijn."
}
}
#endregion Helper functions
#region Script start
$PublicIP = Get-PublicIP
$PrivateIP = Get-PrivateIP
Write-ToLogFile -Message "Huidige datum = $CurrentDate"
Write-ToLogFile -Message "Script auteur = $ScriptAuthor"
Write-ToLogFile -Message "Script versie = $ScriptVersion"
Write-ToLogFile -Message "Wijzigingsdatum = $ScriptChangeDate"
Write-ToLogFile -Message "Wijzigingslog = $ScriptChangeLog"
Write-ToLogFile -Message "Gebruiker die dit script uitvoert = $ScriptCurrentUser"
Write-ToLogFile -Message "Apparaat waarop dit script draait = $ScriptRunningDevice"
Write-ToLogFile -Message "Publiek IP-adres = $PublicIP"
Write-ToLogFile -Message "Privé IP-adres = $PrivateIP"
Write-ToLogFile -Message "Gewenste TAP-configuratie: DefaultLifetime=$DefaultLifetimeInMinutes, MinimumLifetime=$MinimumLifetimeInMinutes, MaximumLifetime=$MaximumLifetimeInMinutes, TapLength=$TapLength, UsableOnce=$UsableOnce"
try {
Start-Transcript -Path $TranscriptFile -Force | Out-Null
}
catch {
Write-ToLogFile -Level "WARN" -Message "Transcript kon niet worden gestart: $($_.Exception.Message)"
}
#endregion Script start
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Write-ToLogFile -Message "TLS 1.2 is ingesteld."
Write-ToLogFile -Message "Valideren van TAP-parameters."
Test-TapParameters
Write-ToLogFile -Message "TAP-parameters zijn geldig."
Write-ToLogFile -Message "Controle op Microsoft.Graph module gestart."
Install-MicrosoftGraphModule
Write-ToLogFile -Message "Verbinding maken met Microsoft Graph."
Connect-MgGraph -Scopes "Policy.ReadWrite.AuthenticationMethod" -ErrorAction Stop | Out-Null
$Context = Get-MgContext
Write-ToLogFile -Message "Verbonden met tenant: $($Context.TenantId)"
Write-ToLogFile -Message "Verbonden met account: $($Context.Account)"
Write-ToLogFile -Message "Huidige TAP-configuratie ophalen."
$TapBefore = Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
-AuthenticationMethodConfigurationId 'temporaryAccessPass' `
-ErrorAction Stop
Write-ToLogFile -Message (
"Huidige TAP-configuratie: " +
"State=$($TapBefore.state), " +
"IsUsableOnce=$($TapBefore.isUsableOnce), " +
"Length=$($TapBefore.length), " +
"MinimumLifetime=$($TapBefore.minimumLifetimeInMinutes), " +
"DefaultLifetime=$($TapBefore.defaultLifetimeInMinutes), " +
"MaximumLifetime=$($TapBefore.maximumLifetimeInMinutes)"
)
$Body = @{
"@odata.type" = "#microsoft.graph.temporaryAccessPassAuthenticationMethodConfiguration"
state = "enabled"
isUsableOnce = $UsableOnce
defaultLifetimeInMinutes = $DefaultLifetimeInMinutes
minimumLifetimeInMinutes = $MinimumLifetimeInMinutes
maximumLifetimeInMinutes = $MaximumLifetimeInMinutes
length = $TapLength
}
Write-ToLogFile -Message "Nieuwe TAP-configuratie toepassen."
Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
-AuthenticationMethodConfigurationId 'temporaryAccessPass' `
-BodyParameter $Body `
-ErrorAction Stop
Write-ToLogFile -Message "Nieuwe TAP-configuratie succesvol toegepast."
Write-ToLogFile -Message "Nieuwe TAP-configuratie verifiëren."
$TapAfter = Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
-AuthenticationMethodConfigurationId 'temporaryAccessPass' `
-ErrorAction Stop
Write-ToLogFile -Message (
"Nieuwe TAP-configuratie: " +
"State=$($TapAfter.state), " +
"IsUsableOnce=$($TapAfter.isUsableOnce), " +
"Length=$($TapAfter.length), " +
"MinimumLifetime=$($TapAfter.minimumLifetimeInMinutes), " +
"DefaultLifetime=$($TapAfter.defaultLifetimeInMinutes), " +
"MaximumLifetime=$($TapAfter.maximumLifetimeInMinutes)"
)
if (-not $Silent) {
Write-Host ""
Write-Host "--- TAP policy na update ---"
$TapAfter |
Select-Object `
state,
isUsableOnce,
length,
minimumLifetimeInMinutes,
defaultLifetimeInMinutes,
maximumLifetimeInMinutes |
Format-Table -AutoSize
Write-Host ""
}
}
catch {
Write-ToLogFile -Level "ERROR" -Message "Er is een fout opgetreden: $($_.Exception.Message)"
Write-Error "Het script is mislukt: $($_.Exception.Message)"
throw
}
finally {
try {
Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
Write-ToLogFile -Message "Microsoft Graph-verbinding is gesloten."
}
catch {
Write-ToLogFile -Level "WARN" -Message "Microsoft Graph kon niet netjes worden afgesloten."
}
try {
Stop-Transcript | Out-Null
}
catch {
Write-ToLogFile -Level "WARN" -Message "Transcript kon niet worden gestopt."
}
try {
if (Test-Path -Path $IntuneLogPath) {
Copy-Item -Path $TranscriptFile -Destination $IntuneLogPath -Force
Write-ToLogFile -Message "Transcriptbestand gekopieerd naar: $IntuneLogPath"
}
else {
Write-ToLogFile -Level "WARN" -Message "Doelmap voor Intune-logs bestaat niet: $IntuneLogPath"
}
}
catch {
Write-ToLogFile -Level "WARN" -Message "Kopiëren van transcriptbestand is mislukt: $($_.Exception.Message)"
}
}
Wanneer gebruik je TAP?
Een paar situaties waarin TAP ideaal is.
Nieuwe medewerker
Nieuwe gebruiker krijgt TAP om:
- eerste login te doen
- MFA te configureren
Nieuwe telefoon
Gebruiker heeft een nieuwe telefoon.
Met TAP kan hij eenvoudig MFA opnieuw instellen.
Recovery scenario
Als MFA volledig stuk is, kan TAP gebruikt worden als veilige tijdelijke toegang.
Conclusie
Temporary Access Pass is een van de meest onderschatte features van Microsoft Entra ID.
Met een goede configuratie kun je:
- MFA onboarding verbeteren
- support tickets verminderen
- recovery eenvoudiger maken
- veiligheid behouden
Door dit via PowerShell te automatiseren hoef je het maar één keer goed in te richten.
Daarna laat je het script het werk doen.
Precies zoals het hoort.