Entra / Microsoft 365 ยท Conditional Access
Report conditional access policies
A script to show how to report conditional access policy settings.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -NoWelcome -Scopes User.Read.All, Policy.Read.All, Group.Read.All, Agreement.Read.All, Application.Read.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
[string]$RunDate = Get-Date -format "dd-MMM-yyyy HH:mm:ss"$Version = "1.0"$CSVOutputFile = "c:\temp\CAPoliciesReport.CSV"$ReportFile = "c:\temp\CAPoliciesReport.html"Connect-MgGraph -NoWelcome -Scopes User.Read.All, Policy.Read.All, Group.Read.All, Agreement.Read.All, Application.Read.AllWrite-Host 'Finding conditional access policies'[array]$CAPolicies = Get-MgIdentityConditionalAccessPolicy | Sort-Object displayNameIf (!($CAPolicies)) {Write-Host "Unable to retrieve conditional access policies - exiting"; break}# Hash lookup table for service principals$HashAppLookup = @{}[array]$SPs = Get-MgServicePrincipal -AllForEach ($SP in $SPs) {$HashAppLookup.Add($SP.AppId, $SP.displayName)}# Hash lookup table for admin roles$HashAdminRoles = @{}[array]$AdminRoles = Get-MgDirectoryRoleTemplate -All | Select-Object Id, DisplayName | Sort-Object IdForEach ($Role in $AdminRoles) {$HashAdminRoles.Add($Role.Id, $Role.DisplayName)}# Loop through the CA policies and extract the values for each before generating a report$Report = [System.Collections.Generic.List[Object]]::new()[int]$i = 0ForEach ($Policy in $CAPolicies) {$i++Write-Host ("Processing conditional access polucy {0} ({1}/{2})" -f $Policy.displayName, $i, $CAPolicies.Count)[array]$Conditions = $Policy | Select-Object -ExpandProperty conditions[array]$SessionControls = $Policy | Select-Object -ExpandProperty sessionControls[array]$GrantControls = $Policy | Select-Object -ExpandProperty grantControls[array]$Applications = $Conditions | Select-Object -ExpandProperty Applications# Application filter[string]$ApplicationFilterMode = $Null; [string]$ApplicationFilterRule = $Null[array]$ApplicationFilter = $Applications | Select-Object -ExpandProperty ApplicationFilter$ApplicationFilterMode = $ApplicationFilter.Mode$ApplicationFilterRule = $ApplicationFilter.Rule# Process app exclusions[string]$ExcludedAppOutput = $Null; [string]$IncludedAppOutput = $NullIf ($Applications.excludeApplications) {[array]$ExcludeAppNames = $NullForEach ($ExcludeApp in $Applications.excludeApplications) {$ExcludeAppName = $HashAppLookup[$ExcludeApp]$ExcludeAppNames += $ExcludeAppName}[string]$ExcludedAppOutput = ($ExcludeAppNames -join ", ")}# Process app inclusionsIf ($Applications.includeApplications) {[array]$IncludeAppNames = $Null;ForEach ($IncludeApp in $Applications.includeApplications) {$IncludeAppName = $HashAppLookup[$IncludeApp]$IncludeAppNames += $IncludeAppName}[string]$IncludedAppOutput = ($IncludeAppNames -join ", ")}[string]$IncludeAuthenticationContextClassReferences = $Applications.IncludeAuthenticationContextClassReferences[string]$IncludeUserActions = $Applications.IncludeUserActions[string]$ServicePrincipalRiskLevels = $Conditions.ServicePrincipalRiskLevels[string]$SignInRiskLevels = $Conditions.SignInRiskLevels[string]$UserRiskLevels = $Conditions.UserRiskLevels# Client app types[string]$ClientAppTypes = ($Conditions.ClientAppTypes -join ", ")# Client applications[array]$ClientApplications = $Conditions | Select-Object -ExpandProperty ClientApplications[array]$ExcludedClientApps = $Null; [string]$ExcludedClientAppNames = $Null[array]$IncludedClientApps = $Null; [string]$IncludedClientAppNames = $NullIf ($ClientApplications.ExcludeServicePrincipals) {ForEach ($ClientApp in $ClientApplications.ExcludeServicePrincipals) {$ClientAppName = $HashLookUp[$ClientApp]$ExcludedClientApps += $ClientAppName}$ExcludedClientAppNames = $ExcludedClientApps -join ", "}If ($ClientApplications.IncludeServicePrincipals) {ForEach ($ClientApp in $ClientApplications.IncludeServicePrincipals) {$ClientAppName = $HashLookUp[$ClientApp]$IncludedClientApps += $ClientAppName}$IncludedClientAppNames = $IncludedClientApps -join ", "}[array]$Devices = $Conditions | Select-Object -ExpandProperty Devices[string]$DeviceFilterMode = $Devices.DeviceFilter.Mode[string]$DeviceFilterRule = $Devices.DeviceFilterRule[array]$Locations = $Conditions | Select-Object -ExpandProperty Locations# Process locations - included and excluded[string]$IncludeLocationsOutput = $Null; [array]$IncludeLocationNames = $NullIf ($Locations.IncludeLocations -eq 'All') {[string]$IncludeLocationsOutput = 'All'} Else {ForEach ($Location in $Locations.IncludeLocations) {$IncludeLocationName = (Get-MgIdentityConditionalAccessNamedLocation -NamedLocationId $Location).DisplayName$IncludeLocationNames += $IncludeLocationName}[string]$IncludeLocationsOutput = ($IncludeLocationNames -join ", ")}[string]$ExcludeLocationsOutput = $Null; [array]$ExcludeLocationNames = $NullIf ($Locations.ExcludeLocations -eq 'All') {[string]$ExcludeLocationsOutput = 'All'} Else {ForEach ($Location in $Locations.ExcludeLocations) {$ExcludeLocationName = (Get-MgIdentityConditionalAccessNamedLocation -NamedLocationId $Location).DisplayName$ExcludeLocationNames += $ExcludeLocationName}[string]$ExcludeLocationsOutput = ($ExcludeLocationNames -join ", ")}# Process platforms[array]$Platforms = $Conditions | Select-Object -ExpandProperty Platforms[array]$ExcludedPlatforms = $Platforms.ExcludePlatforms[array]$IncludedPlatforms = $Platforms.IncludePlatforms[string]$IncludedPlatformsOutput = $IncludedPlatforms -join ", "[string]$ExcludedPlatformsOutput = $ExcludedPlatforms -join ", "# Process users, groups, and roles[array]$Users = $Conditions | Select-Object -ExpandProperty Users[array]$ExcludedRoles = $Users.ExcludeRoles[array]$IncludedRoles = $Users.IncludeRoles[array]$ExcludedUsers = $Users.ExcludeUsers[array]$IncludedUsers = $Users.IncludeUsers[array]$IncludedGroups = $Users.IncludeGroups[array]$ExcludedGroups = $Users.ExcludeGroups$ExcludeGuests = $Conditions.Users.ExcludeGuestsOrExternalUsers | Select-Object -ExpandProperty GuestOrExternalUserTypes$IncludeGuests = $Conditions.Users.IncludeGuestsOrExternalUsers | Select-Object -ExpandProperty GuestOrExternalUserTypes$ExcludeGuestsOutput = $null$IncludeGuestsOutput = $null# Excluded GuestsIf ($ExcludeGuests) {[array]$GuestTypes = $nullForEach ($Guest in $ExcludeGuests) {Switch ($Guest) {"internalGuest" {$GuestType = "Local Guest users" }"b2bCollaborationGuest" {$GuestType = "B2B Collaboration guest users"}"b2bCollaborationMember" {$GuestType = "B2B Collaboration member users"}"b2bDirectConnectUser" {$GuestType = "B2B Direct connect users"}"otherExternalUser" {$GuestType = "Other external users"}"serviceProvider" {$GuestType = "Service Provider users"}}$GuestTypes += $GuestType}$ExcludeGuestsOutput = $GuestTypes -join ", "}# included guestsIf ($IncludeGuests) {[array]$GuestTypes = $nullForEach ($Guest in $IncludeGuests) {Switch ($Guest) {"internalGuest" {$GuestType = "Local Guest users" }"b2bCollaborationGuest" {$GuestType = "B2B Collaboration guest users"}"b2bCollaborationMember" {$GuestType = "B2B Collaboration member users"}"b2bDirectConnectUser" {$GuestType = "B2B Direct connect users"}"otherExternalUser" {$GuestType = "Other external users"}"serviceProvider" {$GuestType = "Service Provider users"}}$GuestTypes += $GuestType}$IncludeGuestsOutput = $GuestTypes -join ", "}# Included groups[string]$IncludedGroupsOutput = $nullIf ($IncludedGroups) {[array]$IncludedGroupNames = $nullForEach ($GroupId in $IncludedGroups) {$GroupName = (Get-MgGroup -GroupId $GroupId).DisplayName$IncludedGroupNames += $GroupName}$IncludedGroupsOutput = $IncludedGroupNames -join ", "}# Excluded groups[string]$ExcludedGroupsOutput = $nullIf ($ExcludedGroups) {[array]$ExcludedGroupNames = $nullForEach ($GroupId in $ExcludedGroups) {$GroupName = (Get-MgGroup -GroupId $GroupId).DisplayName$ExcludedGroupNames += $GroupName}$ExcludedGroupsOutput = $ExcludedGroupNames -join ", "}# Excluded admin roles[string]$ExcludedRolesOutput = $nullIf ($ExcludedRoles) {[array]$ExcludedRoleNames = $nullForEach ($Role in $ExcludedRoles) {$RoleName = $HashAdminRoles[$Role]$ExcludedRoleNames += $RoleName}$ExcludedRolesOutput = $ExcludedRoleNames -join ", "}# Included admin roles[string]$IncludedRolesOutput = $nullIf ($IncludedRoles) {[array]$IncludedRoleNames = $nullForEach ($Role in $IncludedRoles) {$RoleName = $HashAdminRoles[$Role]$IncludedRoleNames += $RoleName}$IncludedRolesOutput = $IncludedRoleNames -join ", "}# Included users - the Regex check for a GUID is to make sure that we don't try to use values like GuestsOrExternalUsers with Get-MgUser[string]$IncludedUsersOutput = $nullIf ($IncludedUsers) {[array]$IncludedUserNames = $NullIf ($IncludedUsers[0] -match("^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$")) {ForEach ($U in $IncludedUsers) {$UserName = (Get-MgUser -UserId $U).UserPrincipalName$IncludedUserNames += $UserName}$IncludedUsersOutput = $IncludedUserNames -join ", "} Else {$IncludedUsersOutput = $IncludedUsers[0]}}# Excluded users[string]$ExcludedUsersOutput = $nullIf ($ExcludedUsers) {[array]$ExcludedUserNames = $NullForEach ($U in $ExcludedUsers) {$UserName = (Get-MgUser -UserId $U).UserPrincipalName$ExcludedUserNames += $UserName}$ExcludedUsersOutput = $ExcludedUserNames -join ", "}If ($Policy.State -eq 'enabledForReportingButNotEnforced') {$PolicyState = "Report only"} Else {$PolicyState = $Policy.State}# Session Controls$AppRestrictions = $SessionControls | Select-Object -ExpandProperty ApplicationEnforcedRestrictions$AppRestrictionsEnabled = $AppRestrictions.IsEnabled$CloudAppSecurity = $SessionControls | Select-Object -ExpandProperty CloudAppSecurity$PersistentBrowser = $SessionControls | Select-Object -ExpandProperty PersistentBrowser$SignInFrequency = $SessionControls | Select-Object -ExpandProperty SignInFrequency# Grant controls$AuthenticationStrength = $GrantControls | Select-Object -ExpandProperty AuthenticationStrength$BuiltInControls = $GrantControls | Select-Object -ExpandProperty builtInControls$GrantOperator = $GrantControls | Select-Object -ExpandProperty Operator$TermsOfUse = $GrantControls | Select-Object -ExpandProperty TermsOfUse# Output the data$DataLine = [PSCustomObject][Ordered]@{'Policy' = $Policy.ID'Policy name' = $Policy.displayName'State' = $PolicyState'Created' = $Policy.CreatedDateTime'Last modified' = $Policy.ModifiedDateTime'Included apps' = $IncludedAppOutput'Excluded apps' = $ExcludedAppOutput'App filter mode' = $ApplicationFilterMode'App filter rule' = $ApplicationFilterRule'Client app types' = $ClientAppTypes'Included client apps' = $IncludedClientAppNames'Excluded client apps' = $ExcludedClientAppNames'Included Locations' = $IncludeLocationsOutput'Excluded locations' = $ExcludeLocationsOutput'Included users' = $IncludedUsersOutput'Excluded users' = $ExcludedUsersOutput'Included groups' = $IncludedGroupsOutput'Excluded groups' = $ExcludedGroupsOutput'Included roles' = $IncludedRolesOutput'Excluded roles' = $ExcludedRolesOutput'Include guests' = $IncludeGuestsOutput'Exclude guests' = $ExcludeGuestsOutput'User risk levels' = $UserRiskLevels'Sign in risk levels' = $SignInRiskLevels'SP risk levels' = $ServicePrincipalRiskLevels'Auth context' = $IncludeAuthenticationContextClassReferences'Include User actions' = $IncludeUserActions'Included platforms' = $IncludedPlatformsOutput'Excluded platforms' = $ExcludedPlatformsOutput'Device filter mode' = $DeviceFilterMode'Device filter rule' = $DeviceFilterRule'App Restrictions' = $AppRestrictionsEnabled'Cloud App Security' = $CloudAppSecurity.IsEnabled'CAS type' = $CloudAppSecurity.Type'Persistent browser' = $PersistentBrowser.IsEnabled'Browser mode' = $PersistentBrowser.Mode'Sign in frequency' = $SignInFrequency.IsEnabled'Sign in frequency auth' = $SignInFrequency.AuthenticationType'Sign in frequency interval' = $SignInFrequency.FrequencyInterval'Sign in frequency type' = $SignInFrequency.Type'Sign in frequency value' = $SignInFrequency.Value'Authentication strength' = $AuthenticationStrength.displayName'Auth strength description' = $AuthenticationStrength.description'Built in controls' = $BuiltInControls'Grant operator' = $GrantOperator'Terms of use' = $TermsOfUse}$Report.Add($DataLine)}# Now to generate a HTML reportWrite-Host "Generating report..."$OrgName = (Get-MgOrganization).DisplayName# First, define the header.$HTMLHead="<html><style>BODY{font-family: Arial; font-size: 8pt;}H1{font-size: 22px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}H2{font-size: 18px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}H3{font-size: 16px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}TABLE{border: 1px solid black; border-collapse: collapse; font-size: 8pt;}TH{border: 1px solid #969595; background: #dddddd; padding: 5px; color: #000000;}TD{border: 1px solid #969595; padding: 5px; }td.enabled{background: #B7EB83;}td.disabled{background: #E3242B;}</style><body><div align=center><p><h1>Conditional Access Policies Report</h1></p><p><h2><b>For the " + $Orgname + " tenant</b></h2></p><p><h3>Generated: " + $RunDate + "</h3></p></div>"# This section highlights whether a conditional access policy is enabled or disabled in the summary.# Idea from https://stackoverflow.com/questions/37662940/convertto-html-highlight-the-cells-with-special-values# First, convert the CA Policies report to HTML and then import it into an XML structure[array]$CAPoliciesTable = $Report | Select-Object 'Policy Name', 'Policy', State, 'Created', 'Last Modified'$HTMLTable = $CAPoliciesTable | ConvertTo-Html -Fragment[xml]$XML = $HTMLTable# Create an attribute class to use, name it, and append to the XML table attributes$TableClass = $XML.CreateAttribute("class")$TableClass.Value = "State"$XML.table.Attributes.Append($TableClass) | Out-Null# Conditional formatting for the table rows. The number of available units is in table row 6, so we update td[5]ForEach ($TableRow in $XML.table.SelectNodes("tr")) {# each TR becomes a member of class "tablerow"$TableRow.SetAttribute("class","tablerow")## If row has TD check the state of the policyIf (($TableRow.td) -and ([string]$TableRow.td[2] -eq 'enabled')) {## tag the TD with eirher the color for "warn" or "pass" defined in the heading$TableRow.SelectNodes("td")[2].SetAttribute("class","enabled")} ElseIf (($TableRow.td) -and ([string]$TableRow.td[2] -eq 'disabled')) {$TableRow.SelectNodes("td")[2].SetAttribute("class","disabled")}}# Wrap the output table with a div tag$HTMLBody = [string]::Format('<div class="tablediv">{0}</div>',$XML.OuterXml)# Add separate section for each policy[string]$HTMLPolicySeparator = "<p><h2>Details of Conditional Access Policies</h2></p>"[string]$HTMLPolicyOutput = $null# Foreach policy, extract its details and outputForEach ($Policy in $Report) {$HTMLPolicyHeader = "<h3>Policy Settings for <b>" + $Policy.'Policy Name' + "<b></h3><p>"$HTMLPolicyContent = $Policy | `Select-Object 'Authentication strength', 'Auth strength description', 'Included apps', 'Excluded apps', 'Included Locations', 'Excluded locations', `'Included users', 'Excluded users', 'Included groups', 'Excluded groups', 'Included roles', 'Excluded roles', 'Include guests','Exclude guests' | ConvertTo-HTML -Fragment$HTMLPolicyOutput = $HTMLPolicyOutput + $HTMLPolicyHeader + $HTMLPolicyContent + "<p><p>"}# End stuff to output$HTMLTail = "<p>Report created for the " + $OrgName + " tenant on " + $RunDate + "<p>" +"<p>-----------------------------------------------------------------------------------------------------------------------------</p>"+"<p>Number of conditionl access policies found: " + $CAPolicies.Count + "</p>" +"<p>-----------------------------------------------------------------------------------------------------------------------------</p>"+"<p>Entra ID Conditional Access Policies Report<b> " + $Version + "</b>"$HTMLReport = $HTMLHead + $HTMLBody + $HTMLPolicySeparator + $HTMLPolicyOutput + $HTMLtail$HTMLReport | Out-File $ReportFile -Encoding UTF8$Report | Export-Csv -NoTypeInformation $CSVOutputFile -Encoding utf8Write-Host ("HTML format report is available in {0} and CSV file in {1}" -f $ReportFile, $CSVOutputFile)
Attribution
Author
Office365itpros