Monday, July 14, 2014

Use PowerShell to audit domain member local admins.


This PowerShell script was written to facilitate an audit of the local admin groups on all systems in our domain.  It will remove invalid accounts, or add new ones, or simply report what it finds.

*******  Updated version *******




#Region Header
#======================================================================================
#         File Name : Audit-And-Purge-LocalAdmins.ps1
#   Original Author : Kenneth C. Mazie (kcmjr AT kcmjr.com)
#                   :
#       Description : Used to audit and manage the local administrators group on domain computers.
#                   :
#             Notes : Original version rewritten for more reliable operation and better reporting.
#                   : Make sure to update all variables before running.  Output is only to the console.
#                   :
#         Arguments : 4 pipeline command argument options exist:
#                   :   -Report <mode>   Mode must be "brief" "verbose", or "none". Defaults to "brief"
#                   :   -Debug      If included enables test mode.  Defaults to $false if left off.   
#                   :   -Add        If included will add any user ID in the "ValidAdmins"
#                   :               array to local admin group on domain systems.
#                   :   -Remove     If included will remove any user ID in the "BadUserNames"
#                   :               hash table from the local admins group on domain systems.
#                   :   Any combination of options can be included at once.
#                   :
#          Warnings : Be carefull not to delete important accounts (like your own)!!
#                   :  
#             Legal : Public Domain. Modify and redistribute freely. No rights reserved.
#                   : SCRIPT PROVIDED "AS IS" WITHOUT WARRANTIES OR GUARANTEES OF
#                   : ANY KIND. USE AT YOUR OWN RISK. NO TECHNICAL SUPPORT PROVIDED.
#                   :
#           Credits : Code snippets and/or ideas came from many sources including but
#                   : not limited to the following:
#                   :
#    Last Update by : Kenneth C. Mazie
#   Version History : v1.0 - 02-19-14 - Original
#    Change History : v1.1 - 04-03-14 - Misc edits 
#                   : v2.0 - 05-15-14 - MAJOR rewrite.  Altered all process flows.  Altered
#                   :   input file format.
#                   :
#=======================================================================================
#EndRegion

Param (
   [switch]$Debug,
   [switch]$Add,
   [switch]$Remove,
   $Report = "brief"
)

Clear-Host
$ErrorActionPreference = "silentlycontinue"
$Domain = "mydomain"  
$TestPC = "test-machine"

Try{if (!(Get-Module -Name ActiveDirectory)){Import-Module ActiveDirectory}}
Catch{[void][System.Windows.Forms.MessageBox]::Show("The Active Directory PowerShell module was not found and is required.  Exiting" , "Error");Break}
 
function ListAdministrators 
  {$members = $Group.psbase.invoke("Members") | %{$_.GetType().InvokeMember("Name",'GetProperty',$null,$_,$null)} 
  #$members
return $members

$ValidAdmins = @{  #--[ Hash table of valid local admins ]--
1="enterprise admins";
2="domain admins";
3="administrator";
4="user1"
}

$NewAdmins = @()   #--[ Array of new admins to add ]--
$NewAdmins += "testuser"

$BadUserNames = @{  #--[ Hash table of users to remove from local admins group ]--
1="testuser";
2="bob";
3="jim";
4="dan"
}

Write-Host "Processing domain membership..." -ForegroundColor Yellow
If ($Debug){$Computers = @($TestPC)}Else{$Computers = Get-ADComputer -Filter {OperatingSystem -Like "*Windows*"} -Property * | select name}
$Count = $Computers.count

foreach ($Computer in $Computers){
   $List = "";$Target = "";$Group = ""
   If ($Debug){$Computer = $Computer}Else{$Computer = $Computer.name}
     
   Write-Host "`nTarget = $Computer     ("($Count-1)" remaining )" -ForegroundColor Cyan
     
   if ($Computer -like "*ESX*"){ #--[ One off bypass ]--
        Write-Host "BYPASSING "$Computer -ForegroundColor Magenta
   }Else{
      #--[ Check if computer is accessible, if not go to next computer in list ]--
      if (Test-Connection -ComputerName $Computer -Count 1 -ErrorAction SilentlyContinue){
         $Target = [ADSI]("WinNT://" + $Computer + ",computer"
             
         $Group = $Target.psbase.children.find("administrators")    #--[ Select the local admins group ]--

         $List = ListAdministrators #--[ Get the members of the administrators group via ADSI ]--
             
           #--[ Alternately get list of users in local admin group with WMI ]--
         # $List = Get-WmiObject -computername $computer -Class win32_GROUPUSER -ErrorAction SilentlyContinue | WHERE {$_.groupcomponent -match 'administrators' } | foreach {[wmi]$_.partcomponent }
  
         ForEach ($User in $List){
             $User = $User.TrimStart().ToLower()
              If ($Report -ne ""){
                     If ($Report -eq "brief"){} #Write-Host "   "$User -ForegroundColor Gray }        #--[ Just report changes, or optionally all entries ]--
                     If ($Report -eq "verbose"){                                      #--[ Report results against valid user list ]--
                        If ($ValidAdmins.ContainsValue($User)){
                           Write-Host "   AUTHORIZED   admin ""$User"" detected..." -ForegroundColor Green
                        }Else{
                               If ($User.substring(0,8) -eq "S-1-5-21"){   #--[ Name is an orphaned SID ]--
                              Write-Host "   ORPHANED SID Please Remove Manually... $User" -ForegroundColor Yellow  
                           }Else{
                                  If ($Remove){
                                 Write-Host "   UNAUTHORIZED admin ""$User"" detected..." -ForegroundColor Red
                                    }Else{
                                       Write-Host "   UNEXPECTED   admin ""$User"" detected..." -ForegroundColor White 
                                    }
                               }   
                        }
                     }
            }
                 
                  #--[ Remove the user ]----------------------------------------------
                  If ($Remove){     
                     If ($BadUserNames.ContainsValue($User)){
                        Write-Host "     --- Attempting to remove ""$User""..." -ForegroundColor Red
                        $Group.Remove("WinNT://" + $Target + "/" + $User)
                          $VerifyList = ListAdministrators
                  If ($VerifyList -contains $User){
                                Write-Host "     --- Verification failed.  Retrying as ""$Domain\$User""..." -ForegroundColor Red
                                    $Group.Remove("WinNT://" + $Target + "/" + $Domain + "/" + $User)
                             }
                          $VerifyList = ListAdministrators
                             If ($VerifyList -contains $User){
                                Write-Host "     --- Re-Verification failed.  Please remove ""$User"" manually ---" -ForegroundColor Red
                                   
                             }Else{
                                  Write-Host "     --- Verified removal of ""$User"" ---" -ForegroundColor Green
                             }
                        $Change = $true
               }
                  }  
             }
             
                  #--[ Add the user ]-------------------------------------------------     
            If ($Add){    
                     ForEach ($Admin in $NewAdmins){
#                       $Exists = 0
                          $AddList = ListAdministrators
                        If ($AddList -contains $Admin.ToLower()){#$Exist = 1}
#                   If ($Exists -eq 0){
                           Write-Host "   NEW ADMIN    ""$Admin"" " -ForegroundColor Gray -NoNewline
                               Write-Host "EXISTS... Not Adding" -ForegroundColor Red  
                      }Else{
                               Write-Host "   ADDING ----> $Admin" -ForegroundColor Green
                             $Group.Add("WinNT://" + $Target.name + "/" + $Admin)
                           $Change = $true     
                      }
                     }
                }
                         
        }Else{
                  #-------------------------------------------------------------------       
           Write-Host "  System is not reachable online..." -ForegroundColor Red
        }
            If ($Change){Write-Host "   Redetecting admins..." -ForegroundColor Magenta; ListAdministrators; $Change=$false}
   }

   $Count --
  
}
Write-host "`nCompleted... " -ForegroundColor Yellow




-----------------------------------------------------------------------------------
---------------------  Below is the original version -------------------------
-----------------------------------------------------------------------------------


#Region Header

<#======================================================================================
         File Name : AuditLocalAdmins.ps1
   Original Author : Kenneth C. Mazie (kcmjr AT kcmjr.com)
                   :
       Description : Will examine all systems in specified domain to determine membership
                   : in the local admins group.
                   :
             Notes : Add users to ignore to the local admins filter array, otherwise
                   : all members will be listed.  Requires Get-QADComputer from the
                   : Quest (now Dell) Active Directory commandlets available free at
                   : http://www.quest.com/powershell/activeroles-server.aspx
                   :
          Warnings : None
                   :  
             Legal : Public Domain. Modify and redistribute freely. No rights reserved.
                   : SCRIPT PROVIDED "AS IS" WITHOUT WARRANTIES OR GUARANTEES OF
                   : ANY KIND. USE AT YOUR OWN RISK. NO TECHNICAL SUPPORT PROVIDED.
                   :
           Credits : None
                   :
    Last Update by : Kenneth C. Mazie
   Version History : v1.0 - 07-14-14 - Original
    Change History : v1.1 - 00-00-00 -    
                   :
=======================================================================================#>
#EndRegion

Clear-Host

$ErrorActionPreference = "silentlycontinue"

$domain = "mydomain.com"

#--[ Local admins to filter out ]--
$Administrators = @{
1="admin1";
2="admin2"
}

$Computers = Get-QADComputer | select name
$Count = $Computers.count

foreach ($Computer in $Computers){
    $Computer = $Computer.name
    Write-Host "`nTarget = $Computer     ($Count)" -ForegroundColor Cyan
    
      if ($Computer -eq "computer1"){  # --[ A one-off bypass ]--
      Write-Host "BYPASSING "$Computer -ForegroundColor Magenta
      }Else{
    #--[ Check if computer is accessible, if not go to next computer in list ]--
    if (Test-Connection -ComputerName $Computer -Count 1 -ErrorAction SilentlyContinue){
      #--[ Get list of users in local admin group with WMI ]--
      $list = Get-WmiObject -computername $computer -Class win32_GROUPUSER -ErrorAction SilentlyContinue | WHERE {$_.groupcomponent -match 'administrators' } | foreach {[wmi]$_.partcomponent }
      #--[ For each user filter out domain admins and administrator ]--
      foreach ($user in $list){
        If ($Administrators.ContainsValue($user.name.tolower())){}Else{
          Write-Host "       "$user.name
        }
      }
      }Else{
        Write-Host "       System is not reachable online..." -ForegroundColor Red
      }
      $Count --
    }
}
Write-host Completed...