Local Administrator Audit Script

    In a large environment, correctly managing Privileged Access can be complicated and complex. Especially in a Windows Server environment, administrators can easily add other users and groups to the server’s Local Administrator group. This can lead to privilege escalation, privilege abuse, and data loss. So it’s important to monitor local administrators on servers. I put together this script to audit local administrator members across all the servers.


    The goals I had for this script were as follows:

    • The script can be run from one central location and audit all servers in the domain.
    • One output that could be parsed at any time for rogue accounts.
    • As dynamic as possible.

    Powershell Script

    This audit script is fairly simple. There’s an array of users and group names that are whitelisted because they are valid administrator accounts and groups.

    We pull the server machine names from Active Directory and then pull each server’s Local Administrator members using Get-NetLocalGroup. We take the output from that and parse it in Check-Members. Any unexpected accounts that are members of the group are logged with a warning.

    ### Configuration ###
    $Logdir = "C:\scripts\logs\"
    $Logfile = "LocalAdmin-Audit-$(get-date -Format FileDateTimeUniversal).log"
    $Logfile = Join-Path $Logdir $Logfile
    $Tab = [char]9
    # Whitelist Users and Groups
    $validadmins = "LOCALUS\Domain Admins", "Domain Admins", "Enterprise Admins", "LOCALUS\Server-Local-Admins", "LOCALUS\SVC_DEPLOY", "LOCALUS\SVC_CS_Admin", "LOCALUS\SVC_SQL", "LOCALUS\SVC_Manager", "LOCALUS\Web_Admin", "LOCALUS\SVC_SQL_AGENT"
    ### Functions ###
    Function LogWrite
       Param ([string]$logstring)
       if (!(Test-Path $Logdir)) { New-Item -ItemType Directory -Force -Path $Logdir}
       $d = Get-Date -Format “dd/MM/yyyy HH:mm:ss”
       $logline = "$d $Tab $logstring"
       Add-content $Logfile -value $logline
    Function Get-NetLocalGroup {
    [string]$Group = "Administrators",
    Write-Verbose "Getting members of local group $Group"
    #define the scriptblock
    $sb = {
     Param([string]$Name = "Administrators")
    $members = net localgroup $Name | 
     where {$_ -AND $_ -notmatch "command completed successfully"} | 
     select -skip 4
    New-Object PSObject -Property @{
     Computername = $env:COMPUTERNAME
     Group = $Name
    } #end scriptblock
    #define a parameter hash table for splatting
    $paramhash = @{
     Scriptblock = $sb
    if ($Computername[0] -is [management.automation.runspaces.pssession]) {
    else {
    if ($asjob) {
        Write-Verbose "Running as job"
    #run the command
    Invoke-Command @paramhash | Select * -ExcludeProperty RunspaceID
    } #end Get-NetLocalGroup
    function Check-Members($admingroup){
    $h = $admingroup.Computername
    $m = $admingroup.Members
    Write-Host "Admins for $h"
    foreach ($m1 in $m)
         if($validadmins -contains $m1){
            Write-Host $m1 -ForegroundColor Green
            if ($m1 -match "-Admin") { Write-Host $m1 -ForegroundColor Green } else {
            Write-Host $m1 -ForegroundColor Yellow
            LogWrite "$h $Tab WARNING $Tab Unexpected Admin Account found - $m1"
    ### Main ###
    $servers = Get-ADComputer -Filter *
    foreach ($s in $servers)
        $server = $s.DNSHostName
        $ping = Test-Connection $server -Count 1 -Quiet
        LogWrite "$server $Tab Checking $server"
        if ($ping){
            Write-Host "         $server       " -BackgroundColor White -ForegroundColor DarkBlue
            $admins = Get-NetLocalGroup $server
            Check-Members $admins
            Write-Warning "Could not connect to $server"
            LogWrite "$server $Tab Could not connect to $server"
        Write-Host "                                    " -BackgroundColor White -ForegroundColor DarkBlue
    ### End Script ###

    The script will generate a log output for every run. Servers that can not be contacted will be logged. Unexpected Admin accounts are logged as a warning.

    09/11/2019 02:58:20      SERVERSSI01     Checking  SERVERSSI01
    09/11/2019 02:58:20      SERVERAVS02     Checking SERVERAVS02
    09/11/2019 02:58:21      SERVERAVS02     WARNING     Unexpected Admin Account found - LOCALUS\joel.robinson
    09/11/2019 02:58:21      SERVERANL01     Checking SERVERANL01
    09/11/2019 02:58:25      SERVERAPP02     Checking SERVERAPP02
    09/11/2019 02:58:25      SERVERAPP02     Could not connect to SERVERAPP02
    09/11/2019 02:58:29      SERVERSQL01     Checking SERVERSQL01

    You could load this output into your SIEM, or configure the script to log directly to a SIEM or other data lake.


    The Get-NetLocalGroup function was originally published on powershell.org in 2013.

    Recent Articles

    Security Trends – Q2 2024

    Small businesses are under increasing cyberattack threat. The global cost of cybercrime is predicted to skyrocket, reaching a staggering $23.84 trillion by...

    Basic Microsoft 365 Security Improvements

    Overview Securing Microsoft 365 tenants involves implementing various measures to protect data, prevent unauthorized access, and mitigate potential...

    Cyber Security Trends for 2024

    Summary The state of cybersecurity in 2024 reflects a dynamic and complex landscape shaped by technological advancements,...

    Content Filtering with Microsoft Defender for Endpoint

    Why is Content Filtering Important? Web content filtering is a critical element of a comprehensive information security strategy.

    Migrating to Azure AD Authentication Methods

    Azure AD Authentication Changes On September 30th, 2024, the legacy multifactor authentication and...

    Related Stories

    0 0 vote
    Article Rating
    Notify of
    Inline Feedbacks
    View all comments

    Stay on op - Ge the daily news in your inbox

    Would love your thoughts, please comment.x