Skip to content

Commit 1e4a2f9

Browse files
authored
Merge pull request #645 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents b5dd3f6 + 020776a commit 1e4a2f9

10 files changed

+531
-2
lines changed

Modules/CIPPCore/Public/Set-CIPPDBCacheGuests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function Set-CIPPDBCacheGuests {
1515
try {
1616
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching guest users' -sev Info
1717

18-
$Guests = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=userType eq 'Guest'&`$top=999" -tenantid $TenantFilter
18+
$Guests = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=userType eq 'Guest'&`$expand=sponsors&`$top=999" -tenantid $TenantFilter
1919
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'Guests' -Data $Guests
2020
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'Guests' -Data $Guests -Count
2121
$Guests = $null

Modules/CIPPCore/Public/Set-CIPPDBCacheServicePrincipals.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function Set-CIPPDBCacheServicePrincipals {
1515
try {
1616
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching service principals' -sev Info
1717

18-
$ServicePrincipals = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/servicePrincipals?$top=999&$select=id,appId,displayName,servicePrincipalType,accountEnabled' -tenantid $TenantFilter
18+
$ServicePrincipals = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/servicePrincipals' -tenantid $TenantFilter
1919
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'ServicePrincipals' -Data $ServicePrincipals
2020
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'ServicePrincipals' -Data $ServicePrincipals -Count
2121
$ServicePrincipals = $null
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
function Invoke-CippTestZTNA21858 {
2+
param($Tenant)
3+
4+
try {
5+
$Guests = New-CIPPDbRequest -TenantFilter $Tenant -Type 'Guests'
6+
if (-not $Guests) {
7+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21858' -TestType 'Identity' -Status 'Investigate' -ResultMarkdown 'Guest user data not found in database' -Risk 'Medium' -Name 'Inactive guest identities are disabled or removed from the tenant' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'External collaboration'
8+
return
9+
}
10+
11+
$InactivityThresholdDays = 90
12+
$Today = Get-Date
13+
$EnabledGuests = $Guests | Where-Object { $_.AccountEnabled -eq $true }
14+
15+
if (-not $EnabledGuests) {
16+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21858' -TestType 'Identity' -Status 'Passed' -ResultMarkdown 'No guest users found in the tenant' -Risk 'Medium' -Name 'Inactive guest identities are disabled or removed from the tenant' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'External collaboration'
17+
return
18+
}
19+
20+
$InactiveGuests = @()
21+
foreach ($Guest in $EnabledGuests) {
22+
$DaysSinceLastActivity = $null
23+
24+
if ($Guest.signInActivity.lastSuccessfulSignInDateTime) {
25+
$LastSignIn = [DateTime]$Guest.signInActivity.lastSuccessfulSignInDateTime
26+
$DaysSinceLastActivity = ($Today - $LastSignIn).Days
27+
} elseif ($Guest.createdDateTime) {
28+
$Created = [DateTime]$Guest.createdDateTime
29+
$DaysSinceLastActivity = ($Today - $Created).Days
30+
}
31+
32+
if ($null -ne $DaysSinceLastActivity -and $DaysSinceLastActivity -gt $InactivityThresholdDays) {
33+
$InactiveGuests += $Guest
34+
}
35+
}
36+
37+
if ($InactiveGuests.Count -gt 0) {
38+
$Status = 'Failed'
39+
40+
$ResultLines = @(
41+
"Found $($InactiveGuests.Count) inactive guest user(s) with no sign-in activity in the last $InactivityThresholdDays days."
42+
''
43+
"**Total enabled guests:** $($EnabledGuests.Count)"
44+
"**Inactive guests:** $($InactiveGuests.Count)"
45+
"**Inactivity threshold:** $InactivityThresholdDays days"
46+
''
47+
'**Top 10 inactive guest users:**'
48+
)
49+
50+
$Top10Guests = $InactiveGuests | Sort-Object {
51+
if ($_.signInActivity.lastSuccessfulSignInDateTime) {
52+
[DateTime]$_.signInActivity.lastSuccessfulSignInDateTime
53+
} else {
54+
[DateTime]$_.createdDateTime
55+
}
56+
} | Select-Object -First 10
57+
58+
foreach ($Guest in $Top10Guests) {
59+
if ($Guest.signInActivity.lastSuccessfulSignInDateTime) {
60+
$LastActivity = [DateTime]$Guest.signInActivity.lastSuccessfulSignInDateTime
61+
$DaysInactive = [Math]::Round(($Today - $LastActivity).TotalDays, 0)
62+
$ResultLines += "- $($Guest.displayName) ($($Guest.userPrincipalName)) - Last sign-in: $DaysInactive days ago"
63+
} else {
64+
$Created = [DateTime]$Guest.createdDateTime
65+
$DaysSinceCreated = [Math]::Round(($Today - $Created).TotalDays, 0)
66+
$ResultLines += "- $($Guest.displayName) ($($Guest.userPrincipalName)) - Never signed in (Created $DaysSinceCreated days ago)"
67+
}
68+
}
69+
70+
if ($InactiveGuests.Count -gt 10) {
71+
$ResultLines += "- ... and $($InactiveGuests.Count - 10) more inactive guest(s)"
72+
}
73+
74+
$ResultLines += ''
75+
$ResultLines += '**Recommendation:** Review and remove or disable inactive guest accounts to reduce security risks.'
76+
77+
$Result = $ResultLines -join "`n"
78+
} else {
79+
$Status = 'Passed'
80+
$Result = "All enabled guest users have been active within the last $InactivityThresholdDays days"
81+
}
82+
83+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21858' -TestType 'Identity' -Status $Status -ResultMarkdown $Result -Risk 'Medium' -Name 'Inactive guest identities are disabled or removed from the tenant' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'External collaboration'
84+
} catch {
85+
$ErrorMessage = Get-CippException -Exception $_
86+
Write-LogMessage -API 'Tests' -tenant $Tenant -message "Failed to run test: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
87+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21858' -TestType 'Identity' -Status 'Failed' -ResultMarkdown "Test failed: $($ErrorMessage.NormalizedError)" -Risk 'Medium' -Name 'Inactive guest identities are disabled or removed from the tenant' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'External collaboration'
88+
}
89+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
function Invoke-CippTestZTNA21868 {
2+
param($Tenant)
3+
4+
try {
5+
$Guests = New-CIPPDbRequest -TenantFilter $Tenant -Type 'Guests'
6+
$Apps = New-CIPPDbRequest -TenantFilter $Tenant -Type 'Apps'
7+
$ServicePrincipals = New-CIPPDbRequest -TenantFilter $Tenant -Type 'ServicePrincipals'
8+
9+
if (-not $Guests -or -not $Apps -or -not $ServicePrincipals) {
10+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21868' -TestType 'Identity' -Status 'Investigate' -ResultMarkdown 'Required data not found in database' -Risk 'Medium' -Name 'Guests do not own apps in the tenant' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'External collaboration'
11+
return
12+
}
13+
14+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21868' -TestType 'Identity' -Status 'Investigate' -ResultMarkdown 'This test requires Graph API calls to check application and service principal ownership. Owner relationships are not cached.' -Risk 'Medium' -Name 'Guests do not own apps in the tenant' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'External collaboration'
15+
}
16+
catch {
17+
$ErrorMessage = Get-CippException -Exception $_
18+
Write-LogMessage -API 'Tests' -tenant $Tenant -message "Failed to run test: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
19+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21868' -TestType 'Identity' -Status 'Failed' -ResultMarkdown "Test failed: $($ErrorMessage.NormalizedError)" -Risk 'Medium' -Name 'Guests do not own apps in the tenant' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'External collaboration'
20+
}
21+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
function Invoke-CippTestZTNA21869 {
2+
param($Tenant)
3+
4+
try {
5+
$ServicePrincipals = New-CIPPDbRequest -TenantFilter $Tenant -Type 'ServicePrincipals'
6+
if (-not $ServicePrincipals) {
7+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21869' -TestType 'Identity' -Status 'Investigate' -ResultMarkdown 'Service principal data not found in database' -Risk 'Medium' -Name 'Enterprise applications must require explicit assignment or scoped provisioning' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Application management'
8+
return
9+
}
10+
11+
$AppsWithoutAssignment = $ServicePrincipals | Where-Object {
12+
$_.appRoleAssignmentRequired -eq $false -and
13+
$null -ne $_.preferredSingleSignOnMode -and
14+
$_.preferredSingleSignOnMode -in @('password', 'saml', 'oidc') -and
15+
$_.accountEnabled -eq $true
16+
}
17+
18+
if (-not $AppsWithoutAssignment) {
19+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21869' -TestType 'Identity' -Status 'Passed' -ResultMarkdown 'All enterprise applications have explicit assignment requirements' -Risk 'Medium' -Name 'Enterprise applications must require explicit assignment or scoped provisioning' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Application management'
20+
return
21+
}
22+
23+
$Status = 'Investigate'
24+
25+
$ResultLines = @(
26+
"Found $($AppsWithoutAssignment.Count) enterprise application(s) without assignment requirements."
27+
''
28+
'**Applications without user assignment requirements:**'
29+
)
30+
31+
$Top10Apps = $AppsWithoutAssignment | Select-Object -First 10
32+
foreach ($App in $Top10Apps) {
33+
$ResultLines += "- $($App.displayName) (SSO: $($App.preferredSingleSignOnMode))"
34+
}
35+
36+
if ($AppsWithoutAssignment.Count -gt 10) {
37+
$ResultLines += "- ... and $($AppsWithoutAssignment.Count - 10) more application(s)"
38+
}
39+
40+
$ResultLines += ''
41+
$ResultLines += '**Note:** Full provisioning scope validation requires Graph API synchronization endpoint not available in cache.'
42+
$ResultLines += ''
43+
$ResultLines += '**Recommendation:** Enable user assignment requirements or configure scoped provisioning to limit application access.'
44+
45+
$Result = $ResultLines -join "`n"
46+
47+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21869' -TestType 'Identity' -Status $Status -ResultMarkdown $Result -Risk 'Medium' -Name 'Enterprise applications must require explicit assignment or scoped provisioning' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Application management'
48+
}
49+
catch {
50+
$ErrorMessage = Get-CippException -Exception $_
51+
Write-LogMessage -API 'Tests' -tenant $Tenant -message "Failed to run test: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
52+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21869' -TestType 'Identity' -Status 'Failed' -ResultMarkdown "Test failed: $($ErrorMessage.NormalizedError)" -Risk 'Medium' -Name 'Enterprise applications must require explicit assignment or scoped provisioning' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Application management'
53+
}
54+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
function Invoke-CippTestZTNA21877 {
2+
param($Tenant)
3+
4+
try {
5+
$Guests = New-CIPPDbRequest -TenantFilter $Tenant -Type 'Guests'
6+
if (-not $Guests) {
7+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21877' -TestType 'Identity' -Status 'Investigate' -ResultMarkdown 'Guest user data not found in database' -Risk 'Medium' -Name 'All guests have a sponsor' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Application management'
8+
return
9+
}
10+
11+
if ($Guests.Count -eq 0) {
12+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21877' -TestType 'Identity' -Status 'Passed' -ResultMarkdown 'No guest accounts found in the tenant' -Risk 'Medium' -Name 'All guests have a sponsor' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Application management'
13+
return
14+
}
15+
16+
$GuestsWithoutSponsors = $Guests | Where-Object { -not $_.sponsors -or $_.sponsors.Count -eq 0 }
17+
18+
if ($GuestsWithoutSponsors.Count -eq 0) {
19+
$Status = 'Passed'
20+
$Result = 'All guest accounts in the tenant have an assigned sponsor'
21+
} else {
22+
$Status = 'Failed'
23+
24+
$ResultLines = @(
25+
"Found $($GuestsWithoutSponsors.Count) guest user(s) without sponsors out of $($Guests.Count) total guests."
26+
''
27+
"**Total guests:** $($Guests.Count)"
28+
"**Guests without sponsors:** $($GuestsWithoutSponsors.Count)"
29+
"**Guests with sponsors:** $($Guests.Count - $GuestsWithoutSponsors.Count)"
30+
''
31+
'**Top 10 guests without sponsors:**'
32+
)
33+
34+
$Top10Guests = $GuestsWithoutSponsors | Select-Object -First 10
35+
foreach ($Guest in $Top10Guests) {
36+
$ResultLines += "- $($Guest.displayName) ($($Guest.userPrincipalName))"
37+
}
38+
39+
if ($GuestsWithoutSponsors.Count -gt 10) {
40+
$ResultLines += "- ... and $($GuestsWithoutSponsors.Count - 10) more guest(s)"
41+
}
42+
43+
$ResultLines += ''
44+
$ResultLines += '**Recommendation:** Assign sponsors to all guest accounts for better accountability and lifecycle management.'
45+
46+
$Result = $ResultLines -join "`n"
47+
}
48+
49+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21877' -TestType 'Identity' -Status $Status -ResultMarkdown $Result -Risk 'Medium' -Name 'All guests have a sponsor' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Application management'
50+
} catch {
51+
$ErrorMessage = Get-CippException -Exception $_
52+
Write-LogMessage -API 'Tests' -tenant $Tenant -message "Failed to run test: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
53+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21877' -TestType 'Identity' -Status 'Failed' -ResultMarkdown "Test failed: $($ErrorMessage.NormalizedError)" -Risk 'Medium' -Name 'All guests have a sponsor' -UserImpact 'Medium' -ImplementationEffort 'Medium' -Category 'Application management'
54+
}
55+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
function Invoke-CippTestZTNA21886 {
2+
param($Tenant)
3+
4+
try {
5+
$ServicePrincipals = New-CIPPDbRequest -TenantFilter $Tenant -Type 'ServicePrincipals'
6+
if (-not $ServicePrincipals) {
7+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21886' -TestType 'Identity' -Status 'Investigate' -ResultMarkdown 'Service principal data not found in database' -Risk 'Medium' -Name 'Applications are configured for automatic user provisioning' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'Applications management'
8+
return
9+
}
10+
11+
$AppsWithSSO = $ServicePrincipals | Where-Object {
12+
$null -ne $_.preferredSingleSignOnMode -and
13+
$_.preferredSingleSignOnMode -in @('password', 'saml', 'oidc') -and
14+
$_.accountEnabled -eq $true
15+
}
16+
17+
if (-not $AppsWithSSO) {
18+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21886' -TestType 'Identity' -Status 'Passed' -ResultMarkdown 'No applications configured for SSO found' -Risk 'Medium' -Name 'Applications are configured for automatic user provisioning' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'Applications management'
19+
return
20+
}
21+
22+
$Status = 'Investigate'
23+
24+
$ResultLines = @(
25+
"Found $($AppsWithSSO.Count) application(s) configured for SSO."
26+
''
27+
'**Applications with SSO enabled:**'
28+
)
29+
30+
$SSOByType = $AppsWithSSO | Group-Object -Property preferredSingleSignOnMode
31+
foreach ($Group in $SSOByType) {
32+
$ResultLines += ""
33+
$ResultLines += "**$($Group.Name.ToUpper()) SSO** ($($Group.Count) app(s)):"
34+
$Top5 = $Group.Group | Select-Object -First 5
35+
foreach ($App in $Top5) {
36+
$ResultLines += "- $($App.displayName)"
37+
}
38+
if ($Group.Count -gt 5) {
39+
$ResultLines += "- ... and $($Group.Count - 5) more"
40+
}
41+
}
42+
43+
$ResultLines += ''
44+
$ResultLines += '**Note:** Provisioning template and job validation requires Graph API synchronization endpoint not available in cache.'
45+
$ResultLines += ''
46+
$ResultLines += '**Recommendation:** Configure automatic user provisioning for applications that support it to ensure consistent access management.'
47+
48+
$Result = $ResultLines -join "`n"
49+
50+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21886' -TestType 'Identity' -Status $Status -ResultMarkdown $Result -Risk 'Medium' -Name 'Applications are configured for automatic user provisioning' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'Applications management'
51+
} catch {
52+
$ErrorMessage = Get-CippException -Exception $_
53+
Write-LogMessage -API 'Tests' -tenant $Tenant -message "Failed to run test: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage
54+
Add-CippTestResult -TenantFilter $Tenant -TestId 'ZTNA21886' -TestType 'Identity' -Status 'Failed' -ResultMarkdown "Test failed: $($ErrorMessage.NormalizedError)" -Risk 'Medium' -Name 'Applications are configured for automatic user provisioning' -UserImpact 'Low' -ImplementationEffort 'Medium' -Category 'Applications management'
55+
}
56+
}

0 commit comments

Comments
 (0)