With the PowerShell script I discuss in this post, you can find out who has administrator rights on specific computers.

Robert Pearman

Robert is a small business specialist from the UK and currently works as a system administrator for IT Authority. He has been a Microsoft MVP for seven years and has worked as a technical reviewer for Microsoft Press. You can follow Robert in his blog.

One of my customers is a typical freewheeling creative media giant, with pool tables, ping-pong, free lattes, and a real lack of concern for network security—which is why they pay us to manage it for them.

Over the years, as I'm sure you appreciate, one or two more technically minded people in your organization may have asked to be local administrators on their computers. Or at least they wanted a separate local admin account created so they could continually update their equally awesome open-source development tool. Of course, no one has ever heard of this, and it doesn't have a management engine GPO can control.

Unfortunately for those free spirits, someone just bought out their company. And although the users can go along with the pool tables and the lattes, the new owner will not tolerate under any circumstances users with administrative permissions on their computers—quite rightly so.

So we face the easy option to enable restricted groups and wipe out any local admin permissions anyone may have had. Before doing this, I wanted to search those devices to see who actually did still have local admin permissions and whether we needed to inform any folks their party was over or if we could just silently roll out a restricted group.

PowerShell seemed like an easy option, but with no direct way to query local groups, we needed to search for a way to do it and additionally exclude our own local administrator accounts from the results.

Using this PowerShell script as a basis, I adapted it to exclude certain accounts from the results (for example, local administrator accounts authorized to be there), direct the query to a specific PC or to an entire organizational unit (OU), and also exclude PCs not active recently.

The base script uses WMI to query Win32_GroupUser and collect all users in all local groups. It then selects only those where the local group is administrators.

In the example, we have queried an OU for any local Aadministrator accounts not either Administrator, REDACTED, or part of the Domain Admins on a PC active on the network since September 1. The result is 139 computers to search, 31 offline computers, and 4 accounts that fail our test.

We can now speak to those four individuals and warn them they'll soon lose their admin permissions.

Local admin query

Local admin query

Of course the good thing about collating all of this information into $report is that we can then use the object to build a picture over time by exporting the result to a CSV or emailing the report to an auditor or anything you can think of!

Join the 4sysops PowerShell group!

2+

Users who have LIKED this post:

  • avatar
Share
35 Comments
  1. Melvin Backus 11 months ago

    Sweet script. Is it possible there's a way I could modify it to include a default set of excluded names?  I never want to see Administrator or Domain Admins, and probably a few others in some groups of machines.

    0

    • Author
      Robert Pearman 11 months ago

      Yes you can do that.

      At line 33, create a new array to include your default exclusions, then add that new array to the $excludeNames array.

      $defaultExclude = @(

      "Administrator",

      "Domain Admins"

      )

      $excludeNames += $defaultExclude

      Save and run to test.

      1+

  2. Melvin Backus 11 months ago

    PERFECT! Works great.

    Thank you

    0

  3. John Wagner 11 months ago

    How about having this save to a file?

    2+

    • Author
      Robert Pearman 11 months ago

      Yes, can do that.

      Up around line 32, add in a new array.

      $report = @()

      Then line 93 add,

      $report += $comGroupObj

      Then at the end around line 111, you can do something like,

      $report | export-csv <my file path>

      Should work fine.

      1+

      • John Wagner 11 months ago

        So I added this in and it ends up generating the following error. I am running powershell with elevated rights so I should have full rights to the path I'm using.

        export-csv : Access to the path 'C:\logs' is denied.
        At C:\temp\LocalAdminGroupAudit.ps1:107 char:11
        + $report | export-csv C:\logs
        + ~~~~~~~~~~~~~~~~~~
        + CategoryInfo : OpenError: (:) [Export-Csv], UnauthorizedAccessException
        + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.ExportCsvCommand

        0

        • Author
          Robert Pearman 11 months ago

          You should use a full file path, like c:\logs\mylog.csv

           

          1+

          • john wagner 11 months ago

            doh! thanks my bad!! 🙂
            works perfectly now.

            0

          • john wagner 11 months ago

            OOps i spoke to soon.

            So the report file is generated but its empty. lol

            0

          • john 11 months ago

            So i just added a redirect at the end of the cli call to send it to a text file and that works okay.

            0

  4. John Wagner 11 months ago

    One issue I ran into in my environment.

    I have a  parent OU with several sub OUs and all have computer objects under them.  When I kick off the script it only scans the parent and doesn't scan the subs.  How would I set it to scan parent and subs?

    0

  5. David Figueroa 11 months ago

    That's easy.. you just need to change this line (59):

     
    Add the parameter -SearchScope Subtree, and that will pick up all the computers you need..

    David F.

    1+

  6. David Figueroa 11 months ago

    Sorry.. that didn't quote correctly..

    Original:
    $computers = Get-AdComputer -filter {LastLogonDate -ge $searchDays } -searchbase $ou -properties LastLogonDate | sort Name -Descending
     

    Updated:

    $computers = Get-AdComputer -filter {LastLogonDate -ge $searchDays } -searchbase $ou -properties LastLogonDate -SearchScope Subtree| sort Name -Descending

     
    David F.

    2+

    Users who have LIKED this comment:

    • avatar
  7. John Wagner 11 months ago

    Awesome thanks very much!!!!

    0

  8. Eddiberto Silvaz 7 months ago

    Can you make it run for multiple PC that you have a file with the PC like "pc.txt"

     

    0

  9. moulouya 7 months ago

    Hello ,

    I receive this kind of error when i execute the script:

    Parameter set cannot be resolved using the specified named parameters.
    + CategoryInfo : InvalidArgument: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : AmbiguousParameterSet

    0

  10. moulouya 7 months ago

    I copy paste the script with changing  :

    ParameterSetName="domain = i set my domain)

    ParameterSetName="computer = i set the path of the OU")

    Regards,

    0

    • Author
      Robert Pearman 7 months ago

      OK that sounds like a bad idea.

      Copy the script 'as is', make no changes.

      When you run it, do the following..

      .\myscript.ps1 -pcname pc01

      This will query one pc, named PC01

      .\myscript.ps1 -ou "ou=myou,dc=domain,dc=com"

      This will query the whole OU.

      1+

  11. Eddiberto Silvaz 7 months ago

    Robert,

    I left the script as is and ran it against my domain that has 4500+ pc it runs great but the buffer gets full and before it can copy the $report | export-csv c:\logs\logs.csv it ran for days and I got no data.  if I break it down to groups of 100 it works.

    I had modified another script in VBS that I had set the files and created a log that passed the data results from each scan, so there were no buffering issues

    Option Explicit

    Const LogFile = "LocalAdmins.log"
    Const resultFile = "LocalAdministratorsMembership.csv"
    Const inputFile = "workstations.txt"

    Dim fso
    Set fso = CreateObject("Scripting.FileSystemObject")

    Dim shl
    Set shl = WScript.CreateObject("WScript.Shell")

    Dim fil
    Set fil = fso.OpenTextFile(inputFile)

    Dim results
    Set results = fso.CreateTextFile(resultFile, True)

    WriteToLog "Beginning Pass of " & inputFile & " at " & Now()
    WScript.Echo "Beginning Pass of " & inputFile & " at " & Now()
    'On Error Resume Next

    Dim grp
    Dim line
    Dim exec
    Dim pingResults
    Dim member

    What would you recomend I do to with your script to scan 4000+ systems and keep the data?

    0

  12. Author
    Robert Pearman 7 months ago

    Are they all in one single OU?

    Im not particularly experience with large environments like that. How many objects does it process before it crashes? 1000?

    0

  13. Eddiberto Silvaz 7 months ago

    Are they all in one single OU? YES computer is in one or broken into areas

    How many objects does it process before it crashes? 1000?  it doesn't crash it continues and displays about 100 lines on the screen, but nothing gets passed over to the CSV file.  The script runs for days, I'm thinking to increase the buffer size and the number of buffers to see if it can keep all the data.

    I'm not very good using Powershell but I'm thinking if I add a line to move output to a temp log.txt and then push the log.txt to my log.csv when the script is completed?

    0

  14. David Figueroa 7 months ago

    I can't see the entire modified script.. but the basic idea isn't too tough to do what you need Eddiberto..

    Change the array to an arraylist and use a .add() for each object, then use your $pCounter to export every 100 lines and then .Clear() it and continue on.  Here's a quick example of what I'm talking about..

    So, this should export every 100 numbers which will keep your memory from running out.  Base on the $pCounter you would use if ($pCounter % 100 -eq 0) { $var | export-csv -path <path> -append -notypeinformation }.

    Coralon

    0

    • Eddiberto Silvaz 7 months ago

      David,

      the code is the original on top with the following added

      create a new array to include your default exclusions, then add that new array to the $excludeNames array

      $defaultExclude = @("Administrator","Domain Admins")

      $excludeNames += $defaultExclud

      ----------------------------------------------------------------------

      Up around line 32, add in a new array.

      $report = @()

      Then line 93 add,

      $report += $comGroupObj

      Then at the end around line 111, you can do something like,

      $report | export-csv <my file path>

      where would you add your code?

      0

  15. moulouya 7 months ago

    Cooooool, thx a lot , it works

    0

  16. Jim 4 months ago

    I know this thread is a bit old, but I can run the script when I use -PCName. However when I use -ou it runs but returns nothing (No computers found). I've tried it on several different OUs. 

    0

    • Swapnil Kambli 4 months ago

      Hi Jim, The script is running fine for me. Could you please provide the exact command you are running and the result.

      0

      • Jim 4 months ago

        I figured it out actually, I was not adding in the rest of the variables after the ou section (-excludeNames, active days, etc)

        But thanks for replying!

        1+

        Users who have LIKED this comment:

        • avatar
  17. Steve 4 months ago

    For some reason when I run it I get no results.  It shows:

     

    Local Admin Group membership audit tool. Searches computers for membership of Local Administrators group.
    Accounts not listed in -excludeNames will be displayed.
    Searching Computers in : <redacted> (it shows the proper OU)
    Computers Active Since : 04/14/2019 16:22:25
    Admin Users            : Administrators Domain Admins
    Computers Found        : 0

    Total invalid Accounts : 0
    Online Computers       : 0
    Offline Computers      : 0

     

    I even made sure to run it as an admin, and tried the Import-Module ActiveDirectory to see if that helped, but still get the same results.

     

    0

  18. Steve 4 months ago

    Disregard, I didn't notice but I was searching the "Users" OU instead of computers.  It works now.

    0

  19. Michael 4 months ago

    Hi Robert 

    This is excellent and does exactly what I need.

    However one thing is our hostname are pretty long so the left column does not show the full hostname like the below.

    Can you please advise where do I modify to make the left column to fit more character.

     

    thanks

    DT-4RFS... test                                      Administrators
    DT-5CG6... test                                      Administrators

    1+

    Users who have LIKED this comment:

    • avatar
    • Swapnil Kambli 4 months ago

      Hi Michael,

      Please make use of Format-table to have the desired output

      Line no 93:  $comGroupObj | Format-Table -AutoSize

      -1+

  20. michael 4 months ago

    Thanks so much Swaphnil and Robert, worked perfectly.

    0

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2019

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account