ProvAgentTroubleshooter.psm1
<#
Disclaimer: The scripts are not supported under any Microsoft standard support program or service. The scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the scripts or documentation, even if Microsoft has been advised of the possibility of such damages. #> #------------------------------------------------------------------------------------------------------------------------------------- # # Copyright © 2025 Microsoft Corporation. All rights reserved. # #------------------------------------------------------------------------------------------------------------------------------------- # # NAME: Entra ID Provisioning Agent Troubleshooting PowerShell Module # #------------------------------------------------------------------------------------------------------------------------------------- <# .SYNOPSIS Collects logs and diagnostic data for Microsoft Entra ID Provisioning Agent troubleshooting. .DESCRIPTION This module configures tracing, collects logs, exports event logs, gathers system and registry info, and compresses all data that helps in troubleshooting Microsoft Entra ID Provisioning Agent. .NOTES Author: [Akos Regi & Tariq Jaber] Version: 0.0.3 Date: 2025-05-20 #> # Color for progress Write-Host messages $ProgressColor = 'Cyan' # List of required files to check before compressing $RequiredFiles = @( 'Application.evtx', 'build.txt', 'CAPI2.evtx', 'CLOUDSYNC-AADConnectProvisioningAgentServiceReg.txt', 'credman.txt', 'CurrentUser-CA-store.txt', 'CurrentUser-My-store.txt', 'CurrentUser-Root-store.txt', 'env.txt', 'GPComputer.html', 'GPReport_User.html', 'ipconfig-info.txt', 'kdc.etl', 'kerb.etl', 'kerb.evtx', 'lanmanserver-key.txt', 'lanmanworkstation-key.txt', 'ldap.etl', 'LocalMachine-CA-store.txt', 'LocalMachine-My-store.txt', 'LocalMachine-Root-store.txt', 'lsa-key.txt', 'machine.config', 'msinfo32.nfo', 'Netlogon-key.txt', 'netlogon.log', 'netmon.cab', 'netmon.etl', 'netsetup.log', 'NTDS.txt', 'ntlm.etl', 'Policies-key.txt', 'schannel-key.txt', 'Security.evtx', 'ssl.etl', 'start-tasklist.txt', 'stop-tasklist.txt', 'System.evtx', 'whoami.txt', 'winhttp.txt' ) function Test-FileReady { param([string]$Path) if (!(Test-Path $Path)) { return $false } try { $stream = [System.IO.File]::Open($Path, 'Open', 'Read', 'ReadWrite') $stream.Close() return $true } catch { return $false } } <# .SYNOPSIS Collects logs and diagnostic data for Microsoft Provisioning Agent troubleshooting. .DESCRIPTION Collects logs, event logs, system info, and compresses them for support. .EXAMPLE Start-ProvAgentLogCollection Start-ProvAgentLogCollection -OutputFolder 'C:\temp' -LogFolder 'C:\temp\Logs' #> function Start-ProvAgentLogCollection { [CmdletBinding()] param( [string]$OutputFolder = 'C:\temp', [string]$LogFolder = ("C:\temp\ProvAgentLogs_{0}_UTC" -f (Get-Date -Format 'yyyyMMdd-HHmm')) ) # Add current date/time to zip file name if not provided, and ensure it's inside $LogFolder $dateTimeStr = (Get-Date -Format 'yyyyMMdd-HHmm') if ([string]::IsNullOrWhiteSpace($ZipFile)) { $ZipFile = Join-Path $LogFolder "MicrosoftTraceLogs_$dateTimeStr.zip" } elseif ($ZipFile -notmatch '\\[0-9]{8}-[0-9]{4}\.zip$') { $baseName = [System.IO.Path]::GetFileNameWithoutExtension($ZipFile) $ZipFile = Join-Path $LogFolder ("{0}_{1}.zip" -f $baseName, $dateTimeStr) } else { $ZipFile = Join-Path $LogFolder ([System.IO.Path]::GetFileName($ZipFile)) } Write-Host 'Starting Microsoft Provisioning Agent log collection...' Write-Host "[+] Checking/creating output folder: $OutputFolder" -ForegroundColor $ProgressColor if (!(Test-Path $OutputFolder)) { New-Item -Path $OutputFolder -ItemType Directory -Force | Out-Null } Write-Host "[+] Preparing log folder: $LogFolder" -ForegroundColor $ProgressColor if (Test-Path $LogFolder) { Remove-Item "$LogFolder\*" -Force -Recurse -ErrorAction SilentlyContinue } else { New-Item -Path $LogFolder -ItemType Directory -Force | Out-Null } # Set debug flags (environment variables) Write-Host '[+] Setting debug environment variables' -ForegroundColor $ProgressColor $env:KdcDebugFlags = '0xfffff' $env:ldapDebugFlags = '0x1FFFDFF3' $env:NtlmDebugFlags = '0x1ffDf' $env:SslDebugFlags = '0xffffffff' $env:KerbDebugFlags = '0x6ffffff' # Configure registry for tracing $regPaths = @( 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\AADConnectProvisioningAgent.exe', 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\AADConnectProvisioningAgentWizard.exe', 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\powershell.exe' ) Write-Host '[+] Configuring registry for tracing' -ForegroundColor $ProgressColor foreach ($reg in $regPaths) { if (!(Test-Path $reg)) { New-Item -Path $reg -Force | Out-Null } } Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA' -Name 'SPMInfoLevel' -Value 0xC03E3F -Type DWord -Force Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA' -Name 'LogToFile' -Value 1 -Type DWord -Force Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA' -Name 'NegEventMask' -Value 0xF -Type DWord -Force # Start ETW tracing Write-Host '[+] Starting ETW tracing...' -ForegroundColor $ProgressColor $logman = 'logman.exe' Write-Host '[+] Running: logman start kdc ...' -ForegroundColor $ProgressColor & $logman start kdc -p '{1BBA8B19-7F31-43c0-9643-6E911F79A06B}' $env:KdcDebugFlags -o "$LogFolder\kdc.etl" -ets Write-Host '[+] Running: logman start kerb ...' -ForegroundColor $ProgressColor & $logman start kerb -p '{6B510852-3583-4e2d-AFFE-A67F9F223438}' $env:KerbDebugFlags -o "$LogFolder\kerb.etl" -ets Write-Host '[+] Running: logman start ldap ...' -ForegroundColor $ProgressColor & $logman start ldap -p '{099614A5-5DD7-4788-8BC9-E29F43DB28FC}' $env:ldapDebugFlags -o "$LogFolder\ldap.etl" -ets Write-Host '[+] Running: logman start ntlm ...' -ForegroundColor $ProgressColor & $logman start ntlm -p '{5BBB6C18-AA45-49b1-A15F-085F7ED0AA90}' $env:NtlmDebugFlags -o "$LogFolder\ntlm.etl" -ets Write-Host '[+] Running: logman start ssl ...' -ForegroundColor $ProgressColor & $logman start ssl -p '{37D2C3CD-C5D4-4587-8531-4696C44244C8}' $env:SslDebugFlags -o "$LogFolder\ssl.etl" -ets # Set nltest debug flag Write-Host '[+] Setting nltest debug flag' -ForegroundColor $ProgressColor nltest /dbflag:0x2fffffff | Out-Null # Enable and clear event logs Write-Host '[+] Enabling and clearing event logs (CAPI2, Kerberos)' -ForegroundColor $ProgressColor wevtutil.exe set-log Microsoft-Windows-CAPI2/Operational /enabled:true wevtutil.exe clear-log Microsoft-Windows-CAPI2/Operational wevtutil.exe set-log Microsoft-Windows-Kerberos/Operational /enabled:true wevtutil.exe clear-log Microsoft-Windows-Kerberos/Operational # Start network trace Write-Host '[+] Starting network trace (netsh)' -ForegroundColor $ProgressColor netsh trace start traceFile="$LogFolder\netmon.etl" capture=yes | Out-Null # Flush DNS, purge tickets Write-Host '[+] Flushing DNS and purging Kerberos tickets' -ForegroundColor $ProgressColor ipconfig /flushdns | Out-Null klist purge | Out-Null klist -li 0x3e7 purge | Out-Null # Collect initial info Write-Host '[+] Collecting initial system and network info' -ForegroundColor $ProgressColor whoami /all | Out-File "$LogFolder\whoami.txt" netsh winhttp show proxy | Out-File "$LogFolder\winhttp.txt" tasklist /svc | Out-File "$LogFolder\start-tasklist.txt" Write-Host '[*] Please reproduce the issue now...' -BackgroundColor Yellow -ForegroundColor Black Write-Host ("[*] Current UTC time: {0}" -f ((Get-Date).ToUniversalTime().ToString('yyyy-MM-dd HH:mm:ss'))) -ForegroundColor $ProgressColor Write-Host '[*] Waiting 30 seconds or for the issue to be reproduced...' -BackgroundColor Yellow -ForegroundColor Black for ($i = 0; $i -lt 30; $i++) { $char = if ($i % 2 -eq 0) { '/' } else { '\' } Write-Host -NoNewline "`r$char" Start-Sleep -Seconds 1 } Write-Host "`r " -NoNewline # Clear spinner character Write-Host '[*] 30 seconds elapsed.' -BackgroundColor Yellow -ForegroundColor Blue Read-Host '[*] If the issue was reproduced, press Enter to stop tracing.' Write-Host ("[*] Current UTC time: {0}" -f ((Get-Date).ToUniversalTime().ToString('yyyy-MM-dd HH:mm:ss'))) -ForegroundColor $ProgressColor Write-Host '[+] Stopping ETW tracing and collecting logs...' -ForegroundColor $ProgressColor Write-Host '[+] Running: logman stop kerb ...' -ForegroundColor $ProgressColor & $logman stop kerb -ets Write-Host '[+] Running: logman stop kdc ...' -ForegroundColor $ProgressColor & $logman stop kdc -ets Write-Host '[+] Running: logman stop ldap ...' -ForegroundColor $ProgressColor & $logman stop ldap -ets Write-Host '[+] Running: logman stop ntlm ...' -ForegroundColor $ProgressColor & $logman stop ntlm -ets Write-Host '[+] Running: logman stop ssl ...' -ForegroundColor $ProgressColor & $logman stop ssl -ets # Remove registry keys Write-Host '[+] Cleaning up registry keys and debug flags' -ForegroundColor $ProgressColor foreach ($reg in $regPaths) { Remove-Item -Path $reg -Force -ErrorAction SilentlyContinue } Remove-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA' -Name 'SPMInfoLevel' -ErrorAction SilentlyContinue Remove-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA' -Name 'LogToFile' -ErrorAction SilentlyContinue Remove-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA' -Name 'NegEventMask' -ErrorAction SilentlyContinue nltest /dbflag:0x0 | Out-Null # Export event logs Write-Host '[+] Exporting event logs (Application, System, Security, Kerberos, CAPI2)' -ForegroundColor $ProgressColor wevtutil.exe export-log Application "$LogFolder\Application.evtx" /overwrite:true wevtutil.exe export-log System "$LogFolder\System.evtx" /overwrite:true wevtutil.exe export-log Security "$LogFolder\Security.evtx" /overwrite:true wevtutil.exe set-log Microsoft-Windows-Kerberos/Operational /enabled:false wevtutil.exe export-log Microsoft-Windows-Kerberos/Operational "$LogFolder\kerb.evtx" /overwrite:true wevtutil.exe export-log Microsoft-Windows-CAPI2/Operational "$LogFolder\CAPI2.evtx" /overwrite:true # Certificate store info Write-Host '[+] Exporting certificate store information' -ForegroundColor $ProgressColor $stores = @( @{ Name = "My"; Location = "LocalMachine" }, @{ Name = "My"; Location = "CurrentUser" }, @{ Name = "Root"; Location = "LocalMachine" }, @{ Name = "Root"; Location = "CurrentUser" }, @{ Name = "CA"; Location = "LocalMachine" }, @{ Name = "CA"; Location = "CurrentUser" } ) foreach ($store in $stores) { $locSwitch = if ($store.Location -eq "CurrentUser") { "-user" } else { "" } $fileName = "$LogFolder\$($store.Location)-$($store.Name)-store.txt" Write-Host "[+] -- Exporting cert store: $($store.Location)\$($store.Name)" -ForegroundColor $ProgressColor certutil.exe -silent -v $locSwitch -store $store.Name > $fileName } # Network info Write-Host '[+] Collecting network credentials and IP configuration' -ForegroundColor $ProgressColor cmdkey.exe /list > "$LogFolder\credman.txt" ipconfig /all > "$LogFolder\ipconfig-info.txt" Write-Host '[+] Stopping network trace (netsh)' -ForegroundColor $ProgressColor netsh trace stop | Out-Null # Copy log files $copyFiles = @( "$env:windir\debug\netlogon.log", "$env:windir\debug\netlogon.bak", "$env:windir\system32\lsass.log", "$env:windir\debug\netsetup.log", "$env:windir\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config" ) Write-Host '[+] Copying log files (netlogon, lsass, netsetup, machine.config)' -ForegroundColor $ProgressColor foreach ($file in $copyFiles) { if (Test-Path $file) { Copy-Item $file $LogFolder -Force } } # Environment and registry info Write-Host '[+] Collecting environment and registry information' -ForegroundColor $ProgressColor Get-ChildItem Env: | Out-File "$LogFolder\env.txt" reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v BuildLabEx > "$LogFolder\build.txt" reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa" /s > "$LogFolder\lsa-key.txt" reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies" /s > "$LogFolder\Policies-key.txt" reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer" /s > "$LogFolder\lanmanserver-key.txt" reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation" /s > "$LogFolder\lanmanworkstation-key.txt" reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon" /s > "$LogFolder\Netlogon-key.txt" reg query "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NTDS" /s > "$LogFolder\NTDS.txt" reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL" /s > "$LogFolder\schannel-key.txt" # DLL version info $dlls = @( 'kerberos.dll','lsasrv.dll','netlogon.dll','kdcsvc.dll','msv1_0.dll','schannel.dll','dpapisrv.dll','basecsp.dll','scksp.dll','bcrypt.dll','bcryptprimitives.dll','ncrypt.dll','ncryptprov.dll','cryptsp.dll','rsaenh.dll','Cryptdll.dll' ) Write-Host '[+] Collecting DLL version information' -ForegroundColor $ProgressColor foreach ($dll in $dlls) { $dllPath = Join-Path $env:SystemRoot "System32\$dll" if (Test-Path $dllPath) { (Get-Item $dllPath).VersionInfo | Select-Object FileName,FileVersion | Out-File -Append "$LogFolder\build.txt" } } # More info Write-Host '[+] Collecting additional system information (tasklist, msinfo32, gpresult)' -ForegroundColor $ProgressColor tasklist /svc | Out-File "$LogFolder\stop-tasklist.txt" msinfo32 /nfo "$LogFolder\msinfo32.nfo" gpresult /H "$LogFolder\GPReport_User.html" gpresult /Scope COMPUTER /H "$LogFolder\GPComputer.html" # Registry: AADConnectProvisioningAgent Write-Host '[+] Collecting AADConnectProvisioningAgent registry information' -ForegroundColor $ProgressColor reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AADConnectProvisioningAgent" /s > "$LogFolder\$env:COMPUTERNAME-AADConnectProvisioningAgentServiceReg.txt" Write-Host '[+] Waiting for all required files to be ready for compression...' -ForegroundColor $ProgressColor $waitTimeout = 180 # seconds $waitInterval = 10 # seconds $startTime = Get-Date $notReady = @() do { $notReady = @() foreach ($file in $RequiredFiles) { $fullPath = Join-Path $LogFolder $file if (-not (Test-FileReady $fullPath)) { $notReady += $file } } if ($notReady.Count -gt 0) { Write-Host ("[+] Waiting for files: {0}" -f ($notReady -join ', ')) -ForegroundColor Yellow Start-Sleep -Seconds $waitInterval } } while ($notReady.Count -gt 0 -and ((Get-Date) - $startTime).TotalSeconds -lt $waitTimeout) while ($notReady.Count -gt 0) { Write-Host ("[!] Warning: Some files are still not ready after waiting: {0}" -f ($notReady -join ', ')) -ForegroundColor Red Write-Host "Some files are not ready. Type 'W' to wait another $waitTimeout seconds, or 'C' to continue and compress what is available (you may need to manually collect missing files later)" $choice = Read-Host "Enter your choice (W/C)" $choice = $choice.Trim().ToUpper() if ($choice -eq 'C') { Write-Host "[!] Proceeding to compress available files. You may need to manually collect and send missing files to support." -ForegroundColor Yellow break } # Default to waiting if 'W' or nothing/invalid entered Write-Host "[+] Waiting another $waitTimeout seconds for files to be ready..." -ForegroundColor Yellow $startTime = Get-Date do { $notReady = @() foreach ($file in $RequiredFiles) { $fullPath = Join-Path $LogFolder $file if (-not (Test-FileReady $fullPath)) { $notReady += $file } } if ($notReady.Count -gt 0) { Write-Host ("[+] Waiting for files: {0}" -f ($notReady -join ', ')) -ForegroundColor Yellow Start-Sleep -Seconds $waitInterval } } while ($notReady.Count -gt 0 -and ((Get-Date) - $startTime).TotalSeconds -lt $waitTimeout) } Write-Host '[+] Compressing logs to ZIP archive...' -ForegroundColor $ProgressColor $compressSuccess = $false $compressAttempts = 0 do { try { if (Test-Path $ZipFile) { Remove-Item $ZipFile -Force } Compress-Archive -Path $LogFolder -DestinationPath $ZipFile -Force $compressSuccess = $true } catch { $compressAttempts++ $errMsg = $_.Exception.Message if ($errMsg -like '*because it is being used by another process*') { Write-Host ("[+] Waiting for files to be released by other processes before compressing (attempt $compressAttempts)...") -ForegroundColor Yellow Start-Sleep -Seconds 2 } else { throw $_ } } } while (-not $compressSuccess -and $compressAttempts -lt 10) if (-not $compressSuccess) { Write-Host '[!] Failed to compress logs after multiple attempts.' -ForegroundColor Red } else { Write-Host "[+] Log collection complete. Please provide the file: $ZipFile to support." -ForegroundColor Yellow } # Ask for to manually Compress and collect Azure AD Connect Provisioning Agent folder $aadProvFolder = "C:\ProgramData\Microsoft\Azure AD Connect Provisioning Agent" write-host '' Write-Host "[*] Please manually compress and share the Azure AD Connect Provisioning Agent folder: $aadProvFolder" -ForegroundColor Yellow } Export-ModuleMember -Function Start-ProvAgentLogCollection |