ActiveDirectory/Get-ADPrivilegedAccounts.ps1
|
function Get-ADPrivilegedAccounts { <# .SYNOPSIS Identifies and analyzes privileged accounts in Active Directory .DESCRIPTION Discovers privileged accounts including Domain Admins, Enterprise Admins, and other high-privilege groups with detailed analysis .PARAMETER IncludeNested Include nested group memberships .PARAMETER CheckLastLogon Check last logon time for privileged accounts .PARAMETER Domain Target domain (default: current domain) .EXAMPLE Get-ADPrivilegedAccounts Get-ADPrivilegedAccounts -IncludeNested -CheckLastLogon .OUTPUTS PSCustomObject with privileged account information #> [CmdletBinding()] param( [Parameter()] [switch]$IncludeNested, [Parameter()] [switch]$CheckLastLogon, [Parameter()] [string]$Domain ) begin { Write-Host "=== PRIVILEGED ACCOUNTS ANALYSIS ===" -ForegroundColor Cyan if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) { throw "ActiveDirectory module required. Install RSAT tools." } Import-Module ActiveDirectory -ErrorAction Stop if (-not $Domain) { $Domain = (Get-ADDomain).DNSRoot } Write-Host "Domain: $Domain" -ForegroundColor Yellow Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow # Define privileged groups $privilegedGroups = @( 'Domain Admins', 'Enterprise Admins', 'Schema Admins', 'Administrators', 'Account Operators', 'Backup Operators', 'Server Operators', 'Print Operators', 'DnsAdmins', 'Group Policy Creator Owners' ) $results = [PSCustomObject]@{ AnalysisDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" Domain = $Domain PrivilegedGroups = @() PrivilegedAccounts = @() Statistics = @{ TotalPrivilegedAccounts = 0 EnabledAccounts = 0 DisabledAccounts = 0 StaleAccounts = 0 AccountsWithoutLogon = 0 } Findings = @() Recommendations = @() } } process { try { Write-Host "`nAnalyzing privileged groups..." -ForegroundColor Cyan $allPrivilegedUsers = @{} foreach ($groupName in $privilegedGroups) { try { Write-Host "`nChecking: $groupName" -ForegroundColor Yellow $group = Get-ADGroup -Filter "Name -eq '$groupName'" -Server $Domain -ErrorAction SilentlyContinue if (-not $group) { Write-Verbose "Group not found: $groupName" continue } # Get group members if ($IncludeNested) { $members = Get-ADGroupMember -Identity $group -Recursive -Server $Domain -ErrorAction SilentlyContinue } else { $members = Get-ADGroupMember -Identity $group -Server $Domain -ErrorAction SilentlyContinue } $userMembers = @($members | Where-Object { $_.objectClass -eq 'user' }) Write-Host " Members: $($userMembers.Count)" -ForegroundColor White $groupInfo = [PSCustomObject]@{ GroupName = $groupName MemberCount = $userMembers.Count Members = @() } foreach ($member in $userMembers) { # Get detailed user info $user = Get-ADUser -Identity $member -Properties Enabled, PasswordLastSet, LastLogonDate, whenCreated, AdminCount -Server $Domain # Track unique users if (-not $allPrivilegedUsers.ContainsKey($user.SamAccountName)) { $allPrivilegedUsers[$user.SamAccountName] = @{ User = $user Groups = @() } } $allPrivilegedUsers[$user.SamAccountName].Groups += $groupName $memberInfo = [PSCustomObject]@{ Username = $user.SamAccountName DisplayName = $user.Name Enabled = $user.Enabled Created = $user.whenCreated PasswordLastSet = $user.PasswordLastSet LastLogon = $user.LastLogonDate } $groupInfo.Members += $memberInfo # Check for issues if (-not $user.Enabled) { $results.Statistics.DisabledAccounts++ } else { $results.Statistics.EnabledAccounts++ } if ($CheckLastLogon -and $user.LastLogonDate) { $daysSinceLogon = (New-TimeSpan -Start $user.LastLogonDate -End (Get-Date)).Days if ($daysSinceLogon -gt 90) { $results.Statistics.StaleAccounts++ $results.Findings += [PSCustomObject]@{ Severity = 'Medium' Type = 'Stale Account' Account = $user.SamAccountName Group = $groupName Issue = "Last logon: $daysSinceLogon days ago" } } } if (-not $user.LastLogonDate) { $results.Statistics.AccountsWithoutLogon++ } } $results.PrivilegedGroups += $groupInfo # Warn if group has too many members if ($groupName -in @('Domain Admins', 'Enterprise Admins') -and $userMembers.Count -gt 5) { $results.Findings += [PSCustomObject]@{ Severity = 'High' Type = 'Excessive Membership' Group = $groupName Issue = "$($userMembers.Count) members (recommended: ≤5)" } } } catch { Write-Warning "Error processing group $groupName : $_" } } # Process unique privileged accounts Write-Host "`nProcessing privileged accounts..." -ForegroundColor Cyan foreach ($username in $allPrivilegedUsers.Keys) { $userInfo = $allPrivilegedUsers[$username] $user = $userInfo.User $passwordAge = if ($user.PasswordLastSet) { (New-TimeSpan -Start $user.PasswordLastSet -End (Get-Date)).Days } else { $null } $lastLogonAge = if ($user.LastLogonDate) { (New-TimeSpan -Start $user.LastLogonDate -End (Get-Date)).Days } else { "Never" } $accountInfo = [PSCustomObject]@{ Username = $user.SamAccountName DisplayName = $user.Name Enabled = $user.Enabled Groups = $userInfo.Groups -join ', ' GroupCount = $userInfo.Groups.Count PasswordAge = $passwordAge LastLogon = $user.LastLogonDate LastLogonAge = $lastLogonAge Created = $user.whenCreated AdminCount = $user.AdminCount } $results.PrivilegedAccounts += $accountInfo # Check for anomalies if ($user.Enabled -and $passwordAge -gt 180) { $results.Findings += [PSCustomObject]@{ Severity = 'High' Type = 'Old Password' Account = $user.SamAccountName Issue = "Password not changed in $passwordAge days" } } if ($userInfo.Groups.Count -gt 3) { $results.Findings += [PSCustomObject]@{ Severity = 'Medium' Type = 'Excessive Privileges' Account = $user.SamAccountName Issue = "Member of $($userInfo.Groups.Count) privileged groups" } } } $results.Statistics.TotalPrivilegedAccounts = $allPrivilegedUsers.Count # Generate recommendations if ($results.Statistics.DisabledAccounts -gt 0) { $results.Recommendations += "Remove $($results.Statistics.DisabledAccounts) disabled accounts from privileged groups" } if ($results.Statistics.StaleAccounts -gt 0) { $results.Recommendations += "Review $($results.Statistics.StaleAccounts) stale privileged accounts (90+ days inactive)" } if ($results.Statistics.TotalPrivilegedAccounts -gt 20) { $results.Recommendations += "Reduce total privileged accounts (current: $($results.Statistics.TotalPrivilegedAccounts))" } $results.Recommendations += "Implement privileged access workstations (PAWs)" $results.Recommendations += "Enable MFA for all privileged accounts" $results.Recommendations += "Regular privileged account audits (quarterly)" } catch { Write-Error "Error analyzing privileged accounts: $_" throw } } end { Write-Host "`n=== ANALYSIS COMPLETE ===" -ForegroundColor Green Write-Host "`nStatistics:" -ForegroundColor Cyan Write-Host " Total Privileged Accounts: $($results.Statistics.TotalPrivilegedAccounts)" -ForegroundColor White Write-Host " Enabled: $($results.Statistics.EnabledAccounts)" -ForegroundColor Green Write-Host " Disabled: $($results.Statistics.DisabledAccounts)" -ForegroundColor Yellow Write-Host " Stale (90+ days): $($results.Statistics.StaleAccounts)" -ForegroundColor Red Write-Host "`nTop Privileged Groups:" -ForegroundColor Cyan $results.PrivilegedGroups | Sort-Object -Property MemberCount -Descending | Select-Object -First 5 | ForEach-Object { Write-Host " $($_.GroupName): $($_.MemberCount) members" -ForegroundColor White } if ($results.Findings.Count -gt 0) { Write-Host "`nFindings: $($results.Findings.Count)" -ForegroundColor Yellow $results.Findings | Group-Object Severity | ForEach-Object { Write-Host " $($_.Name): $($_.Count)" -ForegroundColor $( switch ($_.Name) { 'High' { 'Red' } 'Medium' { 'Yellow' } default { 'White' } } ) } } if ($results.Recommendations.Count -gt 0) { Write-Host "`nRecommendations:" -ForegroundColor Cyan $results.Recommendations | ForEach-Object { Write-Host " ! $_" -ForegroundColor Yellow } } return $results } } |