├── .gitignore ├── CAExport-Select.png ├── CaExport-rec.png ├── CaExport-result.png ├── Export-CAPolicyWithRecs.ps1 ├── Export-CaPolicy.ps1 └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.json -------------------------------------------------------------------------------- /CAExport-Select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dougsbaker/CA-Export/4114ba6bc5e31e8821b476047ac32b5f824e868b/CAExport-Select.png -------------------------------------------------------------------------------- /CaExport-rec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dougsbaker/CA-Export/4114ba6bc5e31e8821b476047ac32b5f824e868b/CaExport-rec.png -------------------------------------------------------------------------------- /CaExport-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dougsbaker/CA-Export/4114ba6bc5e31e8821b476047ac32b5f824e868b/CaExport-result.png -------------------------------------------------------------------------------- /Export-CAPolicyWithRecs.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Export Conditional Access Policies with Recommendations. 4 | 5 | .DESCRIPTION 6 | This script exports Conditional Access (CA) policies from Azure AD to an HTML file. 7 | It includes recommendations and checks for each policy to enhance security. 8 | 9 | .EXAMPLE 10 | .\Export-CAPolicyWithRecs.ps1 11 | 12 | This example runs the script and exports all Conditional Access policies with recommendations. 13 | 14 | .NOTES 15 | Author: Douglas Baker 16 | @dougsbaker 17 | Version: 3.0 18 | 19 | 20 | Output report uses open source components for HTML formatting 21 | - bootstrap - MIT License - https://getbootstrap.com/docs/4.0/about/license/ 22 | - fontawesome - CC BY 4.0 License - https://fontawesome.com/license/free 23 | 24 | ############################################################################ 25 | This sample script is not supported under any standard support program or service. 26 | This sample script is provided AS IS without warranty of any kind. 27 | This work is licensed under a Creative Commons Attribution 4.0 International License 28 | https://creativecommons.org/licenses/by-nc-sa/4.0/ 29 | ############################################################################ 30 | 31 | #> 32 | 33 | [CmdletBinding()] 34 | param ( 35 | [Parameter()] 36 | [String]$PolicyID 37 | ) 38 | 39 | $ExportLocation = $PSScriptRoot 40 | if (!$ExportLocation) { $ExportLocation = $PWD } 41 | $FileName = "\CAPolicy.html" 42 | $JsonFileName = "\CAPolicy.json" 43 | $HTMLExport = $true 44 | $JsonExport = $false 45 | 46 | 47 | 48 | 49 | try { 50 | Get-MgIdentityConditionalAccessPolicy -ErrorAction Stop > $null 51 | Write-host "Connected: MgGraph" 52 | } 53 | catch { 54 | Write-host "Connecting: MgGraph" 55 | Try { 56 | #Connect-AzureAD 57 | #Select-MgProfile -Name "beta" 58 | Connect-MgGraph -Scopes 'Policy.Read.All', 'Directory.Read.All', 'Application.Read.All', 'Agreement.Read.All' -nowelcome 59 | } 60 | Catch { 61 | Write-host "Error: Please Install MgGraph Module" -ForegroundColor Yellow 62 | Write-Host "Run: Install-Module Microsoft.Graph" -ForegroundColor Yellow 63 | Exit 64 | } 65 | } 66 | 67 | 68 | $TenantData = Get-MgOrganization 69 | $TenantName = $TenantData.DisplayName 70 | $date = Get-Date 71 | Write-Host "Connected: $TenantName tenant" 72 | $LinkURL = "https://portal.azure.com/#view/Microsoft_AAD_ConditionalAccess/PolicyBlade/policyId/" 73 | 74 | #Collect CA Policy 75 | Write-host "Exporting: CA Policy" 76 | if ($PolicyID) { 77 | $CAPolicy = Get-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $PolicyID 78 | } 79 | else { 80 | $CAPolicy = Get-MgIdentityConditionalAccessPolicy -all -ExpandProperty * 81 | 82 | } 83 | 84 | $TenantData = Get-MgOrganization 85 | $TenantName = $TenantData.DisplayName 86 | $date = Get-Date 87 | 88 | 89 | Write-host "Extracting: Names from Guid's" 90 | #Swap User Guid With Names 91 | #Get Name 92 | $ADUsers = $CAPolicy.Conditions.Users.IncludeUsers 93 | $ADUsers += $CAPolicy.Conditions.Users.IncludeGroups 94 | $ADUsers += $CAPolicy.Conditions.Users.IncludeRoles 95 | $ADUsers += $CAPolicy.Conditions.Users.ExcludeUsers 96 | $ADUsers += $CAPolicy.Conditions.Users.ExcludeGroups 97 | $ADUsers += $CAPolicy.Conditions.Users.ExcludeRoles 98 | 99 | 100 | 101 | # Filter the $AdUsers array to include only valid GUIDs 102 | $ADsearch = $AdUsers | Where-Object { 103 | ([Guid]::TryParse($_, [ref] [Guid]::Empty)) 104 | } 105 | 106 | #users Hashtable 107 | $mgobjects = Get-MgDirectoryObjectById -ids $ADsearch 108 | $mgObjectsLookup = @{} 109 | foreach ($obj in $mgobjects) { 110 | $mgObjectsLookup[$obj.Id] = $obj.AdditionalProperties.displayName 111 | } 112 | 113 | 114 | #Change Guid's 115 | foreach ($policy in $caPolicy) { 116 | # Check if the policy has Conditions and Users and ExcludeUsers properties 117 | if ($policy.Conditions -and $policy.Conditions.Users <#-and $policy.Conditions.Users.ExcludeUsers#>) { 118 | # Loop through each user in ExcludeUsers and replace with displayName if found in $mgObjectsLookup 119 | for ($i = 0; $i -lt $policy.Conditions.Users.ExcludeUsers.Count; $i++) { 120 | $userId = $policy.Conditions.Users.ExcludeUsers[$i] 121 | if ($mgObjectsLookup.ContainsKey($userId)) { 122 | $policy.Conditions.Users.ExcludeUsers[$i] = $mgObjectsLookup[$userId] 123 | } 124 | } 125 | 126 | for ($i = 0; $i -lt $policy.Conditions.Users.IncludeUsers.Count; $i++) { 127 | $userId = $policy.Conditions.Users.IncludeUsers[$i] 128 | if ($mgObjectsLookup.ContainsKey($userId)) { 129 | $policy.Conditions.Users.IncludeUsers[$i] = $mgObjectsLookup[$userId] 130 | } 131 | } 132 | for ($i = 0; $i -lt $policy.Conditions.Users.IncludeGroups.Count; $i++) { 133 | $userId = $policy.Conditions.Users.IncludeGroups[$i] 134 | if ($mgObjectsLookup.ContainsKey($userId)) { 135 | $memberCount = 0 136 | $groupMembers = Get-MgGroupMember -GroupId $userId 137 | $memberCount = $groupMembers | Measure-Object | Select-Object -ExpandProperty Count 138 | $policy.Conditions.Users.IncludeGroups[$i] = $mgObjectsLookup[$userId] + " ($memberCount)" 139 | } 140 | } 141 | for ($i = 0; $i -lt $policy.Conditions.Users.ExcludeGroups.Count; $i++) { 142 | $userId = $policy.Conditions.Users.ExcludeGroups[$i] 143 | if ($mgObjectsLookup.ContainsKey($userId)) { 144 | $memberCount = 0 145 | $groupMembers = Get-MgGroupMember -GroupId $userId 146 | $memberCount = $groupMembers | Measure-Object | Select-Object -ExpandProperty Count 147 | $policy.Conditions.Users.ExcludeGroups[$i] = $mgObjectsLookup[$userId] + " ($memberCount)" 148 | } 149 | } 150 | for ($i = 0; $i -lt $policy.Conditions.Users.IncludeRoles.Count; $i++) { 151 | $userId = $policy.Conditions.Users.IncludeRoles[$i] 152 | if ($mgObjectsLookup.ContainsKey($userId)) { 153 | $policy.Conditions.Users.IncludeRoles[$i] = $mgObjectsLookup[$userId] 154 | } 155 | } 156 | for ($i = 0; $i -lt $policy.Conditions.Users.ExcludeRoles.Count; $i++) { 157 | $userId = $policy.Conditions.Users.ExcludeRoles[$i] 158 | if ($mgObjectsLookup.ContainsKey($userId)) { 159 | $policy.Conditions.Users.ExcludeRoles[$i] = $mgObjectsLookup[$userId] 160 | } 161 | } 162 | } 163 | 164 | } 165 | 166 | #Swap App ID with name 167 | $MGApps = Get-MgServicePrincipal -All 168 | #Hash Table 169 | $MGAppsLookup = @{} 170 | foreach ($obj in $MGApps) { 171 | $MGAppsLookup[$obj.AppId] = "