Rules/ID-007.no-service-accounts-privileged.psd1

@{
    Id          = 'ID-007'
    Title       = 'No service accounts hold privileged directory roles'
    Area        = 'RoleAssignments'
    Severity    = 'High'
    References  = @(
        'https://learn.microsoft.com/entra/identity/role-based-access-control/best-practices'
    )
    Rationale   = 'Service accounts (MDM bindings, sync, backup, automation) with admin roles combine standing privilege with credentials that live in configs and never see an interactive MFA prompt.'
    Remediation = 'Remove the admin role from the flagged accounts; most integrations need it only during setup. Use app registrations with least-privilege application permissions or scoped roles instead.'
    Test        = {
        param($Snapshot)
        $privilegedRoles = @(
            'Global Administrator', 'Privileged Role Administrator', 'Security Administrator',
            'User Administrator', 'Exchange Administrator', 'SharePoint Administrator',
            'Intune Administrator', 'Application Administrator', 'Cloud Application Administrator',
            'Conditional Access Administrator'
        )
        $serviceNamePattern = '(?i)(svc|service|automation|mdm|sync|backup|daemon|scanner|noreply|robot)'
        $hits = @($Snapshot.RoleAssignments.assignments | Where-Object {
                ($privilegedRoles -contains [string]$_._tlRoleName) -and (
                    ([string]$_._tlPrincipalName -match $serviceNamePattern) -or
                    ([string]$_._tlPrincipalUpn -match $serviceNamePattern)
                )
            })
        if ($hits.Count -gt 0) {
            New-TLRuleResult -Status Fail -Evidence @($hits | ForEach-Object {
                    "{0} ({1}) holds {2} - looks like a service account" -f $_._tlPrincipalName, $_._tlPrincipalUpn, $_._tlRoleName
                })
        }
        else {
            New-TLRuleResult -Status Pass -Evidence 'No privileged role assignment matches common service-account naming patterns.'
        }
    }
}