Show which users are in the local admin group for 50-100 servers
Hello all,
I have to do a security audit and find out which users are in local admin groups. I have 50-100 servers windows 2003 and 2008. I also want to add a user called pmbuild to the local admin group on all the servers. Can this be done through GPO?
Any help would be great.
February 4th, 2012 6:12pm
Hello,
some time ago i found this script in another thread:
'You can use the script below to generate a report on local Administrators and Power Users. Copy it into a text file and rename it with the .vbs extension. Run it from the domain controller. For the computers you are auditing, you must have Administrator
privileges and be able to access the computer's RPC ports. The output is tab delimited and can be opened in Excel.
'--------------------------------------------------------------------------------
Set oADInfo = CreateObject("ADSystemInfo")
Set oFso = WScript.CreateObject("Scripting.Filesystemobject")
Set oShell = WScript.CreateObject("Wscript.Shell")
LogPath = oShell.SpecialFolders("MyDocuments") + "\Privileged LocalUser Audit.txt"
AdsiPath = "WinNT://" + oADInfo.DomainShortName
tab = Chr(9)
' Connect to Active Directory
Set ADComputers = GetObject(AdsiPath)
ADComputers.Filter = Array("Computer")
' Open the log file
Set oLog = oFso.CreateTextfile(LogPath, true)
oLog.WriteLine "Privileged Local Users on Computers in the " + _
oADInfo.DomainDNSName + _
" domain."
oLog.WriteLine Now
oLog.WriteLine ""
oLog.WriteLine "Computer" + tab + _
"Administrators" + tab + _
"Administrators Groups" + tab + _
"Power Users" + tab + _
"Power Users Groups"
' Check each computer
For Each oComputer in ADComputers
' Trap any errors in case the user is unauthorized, the computer is inaccessible, etc.
On Error Resume Next
' Get the Administrators users and groups
AdminUsers = ""
AdminGroups = ""
Set objGroup = GetObject("WinNT://" & oComputer.Name & "/Administrators")
If Not(Err.Number = 0) Then
AdminUsers = Err.Number
AdminGroups = Err.Number
End If
For Each objUser In objGroup.Members
If objUser.Class = "User" Then
AdminUsers = AdminUsers + objUser.Name + "; "
else
AdminGroups = AdminGroups + objUser.Name + "; "
end if
Next
' Get the Power Users users and groups
PowerUsers = ""
PowerGroups = ""
Set objGroup = GetObject("WinNT://" & oComputer.Name & "/PowerUsers")
If Not(Err.Number = 0) Then
PowerUsers = Err.Number
PowerGroups = Err.Number
End If
For Each objUser In objGroup.Members
If objUser.Class = "User" Then
PowerUsers = PowerUsers + objUser.Name + "; "
else
PowerGroups = PowerGroups + objUser.Name + "; "
end if
Next
' Output to the log
oLog.WriteLine oComputer.Name + tab + _
AdminUsers + tab + _
AdminGroups + tab + _
PowerUsers + tab + _
PowerGroups
Next
' Close log file handle, open the log in Notepad
oLog.Close
oShell.Run "notepad.exe """ + LogPath + """"
' Clean up
Set ADComputers = Nothing
Set oADInfo = Nothing
Set oFso = Nothing
Set oLog = Nothing
Set oLog = Nothing
Set oShell = Nothing
'--------------------------------------------------------------------------------
Do not forget to RUN IT IN A LAB BEFORE USING on production domains.
Best regards Meinolf Weber Disclaimer: This posting is provided "AS IS" with no warranties or guarantees , and confers no rights.
Free Windows Admin Tool Kit Click here and download it now
February 4th, 2012 6:24pm
I also want to add a user called pmbuild to the local admin group on all the servers. Can this be done through GPO
yes, Computer Config, Policies, Windows Settings, Security Settings, Restricted Groups
February 4th, 2012 6:47pm
Thanks for the help, when I run the scrpit it say error on line 18 permission denied. I'm running the script on a dc with a domain admin credentials. I have a domain called test.local. Do I need to modify the script to include my domain name?
Free Windows Admin Tool Kit Click here and download it now
February 4th, 2012 8:02pm
The script does not need to run on a DC. It can be run on any domain joined computer. The error message indicates line 18 in your copy of the script. If this is the line with the "oLog.WriteLine" statment, then you must lack permissions to write in LogPath.
Check if the text file was created. Maybe you need to specify a different path.
Richard Mueller - MVP Directory Services
February 4th, 2012 9:08pm
That script looks like mine. Like most administrative scripts, it should be run at a command prompt using the cscript host program, so the output can be redirected to a text file. For example, if this program is saved in the file LocalAdmins.vbs, use the
following at the command prompt of any computer joined to the domain:
cscript //nologo LocalAdmins.vbs > report.txt
This assumes you are in the folder where the file LocalAdmins.vbs is saved. Otherwise you must specify the full path to the file. The file report.txt is created in the current folder. The optional "//nologo" suppresses logo information, so it is not included
in the new file.
To restrict the program to the computers in one OU, modify the base of the query. As written, the base is the entire domain. The value of the variable strDNSDomain will be the distinguished name of the domain. Just replace with the distinguished name of
an OU. For example, change this:
strBase = "<LDAP://" & strDNSDomain & ">"
to this, where the DN of your ou is "ou=Sales,ou=West,dc=MyDomain,dc=com":
strBase = "<LDAP://ou=Sales,ou=West,dc=MyDomain,dc=com>"
Does this help?Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
February 7th, 2012 12:02am
Awesome, thanks Richard. It was your script. I called the script localadmins.vbs. Saved it to my c:drive. Ran it throught command prompt with
cscript //nologo LocalAdmins.vbs > report.txt
And I got the result I was looking for. I now have to create an OU and modify the script.
February 7th, 2012 1:02am
Thanks for the help, I'm using a domain admin account on a windows 2008 server to run the script. I get the error on line 18 permission denied but the text file still opens but without any information. It shows computer administrator, administrators
group, power users but no information below.
How would I specify a diffirent path? I noticed the file got saved to the my documents folder.
Free Windows Admin Tool Kit Click here and download it now
February 11th, 2012 12:18pm
On a side note. I found the script below on an older post. This scripts works, it displays the results I'm looking for but doesn't create a txt file. I checked my documents but it wasn't listed. Where would the txt file be located so I can check if it's
there?
Also, the script worked in my testing environment but I will to run the script against a OU with 50 computers. How do I do that?
Option
Explicit
Dim objLocalGroup, strComputer, objShell
Dim adoCommand, adoConnection, strBase, strFilter, strAttributes
Dim objRootDSE, strDNSDomain, strQuery, adoRecordset
' The wshShell object is required by the IsConnectible function.
Set objShell = CreateObject("Wscript.Shell")
' Setup ADO objects.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
Set adoCommand.ActiveConnection = adoConnection
' Search entire Active Directory domain.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
strBase = "<LDAP://" & strDNSDomain &
">"
' Filter on computer objects.
strFilter = "(objectCategory=computer)"
' Comma delimited list of attribute values to retrieve.
strAttributes = "sAMAccountName"
' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter &
";" & strAttributes &
";subtree"
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 200
adoCommand.Properties("Timeout") = 30
adoCommand.Properties("Cache Results") =
False
' Run the query.
Set adoRecordset = adoCommand.Execute
' Enumerate the resulting recordset.
Do Until adoRecordset.EOF
' Retrieve computer name.
strComputer = adoRecordset.Fields("sAMAccountName").Value
' Strip off trailing "$" character.
strComputer = Left(strComputer, Len(strComputer) - 1)
' Ping computer to see if online.
If (IsConnectible(strComputer, 1, 750) = True)
Then
' Bind to local Administrators group.
' Trap error if something is wrong.
On
Error Resume Next
Set objLocalGroup = GetObject("WinNT://" & strComputer _
&
"/Administrators,group")
If (Err.Number = 0)
Then
On Error GoTo 0
Wscript.Echo
"Computer: " & strComputer
' Enumerate members of the local group.
Call EnumLocalGroup(objLocalGroup, strComputer)
Else
Wscript.Echo
"Computer " & strComputer _
&
" unable to bind to local Administrators group"
Wscript.Echo
" Error Number: " & CStr(Err.Number)
Wscript.Echo
" Description: " & Err.Description
On Error GoTo 0
End
If
Else
Wscript.Echo
"Computer " & strComputer & " is not available"
End If
' Move to the next record in the recordset.
adoRecordset.MoveNext
Loop
' Clean up.
adoRecordset.Close
adoConnection.Close
Sub EnumLocalGroup(ByVal objGroup,
ByVal strComputer)
' Subroutine to enumerate members of local group.
Dim objMember, strClass, strPath
' Enumerate direct members of group.
For Each objMember
In objGroup.Members
strClass = objMember.Class
strPath = objMember.ADsPath
Wscript.Echo
" Member: " & strPath & " (" & strClass &
")"
' Test if member is a group.
If (LCase(strClass) =
"group") Then
' Nested group. Test if objMember is a local group.
If (InStr(LCase(strPath), "/" _
& LCase(strComputer) &
"/") > 0) Then
' objMember is a local group.
' Call sub recursively to enumerate nested local group.
Call EnumLocalGroup(objMember, strComputer)
ElseIf (InStr(LCase(strPath), _
"/nt authority/") > 0)
Then
' objMember is local implicit group (special identity).
' Membership cannot be enumerated.
Else
' objMember is a domain group.
' Do not enumerate membership.
End If
End
If
Next
End Sub
Function IsConnectible(ByVal strHost,
ByVal intPings, ByVal intTO)
' Returns True if strHost can be pinged.
' strHost is the NetBIOS name or IP address of host computer.
' intPings is number of echo requests to send.
' intTO is timeout in milliseconds to wait for each reply.
' Based on a program by Alex Angelopoulos and Torgeir Bakken,
' as modified by Tom Lavedas.
' Variable objShell has global scope and must be declared
' and set in the main program.
' Requires Windows NT or above.
' Modified 09/14/2010 to search for "Reply from" instead of "TTL=".
Dim lngResult
If (intPings =
"") Then
intPings = 2
End If
If (intTO =
"") Then
intTO = 750
End If
lngResult = objShell.Run("%comspec% /c ping -n " & intPings _
& " -w " & intTO &
" " & strHost _
& " | find ""Reply from"" > nul 2>&1", 0, True)
Select
Case lngResult
Case 0
IsConnectible =
True
Case
Else
IsConnectible =
False
End Select
End Function
February 11th, 2012 1:02pm
That script looks like mine. Like most administrative scripts, it should be run at a command prompt using the cscript host program, so the output can be redirected to a text file. For example, if this program is saved in the file LocalAdmins.vbs, use the
following at the command prompt of any computer joined to the domain:
cscript //nologo LocalAdmins.vbs > report.txt
This assumes you are in the folder where the file LocalAdmins.vbs is saved. Otherwise you must specify the full path to the file. The file report.txt is created in the current folder. The optional "//nologo" suppresses logo information, so it is not included
in the new file.
To restrict the program to the computers in one OU, modify the base of the query. As written, the base is the entire domain. The value of the variable strDNSDomain will be the distinguished name of the domain. Just replace with the distinguished name of
an OU. For example, change this:
strBase = "<LDAP://" & strDNSDomain & ">"
to this, where the DN of your ou is "ou=Sales,ou=West,dc=MyDomain,dc=com":
strBase = "<LDAP://ou=Sales,ou=West,dc=MyDomain,dc=com>"
Does this help?Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
February 11th, 2012 4:06pm
Awesome, thanks Richard. It was your script. I called the script localadmins.vbs. Saved it to my c:drive. Ran it throught command prompt with
cscript //nologo LocalAdmins.vbs > report.txt
And I got the result I was looking for. I now have to create an OU and modify the script.
February 11th, 2012 5:06pm
I figured it out.. I looked in the attribute editor and although the OU name looked correct in AD, it was named differently. Thanks for all your help, I really appreciate it!
Free Windows Admin Tool Kit Click here and download it now
April 8th, 2012 8:30am
Thank you for the help. I got it to work.. Now is there a way to output this into a CSV file for the auditors instead of having the message box pop up for every one? Thanks again for all the help.
April 8th, 2012 8:45am
Run the script at a command prompt using the cscript host program, so you can redirect the output to a text file. For example, if the VBScript is saved in a file named GetMembers.vbs use a command similar to:
cscript //nologo GetMembers.vbs > Members.csv
The optional //nologo parameter suppresses logo information, so it doesn't show up in the file. The above assumes you are in the folder where the file GetMembers.vbs is saved. Otherwise, you must specify the full path to the file. The new file Members.csv
is created in the current folder.
You mention a csv file, but the program doesn't output in comma delimited format. It could be modified for this, but then there would be only one line of output. Is that really what you want? Or is the text file created with the above statement sufficient?
Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
April 8th, 2012 9:22am
That is beautiful! I should have re-read the whole post before asking, sorry about that. Ugh, Monday!
I will try with the CSV, I might be able to work it in Excel. If not, I will go with txt.
Thanks again sir, you saved me!
April 8th, 2012 9:38am
I decided that a csv format perhaps does make sense here. I modified the script to output one line per computer. The first field in each line is the name of the computer. Each subsequent field (comma delimited) is the ADsPath of each direct member
of the group (Administrators in this case). I use the ADsPath so you can see if the member is local or domain. I don't show the class of the member in this case. I haven't tested, but I think this should work:
Option
Explicit
Dim objLocalGroup, strComputer, objShell
Dim adoCommand, adoConnection, strBase, strFilter, strAttributes
Dim objRootDSE, strDNSDomain, strQuery, adoRecordset
Dim strOutput
' The wshShell object is required by the IsConnectible function.
Set objShell = CreateObject("Wscript.Shell")
' Setup ADO objects.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
Set adoCommand.ActiveConnection = adoConnection
' Search entire Active Directory domain.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
strBase = "<LDAP://" & strDNSDomain &
">"
' Filter on computer objects.
strFilter = "(objectCategory=computer)"
' Comma delimited list of attribute values to retrieve.
strAttributes = "sAMAccountName"
' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter &
";" & strAttributes &
";subtree"
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 200
adoCommand.Properties("Timeout") = 30
adoCommand.Properties("Cache Results") =
False
' Run the query.
Set adoRecordset = adoCommand.Execute
' Enumerate the resulting recordset.
Do Until adoRecordset.EOF
' Retrieve computer name.
strComputer = adoRecordset.Fields("sAMAccountName").Value
' Strip off trailing "$" character.
strComputer = Left(strComputer, Len(strComputer) - 1)
' Ping computer to see if online.
If (IsConnectible(strComputer, 1, 750) = True)
Then
' Bind to local Administrators group.
' Trap error if something is wrong.
On
Error Resume
Next
Set objLocalGroup = GetObject("WinNT://" & strComputer _
&
"/Administrators,group")
If (Err.Number = 0)
Then
On
Error GoTo 0
strOutput = strComputer
' Enumerate members of the local group.
Call EnumLocalGroup(objLocalGroup, strComputer)
Wscript.Echo strOutput
Else
Wscript.Echo strComputer _
&
" unable to bind to local Administrators group"
On
Error GoTo 0
End
If
Else
Wscript.Echo strComputer &
" is not available"
End
If
' Move to the next record in the recordset.
adoRecordset.MoveNext
Loop
' Clean up.
adoRecordset.Close
adoConnection.Close
Sub EnumLocalGroup(ByVal objGroup,
ByVal strComputer)
' Subroutine to enumerate members of local group.
' Variable strOutput has global scope.
Dim objMember, strClass, strPath
' Enumerate direct members of group.
For
Each objMember In objGroup.Members
strClass = objMember.Class
strPath = objMember.ADsPath
strOutput = strOutput &
"," & strPath
' Test if member is a group.
If (LCase(strClass) =
"group") Then
' Nested group. Test if objMember is a local group.
If (InStr(LCase(strPath),
"/" _
& LCase(strComputer) &
"/") > 0) Then
' objMember is a local group.
' Call sub recursively to enumerate nested local group.
Call EnumLocalGroup(objMember, strComputer)
ElseIf (InStr(LCase(strPath), _
"/nt authority/") > 0)
Then
' objMember is local implicit group (special identity).
' Membership cannot be enumerated.
Else
' objMember is a domain group.
' Do not enumerate membership.
End
If
End
If
Next
End Sub
Function IsConnectible(ByVal strHost,
ByVal intPings, ByVal intTO)
' Returns True if strHost can be pinged.
' strHost is the NetBIOS name or IP address of host computer.
' intPings is number of echo requests to send.
' intTO is timeout in milliseconds to wait for each reply.
' Based on a program by Alex Angelopoulos and Torgeir Bakken,
' as modified by Tom Lavedas.
' Variable objShell has global scope and must be declared
' and set in the main program.
' Requires Windows NT or above.
' Modified 09/14/2010 to search for "Reply from" instead of "TTL=".
Dim lngResult
If (intPings =
"") Then
intPings = 2
End
If
If (intTO =
"") Then
intTO = 750
End
If
lngResult = objShell.Run("%comspec% /c ping -n " & intPings _
&
" -w " & intTO & " " & strHost _
&
" | find ""Reply from"" > nul 2>&1", 0, True)
Select
Case lngResult
Case 0
IsConnectible =
True
Case
Else
IsConnectible =
False
End
Select
End Function
-----
Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
April 8th, 2012 10:47am
I am getting an error:
C:\Scripts\adminvbs.vbs(36, 1) Active Directory: There is no such object on the server.
Here is line 36: Set adoRecordset = adoCommand.Execute
Any advice for me?
Thank you
April 8th, 2012 11:32am
That line raises an error if the script cannot contact a Domain Controller in the specified domain. Either the base of the query is incorrect (the value assigned to the variable strBase), or a Domain Controller cannot be contacted, or the computer where
the script is running is not joined to a domain, or you are logged into the computer locally (instead of into the domain).
Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
April 8th, 2012 1:03pm
Just tested it and it works great. Thank you!
Another quick question though.. I had to change the strBase because I didnt want to scan the entire structure, just some OUs. Here is line 21.
strBase = "<LDAP://ou=Laptops, ou=Client Computers - Windows 7, dc=mydomain, dc=com>"
This works just fine, but as soon as I add another sub OU to the code:
strBase = "<LDAP://ou=XX, ou=Laptops, ou=Client Computers - Windows 7, dc=mydomain, dc=com>"
the script returns an error stating:
C:\Scripts\Admin1.vbs(37, 1) Active Directory: There is no such object on the server.
Am I doing something wrong or is there a limitation?
April 8th, 2012 4:45pm
There is no limitation, either in the length of the distinguished name, or in the depth of the OU structure. The error indicates there is no "ou=XX" within "OU=Laptops".
Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
April 8th, 2012 6:17pm
Hello,
Thanks again, do you know how I would change the script above to disaply the users in the power user and users group?
Regards,
K
April 8th, 2012 11:48pm
Instead of binding to the Administrators group with this statement:
Set objLocalGroup = GetObject("WinNT://" & strComputer _
& "/Administrators,group")
-----
You can bind to the Power Users group:
Set objLocalGroup = GetObject("WinNT://" & strComputer _
& "/Power Users,group")
-----
or, to the Users group:
Set objLocalGroup = GetObject("WinNT://" & strComputer _
& "/Users,group")
-----
Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
April 9th, 2012 4:14am
This is great. I was just wondering though, how can I change this to instead of searching all of AD, just pull from a text file with a list of servers?
May 1st, 2012 4:42pm
This maybe should be split into a separate question. In any case, here is the main program modified (but not tested) to read computer names from a text file, one name per line. The EnumLocalGroup and IsConnectible methods would be unchanged and are
not included here:
Option
Explicit
Dim objLocalGroup, strComputer, objShell
Dim strOutput
Dim strFile, objFSO, objFile
Const ForReading = 1
' Specify file of computer names.
strFile = "c:\Scripts\Computers.txt"
' Open file for reading.
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(strFile, ForReading)
' The wshShell object is required by the IsConnectible function.
Set objShell = CreateObject("Wscript.Shell")
' Read the file.
Do Until objFile.AtEndOfStream
strComputer = Trim(objFile.ReadLine)
' Skip blank lines.
If (strComputer <>
"") Then
' Ping computer to see if online.
If (IsConnectible(strComputer, 1, 750) = True)
Then
' Bind to local Administrators group.
' Trap error if something is wrong.
On
Error Resume
Next
Set objLocalGroup = GetObject("WinNT://" & strComputer _
&
"/Administrators,group")
If (Err.Number = 0)
Then
On
Error GoTo 0
strOutput = strComputer
' Enumerate members of the local group.
Call EnumLocalGroup(objLocalGroup, strComputer)
Wscript.Echo strOutput
Else
Wscript.Echo strComputer _
&
" unable to bind to local Administrators group"
On
Error GoTo 0
End
If
Else
Wscript.Echo strComputer &
" is not available"
End
If
End
If
Loop
' Clean up.
objFile.Close
-----
Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
May 1st, 2012 6:36pm
Thanks for the update!
When I run this, I receive an error:
<23,5> Microsoft VBScript runtime error: Variable is undefined: 'strLine'
May 2nd, 2012 2:17pm
Sorry, instead of strLine is should be strComputer. The line should be:
If (strComputer <> "") Then
I've corrected the code in my reply above.
Richard Mueller - MVP Directory Services
Free Windows Admin Tool Kit Click here and download it now
May 2nd, 2012 8:11pm