Troubleshooting¶
Common issues and resolution procedures for the Conditional Access Automation solution.
Deployment Issues¶
Insufficient Permissions Error¶
Symptom:
Cause: Service principal lacks required Graph API permissions.
Resolution: 1. Verify permissions granted:
-
Add missing permissions:
-
Grant admin consent:
Policy Already Exists Error¶
Symptom:
Cause: Attempting to create a policy with a duplicate name.
Resolution: 1. Check existing policies:
Get-MgIdentityConditionalAccessPolicy |
Where-Object { $_.DisplayName -like "CA-FSI-*" } |
Select-Object DisplayName, Id
- Options:
- Use
-Forceparameter to overwrite existing - Delete existing policy first
- Use different policy prefix in config
Key Vault Access Denied¶
Symptom:
Cause: Current identity lacks Key Vault access.
Resolution:
# Grant access to current user
az keyvault set-policy `
--name "<vault-name>" `
--upn "admin@contoso.com" `
--secret-permissions get list set
# Or grant to service principal
az keyvault set-policy `
--name "<vault-name>" `
--object-id "<sp-object-id>" `
--secret-permissions get list
Policy Issues¶
Users Not Being Prompted for MFA¶
Symptom: Users can access AI applications without MFA prompt.
Cause: Multiple possible causes.
Resolution Checklist:
-
Policy State:
-
User Assignment:
- Verify user is in the target group
-
Verify user is not in an exclusion group
-
Application Assignment:
- Verify the application ID is correct
-
Check if using correct enterprise app vs app registration
-
What-If Testing:
MFA Prompt Loops¶
Symptom: User repeatedly prompted for MFA, never able to complete sign-in.
Cause: Conflicting policies or session control issues.
Resolution:
-
Check for conflicting policies:
-
Check session controls:
- Ensure sign-in frequency isn't too short (minimum 1 hour)
-
Check persistent browser settings
-
Temporary workaround:
- Add user to exclusion temporarily
- Investigate with sign-in logs
- Remove from exclusion after fix
Unexpected Blocks¶
Symptom: Legitimate users being blocked from access.
Cause: Overly restrictive conditions or missing exclusions.
Resolution:
-
Check sign-in logs:
-
Review applied policies:
-
Common causes:
- User on unmanaged device (compliant device required)
- Legacy authentication client
- High-risk sign-in detected
- Location-based restrictions
Device Compliance Requirements Failing¶
Symptom: Users with managed devices still blocked for device compliance.
Cause: Device not properly registered or compliance policy not met.
Resolution:
-
Check device status:
-
Verify device compliance:
- Check Intune compliance policies
- Verify device sync status
-
Check compliance policy assignments
-
Temporary workaround:
- Use Microsoft Entra hybrid joined as alternative
- Check if device just needs to sync
Script Issues¶
Module Not Found¶
Symptom:
Cause: Microsoft Graph module not installed or not imported.
Resolution:
# Install module
Install-Module Microsoft.Graph -Scope CurrentUser -Force
# Import specific submodule
Import-Module Microsoft.Graph.Identity.SignIns
# Verify
Get-Command Get-MgIdentityConditionalAccessPolicy
Authentication Token Expired¶
Symptom:
Cause: Cached token expired during long-running operation.
Resolution:
# Disconnect and reconnect
Disconnect-MgGraph
Connect-MgGraph -TenantId "<tenant-id>" -Scopes "Policy.ReadWrite.ConditionalAccess"
# Or use client credentials for automation
$secureSecret = ConvertTo-SecureString "<secret>" -AsPlainText -Force
$credential = New-Object PSCredential("<client-id>", $secureSecret)
Connect-MgGraph -TenantId "<tenant-id>" -ClientSecretCredential $credential
Rate Limiting¶
Symptom:
Cause: Exceeded Graph API rate limits.
Resolution: 1. Add delays between operations:
- Implement retry logic:
$retryCount = 0 $maxRetries = 3 do { try { $result = Invoke-MgGraphRequest -Uri $uri -Method GET break } catch { if ($_.Exception.Response.StatusCode -eq 429) { $retryAfter = $_.Exception.Response.Headers["Retry-After"] Start-Sleep -Seconds ([int]$retryAfter + 1) $retryCount++ } else { throw } } } while ($retryCount -lt $maxRetries)
Compliance Issues¶
Drift Detection False Positives¶
Symptom: Drift alerts for changes that are expected.
Cause: Baseline needs updating after legitimate changes.
Resolution: 1. Verify the change is legitimate 2. Update the baseline:
.\scripts\Export-PolicyBaseline.ps1 `
-TenantId "<tenant-id>" `
-OutputPath "./baselines/baseline.json"
Export-PolicyBaseline.ps1 -OutputPathis a file path, not a directory, and the script does NOT expose a-Forceswitch. The file is overwritten in place each run, so re-running is the supported way to refresh a baseline.
- Add expected changes to ignore list:
Evidence Export Missing Data¶
Symptom: Export doesn't include all expected records.
Cause: Audit log retention or filter issues.
Resolution:
- Check audit log retention:
- Entra ID P1: 30 days
-
Entra ID P2: 30 days (extendable to 2 years with log analytics)
-
Verify date filters:
-
Export to Log Analytics for long-term retention:
- Configure diagnostic settings in Entra ID
- Export to Log Analytics workspace
- Query from workspace for older data
Recovery Procedures¶
Emergency: All Users Blocked¶
If a policy misconfiguration blocks all users:
-
Use break-glass account:
-
If break-glass account also blocked:
- Contact Microsoft Support
- Use Microsoft Entra ID emergency access procedures
Rollback All Policies¶
# Disable all FSI policies
Get-MgIdentityConditionalAccessPolicy |
Where-Object { $_.DisplayName -like "CA-FSI-*" } |
ForEach-Object {
Update-MgIdentityConditionalAccessPolicy `
-ConditionalAccessPolicyId $_.Id `
-State "disabled"
Write-Host "Disabled: $($_.DisplayName)"
}
Restore from Baseline¶
Restore CA policies from a previously exported baseline by re-deploying:
.\scripts\Deploy-CAPolicies.ps1 `
-TenantId "<tenant-id>" `
-ConfigPath "./config/tenant-config.json" `
-TemplateSet "All" `
-EnablePolicies $true
Getting Help¶
If issues persist:
-
Collect diagnostic information:
# Gather CA policy state Get-MgIdentityConditionalAccessPolicy | Where-Object { $_.DisplayName -like "CA-*" } | Select-Object DisplayName, State, Id | ConvertTo-Json -Depth 5 | Out-File "./diagnostics/ca-policies.json" # Check Graph API connectivity Get-MgContext | Select-Object Account, TenantId, Scopes -
Check Microsoft service health:
- https://status.office365.com
-
Entra ID service status
-
Review documentation:
- Microsoft CA documentation
-
Open GitHub issue with:
- Error messages
- Script output
- Policy configurations (sanitized)
Evidence Export Issues¶
Hash Mismatch After Export¶
Symptom:
Cause: The evidence JSON file was modified after export (manual edits, encoding changes, or file transfer corruption).
Resolution: 1. Do not modify evidence files after export — they are tamper-evident by design 2. Re-export from the Dataverse source using the same date range:
.\scripts\Export-CAAComplianceEvidence.ps1 `
-DataverseUrl "https://org.crm.dynamics.com" `
-OutputPath "./evidence" `
-FromDate "2026-01-01" -ToDate "2026-01-31"
Empty Validations Array¶
Symptom: Evidence JSON contains "validations": [] with "recordCount": 0.
Cause: No validation runs occurred within the specified date range.
Resolution:
1. Check the -FromDate and -ToDate parameters — they may be too narrow
2. Verify the daily compliance flow is running:
# Check recent flow runs in Power Automate admin center
# Or query Dataverse directly for recent validation records
Get-CAAValidationResults -DataverseUrl $url -AccessToken $token `
-Table 'fsi_capolicyvalidationhistories' `
-FromDate (Get-Date).AddDays(-7)
Truncated JSON Output¶
Symptom: Evidence JSON file is incomplete or fails to parse (ConvertFrom-Json throws an error).
Cause: The ConvertTo-Json depth was insufficient for nested policy data. The export script uses -Depth 10 by default, but manual JSON manipulation or piping through other tools may truncate the output.
Resolution:
1. Re-run the export script without modifications — it includes -Depth 10 automatically
2. If processing evidence manually, always specify depth:
$json = Get-Content "./evidence/CAA-Evidence-*.json" -Raw | ConvertFrom-Json
$json.metadata # Should contain exportedAt, scope, etc.
Dataverse Authentication Failure¶
Symptom:
Cause: Certificate expired, app registration permissions removed, or managed identity not configured.
Resolution: 1. Verify the app registration certificate is valid:
2. Check Graph API permissions are still granted:Get-MgServicePrincipal -Filter "appId eq '<app-id>'" |
Get-MgServicePrincipalAppRoleAssignment |
Select-Object ResourceDisplayName, AppRoleId
Invoke-RestMethod -Uri "https://org.crm.dynamics.com/api/data/v9.2/WhoAmI" `
-Headers @{ Authorization = "Bearer $token" }
Large Export File¶
Symptom: Evidence JSON file is very large (>100 MB) and slow to process.
Cause: Wide date range capturing months of daily validation data.
Resolution: 1. Filter by specific run ID to export a single scan:
.\scripts\Export-CAAComplianceEvidence.ps1 `
-DataverseUrl "https://org.crm.dynamics.com" `
-OutputPath "./evidence" `
-RunId "specific-run-id"