SHELL/1.1.3.ps1

$CheckId = "1.1.3"
$Title = "Ensure that between two and four global admins are designated"

try {
    $GlobalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'" | Select-Object -First 1

    if (-not $GlobalAdminRole) {
        throw "Global Administrator directory role is not available in this tenant."
    }

    $RoleMembers = @(Get-MgDirectoryRoleMember -DirectoryRoleId $GlobalAdminRole.Id -All)
    $UserIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    $DirectUserIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    $GroupAssignments = @()
    $NonUserAssignments = @()

    foreach ($Member in $RoleMembers) {
        $ObjectType = $Member.AdditionalProperties.'@odata.type'

        if ($ObjectType -eq "#microsoft.graph.user") {
            [void]$UserIds.Add($Member.Id)
            [void]$DirectUserIds.Add($Member.Id)
            continue
        }

        if ($ObjectType -eq "#microsoft.graph.group") {
            $GroupUsers = @()
            try {
                $GroupMembers = @(Get-MgGroupMember -GroupId $Member.Id -All)
                foreach ($GroupMember in $GroupMembers) {
                    $GroupMemberType = $GroupMember.AdditionalProperties.'@odata.type'
                    if ($GroupMemberType -eq "#microsoft.graph.user") {
                        [void]$UserIds.Add($GroupMember.Id)
                        $GroupUsers += $GroupMember.Id
                    }
                }
            }
            catch {
                $GroupAssignments += [pscustomobject]@{
                    GroupId = $Member.Id
                    Error   = $_.Exception.Message
                }
                continue
            }

            $GroupAssignments += [pscustomobject]@{
                GroupId           = $Member.Id
                UserMemberCount   = @($GroupUsers).Count
                UserMemberObjectIds = @($GroupUsers)
            }
            continue
        }

        $NonUserAssignments += [pscustomobject]@{
            Id   = $Member.Id
            Type = $ObjectType
        }
    }

    $AllUserIds = @($UserIds)
    $ResolvedUsers = foreach ($UserId in $AllUserIds) {
        try {
            Get-MgUser -UserId $UserId -Property UserPrincipalName,DisplayName,Id,AccountEnabled |
                Select-Object Id,DisplayName,UserPrincipalName,AccountEnabled
        }
        catch {
            [pscustomobject]@{
                Id                = $UserId
                DisplayName       = $null
                UserPrincipalName = $null
                AccountEnabled    = $null
                ResolutionError   = $_.Exception.Message
            }
        }
    }

    $GlobalAdminCount = @($AllUserIds).Count
    $Pass = ($GlobalAdminCount -ge 2 -and $GlobalAdminCount -le 4)

    [pscustomobject]@{
        CheckId   = $CheckId
        Title     = $Title
        Status    = if ($Pass) { "PASS" } else { "FAIL" }
        Pass      = $Pass
        Evidence  = [pscustomobject]@{
            RequiredRange         = "2-4"
            GlobalAdminCount      = $GlobalAdminCount
            DirectAssignmentCount = @($DirectUserIds).Count
            GroupAssignments      = @($GroupAssignments)
            NonUserAssignments    = @($NonUserAssignments)
            GlobalAdmins          = @($ResolvedUsers)
        }
        Error     = $null
        Timestamp = Get-Date
    }
}
catch {
    [pscustomobject]@{
        CheckId   = $CheckId
        Title     = $Title
        Status    = "ERROR"
        Pass      = $null
        Evidence  = $null
        Error     = $_.Exception.Message
        Timestamp = Get-Date
    }
}