Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve conditional access tests #722

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e41b541
Add ability to disable cache on Get-MtConditionalAccessPolicy
tdcthosc Mar 3, 2025
ba2a993
Add test decription to Test-MtCaApplicationEnforcedRestriction
tdcthosc Mar 3, 2025
18bfd21
Add links to test description Test-MtCaApplicationEnforcedRestriction
tdcthosc Mar 3, 2025
ee8ea6f
Add test result to Test-MtCaApplicationEnforcedRestriction
tdcthosc Mar 3, 2025
e11b40a
Add additional conditions to Test-MtCaDeviceCodeFlow
tdcthosc Mar 3, 2025
95bd33f
Added support for allow grants in Test-MtCaDeviceCodeFlow
tdcthosc Mar 3, 2025
58daa0b
Added support for networks in Test-MtCaDeviceCodeFlow
tdcthosc Mar 3, 2025
cd4103b
Catch errors if user no longer exists in Test-MtCaGap upon resolve di…
tdcthosc Mar 3, 2025
3bfcc9b
Catch errors if group no longer exists in Test-MtCaGap
tdcthosc Mar 3, 2025
e1029d3
Add role display name lookup in Test-MtCaGap
tdcthosc Mar 3, 2025
2d5ccee
Add application object display name lookup in Test-MtCaGap
tdcthosc Mar 3, 2025
d0f2999
Add service principal display name lookup to Test-MtCaGap
tdcthosc Mar 3, 2025
5dd242d
Add location object display name lookup to Test-MtCaGap
tdcthosc Mar 3, 2025
9c7d703
Platform does not require lookup, but adjusted to same foreach logic
tdcthosc Mar 3, 2025
ca3a75a
Adjusted markdown when listing excluded objects
tdcthosc Mar 3, 2025
34e7165
Bugfix for Test-MtCaGap when collecting platforms
tdcthosc Mar 3, 2025
c8681ec
Remove unused parameter on Add-MtTestResultDetail in Test-MtCaGroupsR…
tdcthosc Mar 3, 2025
00b3053
Added markdown for Test-MtCaGroupsRestricted
tdcthosc Mar 3, 2025
9e15910
Ease on the exclamation mark
tdcthosc Mar 3, 2025
d502e56
Remove misplaced variable assignment
tdcthosc Mar 3, 2025
5e557a4
Add test description to Test-MtCaMfaForAdminManagement
tdcthosc Mar 3, 2025
e0b9d85
Add possibility of having selecting the individual roles as specified…
tdcthosc Mar 3, 2025
fda8a1d
Added MicrosoftAdminPortals as an option for application selection in…
tdcthosc Mar 3, 2025
fc24aba
Unfortunately running in parallel can cause issues in some circumstan…
tdcthosc Mar 3, 2025
a5e849b
Added additional logic to DisableCache parameter of Get-MtConditional…
tdcthosc Mar 4, 2025
05d049d
Removed redundant check for network locations also being blocked
tdcthosc Mar 5, 2025
b825e89
Detect if network location is specified it must be configured for all…
tdcthosc Mar 5, 2025
8b3d068
Add display of conditional access policies that does not sufficiently…
tdcthosc Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions powershell/public/Get-MtConditionalAccessPolicy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
#>
function Get-MtConditionalAccessPolicy {
[CmdletBinding()]
param()
param(
# Specify if this request should skip cache and go directly to Graph.
[Parameter(Mandatory = $false)]
[Switch]$DisableCache
)

Write-Verbose -Message "Getting conditional access policies."

return Invoke-MtGraphRequest -RelativeUri 'identity/conditionalAccess/policies' -ApiVersion beta
return Invoke-MtGraphRequest -RelativeUri 'identity/conditionalAccess/policies' -ApiVersion beta -DisableCache:$DisableCache

}
12 changes: 12 additions & 0 deletions powershell/public/Test-MtCaApplicationEnforcedRestriction.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ function Test-MtCaApplicationEnforcedRestriction {

$policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" }

$testDescription = "
Microsoft recommends blocking or limiting access to SharePoint, OneDrive, and Exchange content from unmanaged devices.

See [Use application enforced restrictions for unmanaged devices - Microsoft Learn](https://aka.ms/CATemplatesAppRestrictions)"
$testResult = "These conditional access policies enforce restrictions for unmanaged devices:`n`n"

$result = $false
foreach ($policy in $policies) {
if ( $policy.conditions.users.includeUsers -eq "All" `
Expand All @@ -35,11 +41,17 @@ function Test-MtCaApplicationEnforcedRestriction {
) {
$result = $true
$currentresult = $true
$testResult += " - [$($policy.displayname)](https://entra.microsoft.com/#view/Microsoft_AAD_ConditionalAccess/PolicyBlade/policyId/$($($policy.id))?%23view/Microsoft_AAD_ConditionalAccess/ConditionalAccessBlade/~/Policies?=)`n"
} else {
$currentresult = $false
}
Write-Verbose "$($policy.displayName) - $currentresult"
}

if ($result -eq $false) {
$testResult = "There was no conditional access policy enforcing restrictions for unmanaged devices."
}
Add-MtTestResultDetail -Description $testDescription -Result $testResult

return $result
}
18 changes: 13 additions & 5 deletions powershell/public/Test-MtCaDeviceCodeFlow.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ function Test-MtCaDeviceCodeFlow {
[OutputType([bool])]
param ()

$policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" }
$policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq 'enabled' -and $_.conditions.authenticationFlows.transferMethods -contains 'deviceCodeFlow' }
$policiesResult = New-Object System.Collections.ArrayList
$result = $false

foreach ($policy in $policies) {
if ( $policy.conditions.authenticationFlows.transfermethods -eq "deviceCodeFlow")
{
if ($policy.conditions.users.includeUsers -eq 'All' `
-and $policy.conditions.clientAppTypes -eq 'all' `
-and ( `
($policy.grantcontrols.builtincontrols -contains 'block' -and (-not $policy.conditions.locations -or $policy.conditions.locations.includeLocations -eq 'All')) -or `
($policy.grantControls.builtInControls -contains 'compliantDevice' -or $policy.grantControls.builtInControls -contains 'domainJoinedDevice' )
)
) {
$result = $true
$currentresult = $true
$policiesResult.Add($policy) | Out-Null
Expand All @@ -38,9 +43,12 @@ function Test-MtCaDeviceCodeFlow {
}

if ( $result ) {
$testResult = "Well done! The following conditional access policies are targeting the Device Code authentication flow:`n`n%TestResult%"
$testResult = "Well done! The following conditional access policies sufficiently cover Device Code authentication flow:`n`n%TestResult%"
} elseif ( $policies ) {
$policiesResult = $policies
$testResult = "None of the following conditional access policies sufficiently cover Device Code authentication flow:`n`n%TestResult%"
} else {
$testResult = "No conditional access policy found that targets the Device Code authentication flow."
$testResult = 'No conditional access policy found that targets the Device Code authentication flow.'
}

Add-MtTestResultDetail -Result $testResult -GraphObjects $policiesResult -GraphObjectType ConditionalAccess
Expand Down
80 changes: 54 additions & 26 deletions powershell/public/Test-MtCaGap.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function Get-RelatedPolicy {
foreach ($obj in $Arr) {
# Check if the excluded object is present in the policy
if ($obj.ExcludedObjects -contains $ObjName) {
$result += " - Excluded in policy '$($obj.PolicyName)'`n`n"
$result += " > Excluded in policy '$($obj.PolicyName)'`n"
}
}

Expand Down Expand Up @@ -144,8 +144,8 @@ function Test-MtCaGap {
$_.Conditions.ClientApplications.IncludeServicePrincipals | ForEach-Object { $includedServicePrincipals.Add($_) | Out-Null }
$_.Conditions.Locations.ExcludeLocations | ForEach-Object { $excludedLocations.Add($_) | Out-Null }
$_.Conditions.Locations.IncludeLocations | ForEach-Object { $includedLocations.Add($_) | Out-Null }
$_.Conditions.Locations.Platforms | ForEach-Object { $excludedPlatforms.Add($_) | Out-Null }
$_.Conditions.Locations.Platforms | ForEach-Object { $includedPlatforms.Add($_) | Out-Null }
$_.Conditions.Platforms.ExcludePlatforms | ForEach-Object { $excludedPlatforms.Add($_) | Out-Null }
$_.Conditions.Platforms.IncludePlatforms | ForEach-Object { $includedPlatforms.Add($_) | Out-Null }

# Create a mapping for each policy with excluded objects
[System.Collections.ArrayList]$allExcluded = $_.Conditions.Users.ExcludeUsers + `
Expand All @@ -154,7 +154,7 @@ function Test-MtCaGap {
$_.Conditions.Applications.ExcludeApplications + `
$_.Conditions.ClientApplications.ExcludeServicePrincipals + `
$_.Conditions.Locations.ExcludeLocations + `
$_.Conditions.Locations.Platforms
$_.Conditions.Platforms.ExcludePlatforms
# Create the mapping
$mapping = [PSCustomObject]@{
PolicyName = $_.DisplayName
Expand Down Expand Up @@ -194,59 +194,87 @@ function Test-MtCaGap {
# Add user objects to results
if ($differencesUsers.Count -ne 0) {
$testResult = "The following user objects did not have a fallback:`n`n"
$differencesUsers | ForEach-Object {
$DisplayName = Invoke-MtGraphRequest -RelativeUri "users/$_" -Select displayName | Select-Object -ExpandProperty displayName
$testResult += " - $_ ($DisplayName)`n`n" # Add Name?
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
ForEach ($Object in $differencesUsers) {
Try {
$DisplayName = (Invoke-MtGraphRequest -RelativeUri 'users' -ApiVersion v1.0 -UniqueId $Object -ErrorAction Stop).displayName
} Catch {
$DisplayName = "${Object} (Unable to resolve GUID)"
}
$testResult += "`n User: ${DisplayName}`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $Object
}
}
# Add group objects to results
if ($differencesGroups.Count -ne 0) {
$testResult += "The following group objects did not have a fallback:`n`n"
$differencesGroups | ForEach-Object {
$DisplayName = Invoke-MtGraphRequest -RelativeUri "groups/$_" -Select displayName | Select-Object -ExpandProperty displayName
$testResult += " - $_ ($DisplayName)`n`n" # Add Name?
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
ForEach ($Object in $differencesGroups) {
Try {
$DisplayName = (Invoke-MtGraphRequest -RelativeUri 'groups' -ApiVersion v1.0 -UniqueId $Object -ErrorAction Stop).displayName
} Catch {
$DisplayName = "${Object} (Unable to resolve GUID)"
}
$testResult += "`n Group: ${DisplayName}`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $Object
}
}
# Add role objects to results
if ($differencesRoles.Count -ne 0) {
$testResult += "The following role objects did not have a fallback:`n`n"
$differencesRoles | ForEach-Object {
$testResult += " - $_`n`n"
ForEach ($Object in $differencesRoles) {
Try {
$DisplayName = (Invoke-MtGraphRequest -RelativeUri 'directoryRoles' -ApiVersion v1.0 -UniqueId $Object -ErrorAction Stop).displayName
} Catch {
$DisplayName = "${Object} (Unable to resolve GUID)"
}
$testResult += "`n Role: ${DisplayName}`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
}
}
# Add application objects to results
if ($differencesApplications.Count -ne 0) {
$testResult += "The following application objects did not have a fallback:`n`n"
$differencesApplications | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
ForEach ($Object in $differencesApplications) {
Try {
$DisplayName = (Invoke-MtGraphRequest -RelativeUri 'serviceprincipals' -ApiVersion v1.0 -Filter "appId eq '${Object}'" -ErrorAction Stop).displayName
} Catch {
$DisplayName = "${Object} (Unable to resolve GUID)"
}
$testResult += "`n Application: ${DisplayName}`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $Object
}
}
# Add service principal objects to results
if ($differencesServicePrincipals.Count -ne 0) {
$testResult += "The following service principal objects did not have a fallback:`n`n"
$differencesServicePrincipals | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
ForEach ($Object in $differencesServicePrincipals) {
Try {
$DisplayName = (Invoke-MtGraphRequest -RelativeUri 'serviceprincipals' -ApiVersion v1.0 -UniqueId $Object -ErrorAction Stop).displayName
} Catch {
$DisplayName = "${Object} (Unable to resolve GUID)"
}
$testResult += "`n Service Principal: ${DisplayName}`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $Object
}
}
# Add location objects to results
if ($differencesLocations.Count -ne 0) {
$testResult += "The following location objects did not have a fallback:`n`n"
$differencesLocations | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
ForEach ($Object in $differencesLocations) {
Try {
$DisplayName = (Invoke-MtGraphRequest -RelativeUri 'identity/conditionalAccess/namedLocations' -ApiVersion v1.0 -UniqueId $Object -ErrorAction Stop).displayName
} Catch {
$DisplayName = "${Object} (Unable to resolve GUID)"
}
$testResult += "`n Location: ${DisplayName}`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $Object
}
}
# Add platform objects to results
if ($differencesPlatforms.Count -ne 0) {
$testResult += "The following platform objects did not have a fallback:`n`n"
$differencesPlatforms | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
ForEach ($Object in $differencesPlatforms) {
$testResult += "`n Platform: ${Object}`n"
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $Object
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions powershell/public/Test-MtCaGroupsRestricted.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Security Groups will be used to exclude and include users from Conditional Access Policies. Modify group membership outside of Conditional Access Administrator or other privileged roles can lead to bypassing Conditional Access Policies.

To prevent this, you can protect these groups by using Restricted Management Administrative Units or Role Assignable Groups. Role Assignable Group should be used in combination of assignments to Entra ID roles. Restricted Management Administrative Units should be used to protect groups by restricting management to specific users or groups. This test checks if all groups used in Conditional Access Policies are protected.

See [Restricted management administrative units in Microsoft Entra ID - Microsoft Entra ID | Microsoft Learn](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/admin-units-restricted-management)
4 changes: 2 additions & 2 deletions powershell/public/Test-MtCaGroupsRestricted.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Function Test-MtCaGroupsRestricted {
$result = ($UnrestrictedGroups | Measure-Object).Count -eq 0

if ( $result ) {
$ResultDescription = "Well done! All security groups with assignment in Conditional Access are protected!"
$ResultDescription = "Well done! All security groups with assignment in Conditional Access are protected."
} else {
$ResultDescription = "These security groups with assignments in Conditional Access are not protected by Restricted Management Admin Units or Role Assignable groups."
$ImpactedCaGroups = "`n`n#### Impacted Conditional Access Policies`n`n | Security Group | Condition | Policy name | `n"
Expand All @@ -81,7 +81,7 @@ Function Test-MtCaGroupsRestricted {
}

$resultMarkdown = $ResultDescription + $ImpactedCaGroups
Add-MtTestResultDetail -Description $testDescription -Result $resultMarkdown
Add-MtTestResultDetail -Result $resultMarkdown

return $result
}
1 change: 0 additions & 1 deletion powershell/public/Test-MtCaLicenseUtilization.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ function Test-MtCaLicenseUtilization {
}
}

$testDescription =
$testDescription = "This test checks the utilization of Entra ID $License licenses in the tenant."
$testResult = "Total users entitled for Entra ID $($License): **$($Result.EntitledLicenseCount)**`n`nTotal $License licenses utilized: **$($Result.TotalLicensesUtilized)**"
Add-MtTestResultDetail -Description $testDescription -Result $testResult
Expand Down
26 changes: 23 additions & 3 deletions powershell/public/Test-MtCaMfaForAdminManagement.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,33 @@ function Test-MtCaMfaForAdminManagement {
$policies = Get-MtConditionalAccessPolicy | Where-Object { $_.state -eq "enabled" }
$policiesResult = New-Object System.Collections.ArrayList

$testDescription = "
Microsoft recommends requiring MFA for Azure Management.

See [Require MFA for administrators - Microsoft Learn](https://learn.microsoft.com/entra/identity/conditional-access/howto-conditional-access-policy-admin-mfa)"

$result = $false
foreach ($policy in $policies) {
if ( ( $policy.grantcontrols.builtincontrols -contains 'mfa' `
-or $policy.grantcontrols.authenticationStrength.requirementsSatisfied -contains 'mfa' ) `
-and $policy.conditions.users.includeUsers -eq "All" `
-and ($policy.conditions.users.includeUsers -eq "All" `
-or ("62e90394-69f5-4237-9190-012177145e10" -in $policy.conditions.users.includeRoles `
-and "194ae4cb-b126-40b2-bd5b-6091b380977d" -in $policy.conditions.users.includeRoles `
-and "f28a1f50-f6e7-4571-818b-6a12f2af6b6c" -in $policy.conditions.users.includeRoles `
-and "29232cdf-9323-42fd-ade2-1d097af3e4de" -in $policy.conditions.users.includeRoles `
-and "b1be1c3e-b65d-4f19-8427-f6fa0d97feb9" -in $policy.conditions.users.includeRoles `
-and "729827e3-9c14-49f7-bb1b-9608f156bbb8" -in $policy.conditions.users.includeRoles `
-and "b0f54661-2d74-4c50-afa3-1ec803f12efe" -in $policy.conditions.users.includeRoles `
-and "fe930be7-5e62-47db-91af-98c3a49a38b1" -in $policy.conditions.users.includeRoles `
-and "c4e39bd9-1100-46d3-8c65-fb160da0071f" -in $policy.conditions.users.includeRoles `
-and "9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3" -in $policy.conditions.users.includeRoles `
-and "158c047a-c907-4556-b7ef-446551a6b5f7" -in $policy.conditions.users.includeRoles `
-and "966707d0-3269-4727-9be2-8c3a10f19b9d" -in $policy.conditions.users.includeRoles `
-and "7be44c8a-adaf-4e2a-84d6-ab2649e08a13" -in $policy.conditions.users.includeRoles `
-and "e8611ab8-c189-46e8-94e1-60213ab1f814" -in $policy.conditions.users.includeRoles )) `
-and ("797f4846-ba00-4fd7-ba43-dac1f8f63013" -in $policy.conditions.applications.includeApplications `
-or $policy.conditions.applications.includeApplications -contains "All") `
-or "MicrosoftAdminPortals" -in $policy.conditions.applications.includeApplications `
-or $policy.conditions.applications.includeApplications -contains "All") `
) {
$result = $true
$currentresult = $true
Expand All @@ -50,7 +70,7 @@ function Test-MtCaMfaForAdminManagement {
} else {
$testResult = "No conditional access policy requires multi-factor authentication for azure management resources."
}
Add-MtTestResultDetail -GraphObjects $policiesResult -Result $testResult -GraphObjectType ConditionalAccess
Add-MtTestResultDetail -Description $testDescription -GraphObjects $policiesResult -Result $testResult -GraphObjectType ConditionalAccess

return $result
}
4 changes: 2 additions & 2 deletions powershell/public/Test-MtCaReferencedGroupsExist.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ Function Test-MtCaReferencedGroupsExist {
$Groups = $Policies.conditions.users.includeGroups + $Policies.conditions.users.excludeGroups | Select-Object -Unique

$GroupsWhichNotExist = [System.Collections.Concurrent.ConcurrentBag[psobject]]::new()
$Groups | ForEach-Object -Parallel {
$Groups | ForEach-Object { # Removed -Parallel as it caused errors
$Group = $_
$NotExistedGroup = $using:GroupsWhichNotExist
$NotExistedGroup = $GroupsWhichNotExist # Adjusted after not running in parallel anymore
$GraphQueryResult = Invoke-MtGraphRequest -RelativeUri "groups/$($Group)" -ApiVersion beta -ErrorVariable GraphErrorResult -ErrorAction SilentlyContinue
if ([string]::IsNullOrEmpty($GraphQueryResult)) {
$NotExistedGroup.Add($Group) | Out-Null
Expand Down
Loading