Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/powershell/public/Get-ZtGraphScope.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
'Policy.Read.ConditionalAccess'
'Policy.Read.PermissionGrant'
'PrivilegedAccess.Read.AzureAD'
'PrivilegedEligibilitySchedule.Read.AzureADGroup'
'Reports.Read.All'
'RoleManagement.Read.All'
'UserAuthenticationMethod.Read.All'
Expand Down
66 changes: 44 additions & 22 deletions src/powershell/tests/Test-Assessment.21816.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,17 @@ function Test-Assessment-21816 {
if ($member.'@odata.type' -eq '#microsoft.graph.user') {
$nonPIMPrivilegedUsers += $memberInfo
} else {
$nonPIMPrivilegedGroups += $memberInfo
# Q5: Check if the group uses PIM for Groups to enforce JIT member access
$pimForGroupsSchedules = $null
try {
$pimForGroupsSchedules = Invoke-ZtGraphRequest -RelativeUri 'identityGovernance/privilegedAccess/group/eligibilitySchedules' -Filter "groupId eq '$($member.id)' and accessId eq 'member'" -ApiVersion beta -ErrorAction Stop
} catch {
Write-PSFMessage "Could not check PIM for Groups for group $($member.displayName): $($_.Exception.Message). Treating as non-PIM managed." -Level Warning
}
if (-not $pimForGroupsSchedules) {
Comment thread
ashwinikarke marked this conversation as resolved.
# Group does not use PIM for Groups - count as non-PIM managed
$nonPIMPrivilegedGroups += $memberInfo
}
}
}
}
Expand Down Expand Up @@ -130,23 +140,36 @@ function Test-Assessment-21816 {
$permanentGAUserList += $memberInfo
} elseif ($member.'@odata.type' -eq '#microsoft.graph.group') {
$permanentGAGroupList += $memberInfo
# Get group members - only users
$groupMembers = Invoke-ZtGraphRequest -RelativeUri "groups/$($member.id)/members" -Select 'userPrincipalName,displayName,id,onPremisesSyncEnabled' -ApiVersion beta
foreach ($groupMember in $groupMembers) {
# Only process users, skip service principals
if ($groupMember.'@odata.type' -eq '#microsoft.graph.user') {
$groupMemberInfo = [PSCustomObject]@{
displayName = $groupMember.displayName
userPrincipalName = $groupMember.userPrincipalName
id = $groupMember.id
roleTemplateId = $globalAdminRoleId
roleDefinitionId = $gaDirectoryRole.id
roleName = 'Global Administrator (via group)'
isPrivileged = $true
assignmentType = 'Via Group'
onPremisesSyncEnabled = $groupMember.onPremisesSyncEnabled
# Q4: Check if the group uses PIM for Groups to enforce JIT member access
$pimForGroupsSchedules = $null
try {
$pimForGroupsSchedules = Invoke-ZtGraphRequest -RelativeUri 'identityGovernance/privilegedAccess/group/eligibilitySchedules' -Filter "groupId eq '$($member.id)' and accessId eq 'member'" -ApiVersion beta -ErrorAction Stop
} catch {
Write-PSFMessage "Could not check PIM for Groups for group $($member.displayName): $($_.Exception.Message). Treating as non-PIM managed." -Level Warning
}
if ($pimForGroupsSchedules) {
# Group enforces JIT via PIM for Groups - exclude from PermanentGAGroupList, do not add members
$permanentGAGroupList = $permanentGAGroupList | Where-Object { $_.id -ne $member.id }
Write-PSFMessage "Group $($member.displayName) uses PIM for Groups, excluding from permanent GA list" -Level Verbose
} else {
# Group does not use PIM for Groups - get members and add to permanentGAUserList (Q5)
$groupMembers = Invoke-ZtGraphRequest -RelativeUri "groups/$($member.id)/members" -Select 'userPrincipalName,displayName,id,onPremisesSyncEnabled' -ApiVersion beta
foreach ($groupMember in $groupMembers) {
# Only process users, skip service principals
if ($groupMember.'@odata.type' -eq '#microsoft.graph.user') {
$groupMemberInfo = [PSCustomObject]@{
displayName = $groupMember.displayName
userPrincipalName = $groupMember.userPrincipalName
id = $groupMember.id
roleTemplateId = $globalAdminRoleId
roleDefinitionId = $gaDirectoryRole.id
roleName = 'Global Administrator (via group)'
isPrivileged = $true
assignmentType = 'Via Group'
onPremisesSyncEnabled = $groupMember.onPremisesSyncEnabled
}
$permanentGAUserList += $groupMemberInfo
}
Comment on lines +150 to 172
$permanentGAUserList += $groupMemberInfo
}
}
}
Expand All @@ -171,10 +194,10 @@ function Test-Assessment-21816 {
} elseif ($permanentGACount -gt 2) {
$passed = $false
$customStatus = 'Investigate'
$testResultMarkdown = 'Three or more accounts are permanently assigned the Global Administrator role. Review to determine whether these are emergency access accounts.'
$testResultMarkdown = 'Three or more accounts are permanently assigned the Global Administrator role. Review to determine whether these are emergency access accounts configured in accordance with Microsoft guidance.'
} else {
$passed = $true
$testResultMarkdown = 'All Microsoft Entra privileged role assignments are managed with PIM with the exception of up to two standing Global Administrator accounts.'
$testResultMarkdown = 'All Microsoft Entra privileged role assignments are managed with PIM with the exception of up to two standing Global Administrator accounts that are permitted by this check for emergency access.'
}

$testResultMarkdown += "`n`n%TestResult%"
Expand Down Expand Up @@ -236,9 +259,8 @@ function Test-Assessment-21816 {
Result = $testResultMarkdown
}

# Only add CustomStatus when it's "Investigate" (more than 2 permanent GAs)
if ($permanentGACount -gt 2) {
$params.CustomStatus = 'Investigate'
if ($customStatus) {
$params.CustomStatus = $customStatus
}

Add-ZtTestResultDetail @params
Expand Down
Loading