Often you need to run some script on a bunch of computers. A standard way is to use logon/logoff or startup/shutdown scripts via GPO or user profile, but that means interrupting users. So, if you want to use psexec or similar to execute some command on a bunch of computers right now without forcing logout or reboot, the first thing you need to do is generate a list of computers!
Below, I show several ways to enumerate computers (there are more, and I will add them as I get around to it). Once you have enumerated a set of computers, you'll want to collect information or make changes on those systems. So, typically these scripts end with an ACTION clause where you can plug in commands to gather that info or make those changes. For some ideas on how to do that, see the WMIC Snippets page.
Query Active Directory
We'll use dsquery and dsget. They are available by default on W2003. You can install them on an XP or Vista system with the W2003 Adminpak. MS recommends you do not run them on W2000 domain controllers, although you can use them to query or manipulate directory objects on W2000 domains.
There are a couple things of note in this script:
- Here we are returning a list of computer accounts in a domain. We will not see computers which have no account in the domain!
- dsget can't return the name of the computer explicitly; it returns the samid which is the name with a $ appended. By using the $ as a delim in the FOR statement we strip it off.
- We chain together two IF statements in the NAME section to act like a brain-dead select case statement. This is because dsget returns a header line and a footer line that we don't want in the output.
- Obviously both dsget and dsquery allow many other parameters which we can query by, or return in our query. Exploring the options of these two commands should suggest many other ways you could use this script.
- The final label is an ACTION clause; you could put whatever actions you want in this statement. For purposes of illustration, the script example just lists the names of the computers it found. Other scripts on this page follow the same
@echo off
for /f "tokens=1 delims=$" %%a in ('"dsquery computer -name * |dsget computer -samid"') do call :NAME %%a
goto :eof
:NAME
if {%1} NEQ {samid} if {%1} NEQ {dsget} call :ACTION %1
goto :eof
:ACTION
::%1 is the name (hostname only) of a computer as enumerated by above code
echo %1
Another, more flexible way to query AD for computers is via WMI. Run the following WMIC commands in the cmd shell and contemplate the results. (Each bulleted entry should copied/pasted as one line in cmd.)
- wmic /namespace:
ootdirectoryLDAP path ds_computer get DS_Name
- wmic /namespace:
ootdirectoryLDAP path ds_computer where (DS_operatingSystem like '%server%') get DS_Name, DS_OperatingSystem, DS_operatingSystemServicePack
- wmic /namespace:
ootdirectoryLDAP path ds_computer where (DS_Name = 'avalidcomputername') get DS_Name, DS_OperatingSystem, DS_operatingSystemServicePack
Query NT4-style domains
Another means of querying computer accounts in a domain is to use netdom.exe from the XP Support Tools. It's a free download. This will work on both AD and NT4-style domains, but is slower than the dsquery method given above.
@echo off
setlocal
for /f "tokens=1" %%a in ('netdom query /domain:yourdomainname workstation') do call :ACTION %%a
goto :eof
:ACTION
::%1 is the name (hostname only) of a computer as enumerated by above code
echo %1
Query DNS
Here, I break out the vbscript. Just like in the cmd scripts above, the final bit is an action which you can change to do whatever you like. You can use the vbscript RunCmd function to run commandline programs or scripts. In line 98, I show RunCmd calling psexec to get the ipconfig /all info from each system found in DNS.
This script will enumerate every A record found in all DNS zones on whatever DNS server you specify.
-
'DNS query script; returns all forward zones and their type A records
-
'WMI DNS Provider docs: http://msdn2.microsoft.com/en-us/library/ms682123.aspx
-
'also helpful:
-
'http://techtasks.com/code/viewbookcode/25 by Cricket Liu, Matt Larson & Robbie Allen
-
'http://www.source-code.biz/snippets/vbscript/3.htm by Christian d'Heureuse
-
-
option explicit
-
On Error Resume Next
-
-
Dim strServer, strUsername, strPassword, objPW
-
Wscript.StdOut.Write "Enter DNS server to query: "
-
strServer = Wscript.StdIn.ReadLine
-
Wscript.StdOut.Write "Enter your username: "
-
strUsername = Wscript.StdIn.ReadLine
-
strPassword = Password()
-
Wscript.StdOut.Write vbCR
-
Wscript.Echo "DNS is being queried (may take a moment)..."
-
-
Dim objLocator, objDNS, objDNSServer, objZones
-
Set objLocator = CreateObject("WbemScripting.SWbemLocator")
-
Err.Clear
-
Set objDNS = objLocator.ConnectServer(strServer, "rootMicrosoftDNS", strUsername, strPassword)
-
If Err.Number <> 0 Then Call ExitError("connect to server",Err.Number, Err.Description, Err.Source)
-
set objDNSServer = objDNS.Get("MicrosoftDNS_Server.Name="".""")
-
If Err.Number <> 0 Then Call Error("get server name",Err.Number, Err.Description, Err.Source)
-
set objZones = objDNS.ExecQuery("Select * from MicrosoftDNS_Zone where DnsServerName = '" & _
-
objDNSServer.Name & "'" & " and Reverse='FALSE'")
-
If Err.Number <> 0 Then Call Error("query zones",Err.Number, Err.Description, Err.Source)
-
WScript.Echo objDNSServer.Name
-
If Err.Number <> 0 Then Call Error("echo servername",Err.Number, Err.Description, Err.Source)
-
Dim objZone, objRR, objRecord
-
for each objZone in objZones
-
WScript.Echo " " & objZone.Name
-
set objRR = objDNS.ExecQuery("Select * from MicrosoftDNS_AType " & _
-
"where ContainerName = '" & objZone.Name & "'")
-
If Err.Number <> 0 Then Call Error("query A records",Err.Number, Err.Description, Err.Source)
-
for Each objRecord in objRR
-
Call Action(objRecord.OwnerName,objRecord.recordData )
-
next
-
WScript.echo " --End of '" & objZone.Name & "' zone A Records--"
-
next
-
-
Function Run (ByVal cmd)
-
Dim sh: Set sh = CreateObject("WScript.Shell")
-
Dim wsx: Set wsx = Sh.Exec(cmd)
-
If wsx.ProcessID = 0 And wsx.Status = 1 Then
-
' (The Win98 version of VBScript does not detect WshShell.Exec errors)
-
Err.Raise vbObjectError,,"WshShell.Exec failed."
-
End If
-
Do
-
Dim Status: Status = wsx.Status
-
WScript.StdOut.Write wsx.StdOut.ReadAll()
-
WScript.StdErr.Write wsx.StdErr.ReadAll()
-
If Status <> 0 Then Exit Do
-
WScript.Sleep 10
-
Loop
-
Run = wsx.ExitCode
-
End Function
-
-
Function RunCmd (ByVal cmd)
-
On Error Goto 0
-
RunCmd = Run("%ComSpec% /c " & cmd)
-
End Function
-
-
function Password()
-
'XP and W2003 include scriptpw.dll by default; other versions of the OS do not.
-
'(You can manually add it to c:windowssystem32)
-
'On OS's without scriptpw.dll, password WILL be echoed to screen.
-
'If scriptpw.dll is present, password will be hidden
-
On Error Resume Next
-
Err.Clear
-
Set objPW = CreateObject("ScriptPW.Password")
-
If Err.Number <> 0 Then
-
Wscript.StdOut.Write "Enter your password: "
-
Password = Wscript.StdIn.ReadLine
-
Else
-
Wscript.StdOut.Write "Enter your password: "
-
Password = objPW.GetPassword()
-
End If
-
Wscript.StdOut.Write vbCRLF & vbCRLF
-
end function
-
-
sub Error(Location, Number, Description, Source)
-
Wscript.Echo "ERROR at " & Location & ": " & Source & " | " & Number & " | " & Description
-
End Sub
-
-
sub ExitError(Location, Number, Description, Source)
-
On Error Goto 0
-
Wscript.Echo
-
Wscript.Echo "ERROR at " & Location & ": " & Source & " | " & Number & " | " & Description
-
wscript.Echo "Script cannot continue. Exiting."
-
Wscript.Quit
-
End Sub
-
-
sub Action(strHostname, strHostIP)
-
On Error Goto 0
-
Wscript.Echo " " & strHostname & " - " & strHostIP
-
RunCmd("psexec " & strHostname & " -u purgatoryquuxda -p mypassword ipconfig /all" )
-
End Sub
Here's what the output looks like:
H:>cscript dnsq.vbs Microsoft (R) Windows Script Host Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.
Enter DNS server to query: ad1 Enter your username: quuxadmin Enter your password:
DNS is being queried (may take a moment)...
ad1.cojones.org cojones.org ad1.cojones.org - 10.0.0.2
Windows IP Configuration
Host Name . . . . . . . . . . . . : ad1 Primary Dns Suffix . . . . . . . : cojones.org Node Type . . . . . . . . . . . . : Unknown IP Routing Enabled. . . . . . . . : No WINS Proxy Enabled. . . . . . . . : No DNS Suffix Search List. . . . . . : cojones.org
Ethernet adapter Local Area Connection:
Connection-specific DNS Suffix . : Description . . . . . . . . . . . : VMware Accelerated AMD PCNet Adapter Physical Address. . . . . . . . . : 00-0C-29-73-48-7F DHCP Enabled. . . . . . . . . . . : No IP Address. . . . . . . . . . . . : 10.0.0.2 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : 10.0.0.10 DNS Servers . . . . . . . . . . . : 10.0.0.2 10.0.0.3
PsExec v1.72 - Execute processes remotely Copyright (C) 2001-2006 Mark Russinovich Sysinternals - www.sysinternals.com
ipconfig exited on ad1.cojones.org with error code 0. ad2.cojones.org - 10.0.0.3
Windows IP Configuration
Host Name . . . . . . . . . . . . : ad2 Primary Dns Suffix . . . . . . . : cojones.org [...output terminated, you get the idea ...]
|
Query DHCP Leases
I had always envisioned writing a script that would parse the output of NETSH DHCP SERVER EXPORT DHCP.TXT ALL to return the IPs of every system with a current lease. Well, when I finally got around to looking into this, I discovered the embarrassing truth: DHCP.TXT isn't all text! It's full of annoying binary entries that beep when you TYPE out the file in cmd.
I did a quick search for WMI ways to query the DHCP database and came up dry. So for the forseeable future, this script idea will remain on the back burner.
Query whole subnets
Sometimes you just need to plod through every IP in the subnet. I have a script for this, but it's too long for this page. See the ping arp subnet page, which shows how to ping all IPs in a range. But of course you can change the ping logic to do something else if you like!
Query arp tables (on PCs)
to do
Query arp tables (on switches)
to do
Query NETBIOS
to do