Skip to content

Commit ff922fd

Browse files
authored
Rename Entra Connect related checks and move from test logic to cmdlets (#1244)
* Rename to match purpose * Rewrite to limit test logic outside of cmdlet * Remove Test-MtDomainsWithSsso from psd1 * Rename correctly * Make sure unconnected Graph does not throw errors
1 parent 3c9cb35 commit ff922fd

9 files changed

Lines changed: 196 additions & 136 deletions

powershell/Maester.psd1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ FunctionsToExport = 'Add-MtTestResultDetail',
160160
'Test-ORCA244', 'Update-MaesterTests', 'Test-MtAppRegistrationOwnersWithoutMFA',
161161
'Test-MtManagementGroupWriteRequirement', 'Test-MtDeviceRegistrationMfaConflict', 'Test-MtVaultSoftDelete',
162162
'Test-MtTenantCreationRestricted', 'Test-MtEntraDeviceJoinRestricted', 'Test-MtSecurityGroupCreationRestricted',
163-
'Test-MtCaApprovedClientApp', 'Test-MtCaAzureDevOps', 'Test-MtDirSyncSoftHardMatching',
163+
'Test-MtCaApprovedClientApp', 'Test-MtCaAzureDevOps', 'Test-MtEntraIDConnectSyncSoftHardMatching', 'Test-MtEntraIDConnectSyncSsso',
164164
'Test-MtExoMoeraMailActivity','Test-MtExoDelicensingResiliency',
165-
'Test-MtLimitOnMicrosoftDomainUsage', 'Test-MtDomainsWithSsso',
165+
'Test-MtLimitOnMicrosoftDomainUsage',
166166
'Test-MtXspmAppRegWithPrivilegedApiAndOwners',
167167
'Test-MtXspmAppRegWithPrivilegedRolesAndOwners',
168168
'Test-MtXspmAppRegWithPrivilegedUnusedPermissions',

powershell/public/maester/entra/Test-MtDomainsWithSsso.ps1

Lines changed: 0 additions & 75 deletions
This file was deleted.

powershell/public/maester/entra/Test-MtDirSyncSoftHardMatching.md renamed to powershell/public/maester/entra/Test-MtEntraIDConnectSyncSoftHardMatching.md

File renamed without changes.

powershell/public/maester/entra/Test-MtDirSyncSoftHardMatching.ps1 renamed to powershell/public/maester/entra/Test-MtEntraIDConnectSyncSoftHardMatching.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
This can lead to unintended consequences, such as mismatching user data.
88
99
.EXAMPLE
10-
Test-MtDirSyncSoftHardMatching
10+
Test-MtEntraIDConnectSyncSoftHardMatching
1111
1212
Returns true if soft and hard matching is blocked / disabled
1313
1414
.LINK
15-
https://maester.dev/docs/commands/Test-MtDirSyncSoftHardMatching
15+
https://maester.dev/docs/commands/Test-MtEntraIDConnectSyncSoftHardMatching
1616
#>
17-
function Test-MtDirSyncSoftHardMatching {
17+
function Test-MtEntraIDConnectSyncSoftHardMatching {
1818
[CmdletBinding()]
1919
[OutputType([bool])]
2020
param()

powershell/public/maester/entra/Test-MtDomainsWithSsso.md renamed to powershell/public/maester/entra/Test-MtEntraIDConnectSyncSsso.md

File renamed without changes.
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<#
2+
.SYNOPSIS
3+
Ensure Microsoft Entra seamless single sign-on is disabled for all domains.
4+
5+
.DESCRIPTION
6+
Microsoft Entra seamless single sign-on (SSSO) provides users with easy access to cloud-based applications by automatically signing them in when they are on their corporate devices connected to the corporate network.
7+
However, if not managed properly, it can introduce security risks, especially if devices are compromised or if there are misconfigurations.
8+
9+
.EXAMPLE
10+
Test-MtEntraIDConnectSyncSsso
11+
12+
Returns true if Microsoft Entra seamless single sign-on is disabled
13+
14+
.LINK
15+
https://maester.dev/docs/commands/Test-MtEntraIDConnectSyncSsso
16+
#>
17+
function Test-MtEntraIDConnectSyncSsso {
18+
[CmdletBinding()]
19+
[OutputType([bool])]
20+
param()
21+
22+
if (-not (Test-MtConnection Graph)) {
23+
Add-MtTestResultDetail -SkippedBecause NotConnectedGraph
24+
return $null
25+
}
26+
$return = $true
27+
28+
Write-Verbose "Checking if Microsoft Entra seamless single sign-on is disabled..."
29+
try {
30+
$organizationConfig = Invoke-MtGraphRequest -RelativeUri "organization"
31+
} catch {
32+
Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_
33+
return $null
34+
}
35+
if ($organizationConfig.onPremisesSyncEnabled -ne $true) {
36+
Add-MtTestResultDetail -SkippedBecause 'Custom' -SkippedCustomReason 'OnPremisesSynchronization is not configured'
37+
return $null
38+
}
39+
# Validate if MDI and MDE data is available
40+
try {
41+
# Check for availability of IdentityLogonEvents table
42+
$params = @{
43+
ApiVersion = "beta"
44+
RelativeUri = "security/runHuntingQuery"
45+
Method = "POST"
46+
ErrorAction = "SilentlyContinue"
47+
Body = (@{"Query" = "IdentityLogonEvents | getschema" } | ConvertTo-Json)
48+
OutputType = "PSObject"
49+
Verbose = $true
50+
}
51+
$IdentityLogonEventsAvailable = ((Invoke-MtGraphRequest @params).results.ColumnName -contains "LogonType")
52+
# Check for availability of DeviceInfo table
53+
$params = @{
54+
ApiVersion = "beta"
55+
RelativeUri = "security/runHuntingQuery"
56+
Method = "POST"
57+
ErrorAction = "SilentlyContinue"
58+
Body = (@{"Query" = "DeviceInfo | getschema" } | ConvertTo-Json)
59+
OutputType = "PSObject"
60+
Verbose = $true
61+
}
62+
$DeviceInfoAvailable = ((Invoke-MtGraphRequest @params).results.ColumnName -contains "DeviceId")
63+
$UnifiedMdiInfoAvailable = $IdentityLogonEventsAvailable -and $DeviceInfoAvailable
64+
} catch {
65+
Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_
66+
return $null
67+
}
68+
69+
if ( $UnifiedMdiInfoAvailable -eq $false) {
70+
Add-MtTestResultDetail -SkippedBecause 'Custom' -SkippedCustomReason 'This test requires availability of Microsoft Defender for Identity and Microsoft Defender for Endpoint to get data from Defender XDR Advanced Hunting tables (IdentityLogonEvents and DeviceInfo).'
71+
return $null
72+
}
73+
74+
try {
75+
$Query = @"
76+
// Get all device info we can find
77+
let devices = (
78+
DeviceInfo
79+
// Search for 14 days
80+
| where TimeGenerated > ago(14d)
81+
// Normalize DeviceName
82+
// --> if it is an IP Address we keep it
83+
// --> If it is not an IP Address we only use the hostname for correlation
84+
| extend DeviceName = iff(ipv4_is_private(DeviceName), DeviceName, tolower(split(DeviceName, ".")[0]))
85+
// Only get interesting data
86+
| distinct DeviceName, OSPlatform, OSVersion, DeviceId, OnboardingStatus, Model, JoinType
87+
);
88+
IdentityLogonEvents
89+
// Get the last 14 days of logon events on Domain Controllers
90+
| where TimeGenerated > ago(14d)
91+
// Search for Seamless SSO events
92+
| where Application == "Active Directory" and Protocol == "Kerberos"
93+
| where TargetDeviceName == "AZUREADSSOACC"
94+
// Save the domain name of the Domain Controller
95+
| extend OnPremisesDomainName = strcat(split(DestinationDeviceName, ".")[-2], ".", split(DestinationDeviceName, ".")[-1])
96+
// Normalize DeviceName
97+
// --> if it is an IP Address we keep it
98+
// --> If it is not an IP Address we only use the hostname for correlation
99+
| extend DeviceName = iff(ipv4_is_private(DeviceName), DeviceName, tolower(split(DeviceName, ".")[0]))
100+
// Only use interesting data and find more info regarding the source device
101+
| distinct AccountUpn, OnPremisesDomainName, DeviceName
102+
| join kind=leftouter devices on DeviceName
103+
| project-away DeviceName1
104+
// Check if Seamless SSO usage is expected
105+
| extend ['Seamless SSO Expected'] = case(
106+
// Cases where we do not expect Seamless SSO to be used
107+
JoinType == "Hybrid Azure AD Join" or
108+
JoinType == "AAD Joined" or
109+
JoinType == "AAD Registered", "No",
110+
// Cases where we do expect Seamless SSO to be used
111+
JoinType == "Domain Joined" or
112+
(OSPlatform startswith "Windows" and toreal(OSVersion) < 10.0) , "Yes",
113+
// Cases that need to be verified
114+
"Unknown (to verify)"
115+
)
116+
| extend Obj = bag_pack(
117+
"AccountUpn", AccountUpn,
118+
"DeviceName", DeviceName,
119+
"OSPlatform", OSPlatform,
120+
"OSVersion", OSVersion,
121+
"OnboardingStatus", OnboardingStatus,
122+
"JoinType", JoinType,
123+
"Seamless SSO Expected", ['Seamless SSO Expected']
124+
)
125+
| summarize JsonArray=make_list(Obj) by OnPremisesDomainName
126+
"@
127+
128+
Write-Verbose "Running KQL query to get domains with Seamless SSO usage"
129+
$DomainsWithSsso = Invoke-MtGraphSecurityQuery -Query $Query -Timespan "P14D"
130+
} catch {
131+
Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_
132+
return $null
133+
}
134+
135+
try {
136+
if ($DomainsWithSsso.Count -gt 0) {
137+
$return = $false
138+
$testResultMarkdown = "At least one domain has sign-ins with Seamless Single SignOn enabled in Entra ID Connect.`n`n%TestResult%"
139+
$result = "| DomainName | AccountUpn | DeviceName | OnboardingStatus | JoinType | Ssso Expected |`n"
140+
$result += "| --- | --- | --- | --- | --- | --- |`n"
141+
foreach ($Domain in $DomainsWithSsso) {
142+
$DomainName = $Domain.OnPremisesDomainName
143+
$Domain.JsonArray | ForEach-Object {
144+
$AccountUpn = $_.AccountUpn
145+
$DeviceName = $_.DeviceName
146+
$OnboardingStatus = $_.OnboardingStatus
147+
$JoinType = $_.JoinType
148+
$SssoExpected = $_.'Seamless SSO Expected'
149+
$result += "| $($DomainName) | $($AccountUpn) | $($DeviceName) | $($OnboardingStatus) | $($JoinType) | $($SssoExpected) |`n"
150+
}
151+
}
152+
$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result
153+
} else {
154+
$testResultMarkdown = "Well done. We found no evidence of domains where Seamless Single SignOn was still enabled in EntraID Connect via the Microsoft Defender for Identity logs."
155+
}
156+
Add-MtTestResultDetail -Result $testResultMarkdown
157+
158+
return $return
159+
} Catch {
160+
Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_
161+
return $null
162+
}
163+
}

tests/Maester/Entra/Test-ConditionalAccessWhatIf.Tests.ps1

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
BeforeAll {
2-
$EntraIDPlan = Get-MtLicenseInformation -Product 'EntraID'
3-
$RegularUsers = Get-MtUser -Count 5 -UserType 'Member'
4-
$AdminUsers = Get-MtUser -Count 5 -UserType 'Admin'
5-
$EmergencyAccessUsers = Get-MtUser -Count 5 -UserType 'EmergencyAccess'
6-
# Remove emergency access users from regular users
7-
$RegularUsers = $RegularUsers | Where-Object { $_.id -notin $EmergencyAccessUsers.id }
8-
# Remove emergency access users from admin users
9-
$AdminUsers = $AdminUsers | Where-Object { $_.id -notin $EmergencyAccessUsers.id }
10-
Write-Verbose "EntraIDPlan: $EntraIDPlan"
11-
Write-Verbose "RegularUsers: $($RegularUsers.id)"
12-
Write-Verbose "AdminUsers: $($AdminUsers.id)"
1+
BeforeDiscovery {
2+
try {
3+
$EntraIDPlan = Get-MtLicenseInformation -Product 'EntraID'
4+
$RegularUsers = Get-MtUser -Count 5 -UserType 'Member'
5+
$AdminUsers = Get-MtUser -Count 5 -UserType 'Admin'
6+
$EmergencyAccessUsers = Get-MtUser -Count 5 -UserType 'EmergencyAccess'
7+
# Remove emergency access users from regular users
8+
$RegularUsers = $RegularUsers | Where-Object { $_.id -notin $EmergencyAccessUsers.id }
9+
# Remove emergency access users from admin users
10+
$AdminUsers = $AdminUsers | Where-Object { $_.id -notin $EmergencyAccessUsers.id }
11+
Write-Verbose "EntraIDPlan: $EntraIDPlan"
12+
Write-Verbose "RegularUsers: $($RegularUsers.id)"
13+
Write-Verbose "AdminUsers: $($AdminUsers.id)"
14+
} catch {
15+
$EntraIDPlan = "NotConnected"
16+
}
1317
}
1418

1519

Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,13 @@
11
BeforeDiscovery {
2-
3-
}
4-
5-
Describe "Maester/Entra" -Tag "EntraIdConnect", "Entra", "Graph", "Security" {
6-
7-
BeforeAll {
2+
try {
83
$EntraIDPlan = Get-MtLicenseInformation -Product "EntraID"
4+
} catch {
5+
$EntraIDPlan = "NotConnected"
96
}
7+
}
108

11-
It "MT.1084: Seamless Single SignOn should be disabled for all domains in EntraID Connect servers. See https://maester.dev/docs/tests/MT.1084" -Tag "MT.1084" -Skip:( $EntraIDPlan -ne "P2" ) {
12-
13-
if ( $UnifiedMdiInfoAvailable -eq $false) {
14-
Add-MtTestResultDetail -SkippedBecause 'Custom' -SkippedCustomReason 'This test requires availability of Microsoft Defender for Identity and Microsoft Defender for Endpoint to get data from Defender XDR Advanced Hunting tables (IdentityLogonEvents and DeviceInfo).'
15-
return $null
16-
}
17-
18-
try {
19-
$DomainsWithSsso = Test-MtDomainsWithSsso
20-
} catch {
21-
Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_
22-
return $null
23-
}
24-
25-
if ($return -or $DomainsWithSsso.Count -eq 0) {
26-
$testResultMarkdown = "Well done. We found no evidence of domains where Seamless Single SignOn was still enabled in EntraID Connect via the Microsoft Defender for Identity logs."
27-
} else {
28-
$testResultMarkdown = "At least one domain has sign-ins with Seamless Single SignOn enabled in Entra ID Connect.`n`n%TestResult%"
29-
$result = "| DomainName | AccountUpn | DeviceName | OnboardingStatus | JoinType | Ssso Expected |`n"
30-
$result += "| --- | --- | --- | --- | --- | --- |`n"
31-
foreach ($Domain in $DomainsWithSsso) {
32-
$DomainName = $Domain.OnPremisesDomainName
33-
$Domain.JsonArray | ForEach-Object {
34-
$AccountUpn = $_.AccountUpn
35-
$DeviceName = $_.DeviceName
36-
$OnboardingStatus = $_.OnboardingStatus
37-
$JoinType = $_.JoinType
38-
$SssoExpected = $_.'Seamless SSO Expected'
39-
$result += "| $($DomainName) | $($AccountUpn) | $($DeviceName) | $($OnboardingStatus) | $($JoinType) | $($SssoExpected) |`n"
40-
}
41-
}
42-
$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result
43-
}
44-
Add-MtTestResultDetail -Result $testResultMarkdown -Description "Enabling Seamless SSO in Entra ID Connect introduces risks of lateral movement between your On-premise domain and Entra ID.`n`nIn the results you will find which domains, users and devices are still using Seamless SSO. Via the column 'Ssso expected' you can learn if Seamless SSO is expected to be used based on enriched device data.`n`nFor more information on the risks regarding Seamless SSO and how to remediate, see: https://maester.dev/docs/tests/MT.1084" -Severity "High"
45-
$DomainsWithSsso.Count -eq "0" | Should -Be $True -Because "Seamless Single SignOn should be disabled for all domains in EntraID Connect servers."
46-
9+
Describe "Maester/Entra" -Tag "EntraIdConnect", "Entra", "Graph", "Security" {
10+
It "MT.1084: Microsoft Entra seamless single sign-on should be disabled for all domains in EntraID Connect servers. See https://maester.dev/docs/tests/MT.1084" -Tag "MT.1084" -Skip:( $EntraIDPlan -ne "P2" ) {
11+
Test-MtEntraIDConnectSyncSSO | Should -Be $True -Because "Microsoft Entra seamless single sign-on should be disabled for all domains in EntraID Connect servers."
4712
}
48-
4913
}

tests/XSPM/Test-XspmPrivilegedIdentities.Tests.ps1

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
BeforeDiscovery {
2-
$DefenderPlan = Get-MtLicenseInformation -Product "DefenderXDR"
2+
try {
3+
$DefenderPlan = Get-MtLicenseInformation -Product "DefenderXDR"
4+
} catch {
5+
$DefenderPlan = "NotConnected"
6+
}
37
}
48

59
Describe "Exposure Management" -Tag "Privileged", "Entra", "Graph", "LongRunning", "Security", "EntraOps", "XSPM" -Skip:( $DefenderPlan -ne "DefenderXDR" ) {

0 commit comments

Comments
 (0)