Checks/GroupsTeams/ARG.GRP.005.ps1

Register-ArgusCheck @{
    Id          = 'ARG.GRP.005'
    Title       = 'Microsoft 365 groups and Teams with a single owner'
    Area        = 'GroupsTeams'
    Category    = 'Ownership'
    Severity    = 'Low'
    Description = 'Microsoft 365 (Unified) groups, including Teams, with exactly one owner. The Owner column shows who it is and OwnerEnabled whether that account is still enabled; groups whose only owner is disabled are listed first.'
    Rationale   = 'A single owner is a single point of failure: when that person leaves or changes role the group becomes ownerless. Microsoft recommends at least two owners.'
    Remediation = @{
        Steps      = "1. Ask each owner to add a second owner (a colleague or a team lead).`n2. Start with the groups whose only owner is disabled.`n3. Make two owners a requirement of your group request process."
        PortalUrl  = 'https://admin.microsoft.com/Adminportal/Home#/groups'
    }
    References  = @(
        @{ Title = 'Add or remove members from Microsoft 365 groups using the admin center'; Url = 'https://learn.microsoft.com/en-us/microsoft-365/admin/create-groups/add-or-remove-members-from-groups' }
        @{ Title = 'Manage ownerless Microsoft 365 groups and teams'; Url = 'https://learn.microsoft.com/en-us/microsoft-365/admin/create-groups/ownerless-groups-teams' }
    )
    Requires    = @{
        Services         = @('Graph')
        GraphScopes      = @('Group.Read.All')
        DataSets         = @('Groups')
        OptionalDataSets = @('Users')
    }
    Tags        = @('Groups', 'Teams', 'Ownership')
    Test        = {
        param($Context)
        $enabled = @{}
        if ($Context.Data.ContainsKey('Users')) {
            foreach ($user in @($Context.Data['Users'])) { $enabled[[string](Get-ArgusProperty $user 'id' '')] = (Get-ArgusProperty $user 'accountEnabled') -eq $true }
        }
        $unified = @($Context.Data['Groups'] | Where-Object { $_.IsUnified })
        $findings = @($unified | Where-Object { $_.OwnerCount -eq 1 } | ForEach-Object {
                $owner = @(Get-ArgusProperty $_ 'owners' @())[0]
                $ownerId = [string](Get-ArgusProperty $owner 'id' '')
                $ownerName = [string](Get-ArgusProperty $owner 'userPrincipalName' '')
                if (-not $ownerName) { $ownerName = [string](Get-ArgusProperty $owner 'displayName' $ownerId) }
                $ownerEnabled = if (-not $enabled.ContainsKey($ownerId)) { 'Unknown' } elseif ($enabled[$ownerId]) { 'Yes' } else { 'No' }
                [pscustomobject]@{
                    DisplayName  = Get-ArgusProperty $_ 'displayName' ''
                    Team         = if ($_.IsTeam) { 'Yes' } else { 'No' }
                    Owner        = $ownerName
                    OwnerEnabled = $ownerEnabled
                    Created      = Format-ArgusDate (Get-ArgusProperty $_ 'createdDateTime')
                    GroupId      = Get-ArgusProperty $_ 'id' ''
                }
            } | Sort-Object @{ Expression = { $_.OwnerEnabled -eq 'No' }; Descending = $true }, DisplayName)
        if ($findings.Count -eq 0) {
            return New-ArgusResult -Status Passed -EvaluatedCount $unified.Count -Summary 'No Microsoft 365 group has a single owner ({EvaluatedCount} evaluated).'
        }
        $disabled = @($findings | Where-Object OwnerEnabled -EQ 'No').Count
        New-ArgusResult -Status Failed -Findings $findings -EvaluatedCount $unified.Count `
            -Summary "{AffectedCount} of {EvaluatedCount} Microsoft 365 groups have exactly one owner ($disabled with a disabled owner)."
    }
}