In-Depth
Automate Your Administration
Speed up and simplify your work by applying these 10 scripts to the job.
- By Chris Brooke
- 03/01/2002
There’s an old joke that goes, “How many computer programmers does it take to
screw in a light bulb?” Answer: “Can’t be done. That’s a hardware problem.” While
that may have been true “back in the day” (back when it was funny), it no longer
truly represents life in IT. The lines have blurred. Developers now take an active
role in choosing and assembling hardware for their applications. By the same token,
network administrators have found themselves in the unenviable position of having
to hone software-development skills in order to speed and simplify their administrative
tasks, lest they end up spending every night at the office. As the father of a
1-year-old, believe me when I say that I would rather be home bouncing said munchkin
on my knee than in the server room waiting for a backup to complete so that I
can finish the rest of the server maintenance.
Enter administrative scripting.
Whether you’re a network administrator looking to speed up certain mundane
tasks or a closet code-monkey who likes to automate everything you possibly
can, scripting can simplify your life and help ensure you get plenty of quality
munchkin-time. To that end, I’ve trudged through mountains of scripts and narrowed
them down to 10 that you’re sure to find indispensable. As part of my search
criteria, I looked for scripts that not only accomplished a vital task, but
also could be easily modified so that you can extend them as your scripting
skills evolve. Along with each script represented here, I’ve highlighted relevant
sections and included sample use-case information.
Scripts
in a Zip |
You can download the complete set of scripts by right-clicking
on this link and saving the file to disk: Scripting.zip
(29KB). As well, each script below is accompanied by a text
download if you're interested only in that particular listing.
Problems downloading this script? Send e-mail to
michael.domingo@mcpmag.com;
put "Scripting.zip" on subject line of message.
|
|
|
One final caveat before we dive in to these scripts: I’ve left them in their
original form, whether or not copyright was explicitly specified. As such, you’ll
notice differences in how variables are declared—with or without some form of
Hungarian Notation, formatting, comments and so on. Most are well commented
and you should be able to interpret them for future editing. Where necessary,
I’ve provided additional explanation in the text discussing the script.
DumpUsers.vbs by Tim Hill
A few years ago, in my column, “Windows Utilities Inside Out,” I highlighted
a utility called DumpACL. It exports NT user and group information, along with
all NTFS Access Control Lists (ACLs) for a given computer. No column before
or since has generated as many inquiries. To this day, I get readers e-mailing
me requesting it. This script was published by Tim Hill in his book, Windows
2000 Windows Script Host. It uses Active Directory Services Interface (ADSI)
to perform half of the DumpACL operation—namely: dumping users. The benefit
of this script is that it dumps extensive user information, including the User
Flags, into a nicely formatted Excel spreadsheet. Below I’ve highlighted some
of the relevant code, which exists almost entirely in the MAIN( ) function.
'////////////////////////////////////////////////////////////////////////////
' $Workfile: DumpUsers.vbs $ $Revision: 2 $ $Date: 7/19/99 12:19a $
' $Archive: /TimH/Projects (SCC)/Books & Articles/Macmillan/Windows 2000
' System Scripting (1999)/Scripts/DumpUsers.vbs $
' Copyright (c) 1998 Tim Hill. All Rights Reserved.
////////////////////////////////////////////////////////////////////////////
' Dump user accounts to an Excel spreadsheet
...
Function Main
Trace 1, "+++Main"
Dim sAdsPath, sExcelPath, oSheet, oExcel, oUser, oAdsObj
…
End If
sAdsPath = "WinNT://" & Wscript.Arguments(0)
sExcelPath = g_oFSO.GetAbsolutePathName(Wscript.Arguments(1))
' Prepare ADSI computer/domain object
On Error Resume Next
Set oAdsObj = GetObject(sAdsPath)
If Err.Number <> 0 Then
Wscript.Echo sAdsPath & ": not found (0x" & Hex(Err.Number) & ")"
Wscript.Quit(Err.Number)
End If
On Error Goto 0
…
' Enumerate all users in the computer/domain
oAdsObj.Filter = Array("User") ' Filter user accounts only
ix = 0
For Each oUser In oAdsObj '
For each user account...
DumpAccount oSheet, ix, oUser ' Go add to sheet
ix = ix + 1 ' Bump index
Next
...
' Return value is passed to Wscript.Quit as script exit
code
Main = 0
End Function
[To download a text file of this script, right-click here
and "Save As."—Ed.]
From Windows 2000 Windows Script
Host, by Tim Hill
ISBN 1578701392, $35
Copyright 2000; reproduced by permission of New
Riders (To order, go to LearningStore.com.)
|
|
After all the “snipping,” we can see the main logic of the script. It uses
the WScript.Arguments collection to retrieve the computer/domain that you want
to work with. Indeed, it speeds things up if you specify the type of object
you want to work when starting the script, but it’s by no means required. For
example, at the command line, you can enter:
C:\cscript dumpusers mycomputer,computer c:\myspreadsheet.xls
Or simply type this:
C:\cscript dumpusers mycomputer c:\myspreadsheet.xls
and let the script figure it out for itself.
It then uses ADSI and applies a filter so that only users will be dumped (I’m
seeing great possibilities already! With just a little editing, we could dump
groups, computers in a domain and so on) It then uses another SUB, “DumpAccount,”
to place the information in the spreadsheet. Figure 1 shows the spreadsheet
I received when I ran the script on my computer.
|
Figure 1. DumpACL generates an Excel report of users.
(Click image to view larger version.) |
This script offers significant advantages over standard GUI tools or even the
DumpACL utility. For instance, with very little editing, you could modify this
script to take users from a spreadsheet and add them to a computer or domain
(a task that, depending upon the size of your organization, you, no doubt, perform
quite often).
AddUsers.vbs by Microsoft
Just in case you don’t have the time to edit (or simply don’t feel like editing)
the previous script to add users, I’ve included this script from the MSDN Scripting
Web site. It’s also included in the Windows NT Server 4.0 Resource Kit. It reads
a list of users from an Excel spreadsheet and adds them using ADSI. This script
is somewhat shorter than Dump Users.vbs, as there’s no spreadsheet formatting
to include. It comes with a spreadsheet already configured in the proper format—all
you have to do is add the names. Also, this script is designed to add users
to Active Directory using the LDAP provider, rather than into an NT domain using
the WinNT provider. If you’re still using NT domains (and, according to the
latest numbers, there are a lot of you!), this script can also be easily edited
to work with the WinNT provider.
' Windows Script Host Sample Script
'
' ------------------------------------------------------------------------
' Copyright (C) 1996 Microsoft Corporation
'
…
' The sample uses the directory root
' "LDAP://DC=ArcadiaBay,DC=Com,O=Internet"
' Change the directory path in the EXCEL spreadsheet to match your DS
' before running this sample.
...
Do While oXL.activecell.Value <> ""
' if the requested OU is a new one...
If oXL.activecell.Value <> ou Then
' Pick up the OU name...
ou = oXL.activecell.Value
' Compose the ADSI path...
s = "LDAP://" + ou+"," + root
' Show it to the user...
WScript.Echo s
' And get the object
Set c = GetObject(s)
End If
' Compose the user common name name from first and last
names...
uname = "CN=" + oXL.activecell.offset(0, 1).Value + "" +
oXL.activecell.offset (0,2).Value
' Create the new user object...
Set u = c.Create("user", uname)
' Set the properties of the new user
u.Put "givenName", oXL.activecell.offset(0, 1).Value ' givenName
u.Put "sn", oXL.activecell.offset(0, 2).Value ' sn
u.Put "mail", oXL.activecell.offset(0, 3).Value ' Email
u.Put "sAMAccountName", oXL.activecell.offset(0, 4).Value ' Sam Acct
u.Put "telephoneNumber", oXL.activecell.offset(0, 5).Value ' Phone
' Enable the account, must change pw @ logon
u.Put "userAccountControl",16
' ... and update the DS
u.SetInfo
' Done with this object, discard it
Set u = Nothing
' Step to the next user...
oXL.activecell.offset(1, 0).Activate ' Next row
Loop
' ... rest of script
[To download the unabridged script as a text file, right-click here
and "Save As." This script also uses the example Addusers Excel spreadsheet,
which you can download here.—Ed.]
This script doesn’t use any user-defined SUBS or FUNCTIONS. Instead, the Do…
Loop shown contains the bulk of the script logic. The script uses automation
to open the spreadsheet, making it a simple matter to step through the cells
and acquire the user data. This is then transferred to the ADSI object and entered
as user information. Once the loop has reached the end of the list, script execution
terminates.
Adding users is a common, yet tedious, administrative task. This script simplifies
and speeds this process as-is. Also, you could tweak it a bit to perform such
tasks as group maintenance, modification of user rights and so on.
The
Future of Scripting: ASP.NET |
Although the scripts highlighted here were, for the most
part ,written for the Windows Script Host, they can be easily
adapted to provide their functionality to a Web page via Active
Server Pages. Consequently, ASP pages provide a wealth of
functionality from which you can extract scripts. Once all
references to intrinsic ASP objects have been removed, you’re
left with plain ol’ VBScript. At least, that’s how it has
been until now.
The Microsoft .NET framework has introduced a new Web platform
called ASP.NET. These Web pages are differentiated from standard
ASP pages by their extension “ASPX.” ASP.NET pages require
that the .NET Framework is installed on the Web server. They
can coexist side-by-side with ASP, but a different engine
handles processing for ASPX files. Why the difference? ASP.NET
is fully integrated with the .NET Framework. Rather than relying
on VBScript or JScript (which are just subsets of larger,
more complete development languages), ASP.NET pages can be
developed in any .NET language such as Visual Basic.NET, C#
or even Visual C++.NET. This gives Web pages the full power
of the underlying development languages.
You may ask, “How does this affect me?” Well, maybe not at
all—or perhaps a great deal. If your duties as a beloved “server-jockey”
include some Web development, it would be in your best interest
to start learning at least one of the .NET languages now.
If you currently use mostly JScript in your scripts, I recommend
J# or C#. If you have become accustomed to VBScript, take
a look at Visual Basic.NET.
I’m not trying to convert any of you into bonafide “code
monkeys” (perish the thought!), but the times, they are a-changin’.
In this era of volatile technology stocks and an uncertain
job market, the ones with the most skills win. See ya at the
finish line!
—Chris Brooke
|
|
|
Shortcut.vbs by Microsoft
This script almost didn’t make the first cut when I was trudging through the
volumes of possible programs to feature here. In the first place, it’s short
and doesn’t do much—only puts a shortcut on the desktop. However, the more I
thought about it, the more I realized it’s likely to end up being one of those
little scripts that you’ll use all the time. Originally, it was one of the samples
included as part of the Windows Script Host documentation download. Here’s the
bulk of it:
' Windows Script Host Sample Script
'
' ------------------------------------------------------------------------
' Copyright (C) 1996-1997 Microsoft Corporation
'
...
' Read desktop path using WshSpecialFolders object
DesktopPath = WSHShell.SpecialFolders("Desktop")
' Create a shortcut object on the desktop
Set MyShortcut = WSHShell.CreateShortcut(DesktopPath
& "\Shortcut to notepad.lnk")
' Set shortcut object properties and save it
MyShortcut.TargetPath= _
WSHShell.ExpandEnvironmentStrings("%windir%\notepad.exe")
MyShortcut.WorkingDirectory = _
WSHShell.ExpandEnvironmentStrings("%windir%")
MyShortcut.WindowStyle = 4
MyShortcut.IconLocation = _
WSHShell.ExpandEnvironmentStrings("%windir%\notepad.exe, 0")
MyShortcut.Save
WScript.Echo "A shortcut to Notepad now exists on your Desktop."
[To download an unabridged text file of this script, right-click here
and "Save As."—Ed.]
Not much on its own, but there’s some real benefit to be found in combining
this script with others. Just think for a minute about how many times you have
to look for something, such as the log file for your backup software or perhaps
the spreadsheet created by the “DumpUsers” script. Putting a shortcut on the
desktop automatically is a real time-saver. Left a script running overnight
and need to view the error log first thing in the morning? Put a shortcut to
it on your desktop. Need everyone in your office to download the latest virus-checking
software? Put a shortcut on their desktop! While the sample script has “Notepad.exe”
hard-wired into it as the application started from the shortcut, this can be
whatever you want. It can even be specified by arguments or as a persisted value
in a text file or database.
SvcStat.vbs by Jeff Honeyman
A large part of NT administration relates to services. Indeed, starting, stopping
and querying service settings such as Log On Account, Startup Mode and so on
are critical tasks that occur quite frequently. This script from Jeffrey Honeyman's
Scripting Windows 2000 uses Windows Management Instrumentation (WMI)
to query the status of a service on a given computer. Is the Scheduler service
running on the DC? Hmm… let’s check!
' SvcStat.vbs
' Copyright 2000 by Jeffrey Honeyman
' Published in Scripting Windows 2000 by McGraw-Hill Professional Publishing
...
Function FindComputer(box)
Set objBoxes=GetObject("LDAP://CN=computers,DC=domain,DC=com”)
For Each thing in objBoxes
If ucase(thing.cn) = ucase(box) Then
FindComputer=thing.ADsPath
Exit Function
End If
Next
End Function
Function FindDC(box)
Set objBoxes=GetObject("LDAP://OU=Domain Controllers,DC=domain,
DC=com")
For Each thing in objBoxes
If ucase(thing.cn) = ucase(box) Then
FindDC=thing.ADsPath
Exit Function
End If
Next
End Function
...
Set objSys=GetObject(patth)
Set objSvc=objSys.GetWMIServices
Set objServs=objSvc.instancesOf("WIN32_Service")
For Each thing in objServs
If ucase(thing.Properties_("DisplayName"))=ucase(serv)
Then
s="Properties for " & serv & " on " & box & ":" & vbCRLF & vbCRLF
For Each prop in thing.Properties_
s=s & prop.name & " = " & prop.value & vbCRLF
Next
End If
Next
...
msgbox s
[To download an unabridged text file of this script, right-click here
and "Save As."—Ed.]
The approach here is straightforward. The functions FindComputer and FindDC
are used to locate the specified computer, whether it’s a domain controller
or not. Once it has been located, WMI is used to get all the properties of the
specified service by comparing every WIN32 service to the one specified on the
command line. Once a match is found, the script builds and displays a msgbox.
This script stands alone as a valuable tool to query service information.
ProcStat.vbs by Jeff Honeyman
Another common administrative task is troubleshooting failed processes. Because
many applications don’t run as services (for instance Outlook, Word and Windows
Messenger), it’s useful to be able to query a running process. This script is
similar to SvcStat.vbs (also from Scripting Windows 2000), except that
it provides information about a particular process. The complete script available
for download online includes a line that displays the correct usage, including
what command-line arguments to use.
' ProcStat.vbs
' Copyright 2000 by Jeffrey Honeyman
' Published in Scripting Windows 2000 by McGraw-Hill/Osborne Media
...
box=WScript.Arguments(0)
proc=WScript.Arguments(1)
...
Set objSys=GetObject(patth)
Set objProc=objSys.GetWMIServices
Set objProcs=objSvc.instancesOf("WIN32_Process")
For Each thing in objProcs
If ucase(thing.Properties_("name"))=ucase(proc) Then
c=c+1
s=s & "Properties for " & proc & " on " & box & ":" & vbCRLF & vbCRLF
For Each prop in thing.Properties_
s=s & prop.name & " = " & prop.value & vbCRLF
Next
End If
Next
If c=0 Then
WScript.Echo "Process " & proc & " does not exist on computer " & box
WScript.Quit (5558)
End If
If c>1 Then
WScript.Echo "There are "& c & "instances of process "& proc * " on system"
& box & vbCRLF & s
Else
msgbox s
[To download an unabridged text file of this script, right-click here
and "Save As."—Ed.]
A significant difference between processes and services is that there can be
more than one instance of a process running at any given time. As such, this
script includes a counter variable “c” to count the number of instances for
the specified process. If more than one is running, the output is echoed to
the command line. If there’s only one instance, it’s placed in a msgbox just
as it was for services.
RemoteReboot.vbs/RemoteShutdown.vbs
by Microsoft
These are actually two separate scripts, but with one common goal: to use WMI
to perform that mainstay of troubleshooting skills—the reboot! How often do
you have to leave your desk and trudge across the office (or perhaps even up
some stairs) to reboot a server? Yes, Windows 2000 significantly reduced the
number of reboots required when installing software and upgrading the OS, but
they still happen. If you have the spare time and you like getting the extra
exercise, feel free to keep walking. If you’re as busy as I am, try this script.
It comes from the Microsoft WMI SDK downloaded at http://download.microsoft.com/download/
platformsdk/sdkx86/1.5/NT45/EN-US/wmisdk.exe.
' Copyright (c) 1997-1999 Microsoft Corporation
' ******************************************************************
'
' WMI Sample Script - REMOTE system reboot (VBScript)
' ...
Set OpSysSet=GetObject(_
"winmgmts:{(_
RemoteShutdown)}//REMOTE_SYSTEM_NAME/root/cimv2").ExecQuery_
("select * from Win32_OperatingSystem where Primary=true")
For Each OpSys in OpSysSet
OpSys.Reboot()
Next
[To download unabridged text files of these scripts, right-click here
and here
and "Save As."—Ed.]
And in case you just need to shut it down…
OpSys.Shutdown()
I suppose the next logical step would be to put both of these scripts into
one .WSF file in two different jobs, so that you can select whether to reboot
or shutdown when you run it. Keep a shortcut to this script on your desktop;
you’ll find yourself using it often.
BackupEventLog.vbs by Microsoft
One of the most useful troubleshooting tools an administrator has at his or
her disposal is the NT Event Log. When “unpredictable” behavior occurs, the
event log is usually the first place to look. The following script uses WMI
to back up the application event log to a file. I downloaded this from the same
site as the previous script.
' Copyright (c) 1997-1999 Microsoft Corporation
' ******************************************************************
'
...
Set LogFileSet=GetObject("winmgmts:{(Backup,Security)}").ExecQuery(_
"select * from Win32_NTEventLogFile where LogfileName='Application'")
For Each Logfile in LogFileSet
RetVal = LogFile.BackupEventlog("c:\BACKUP.LOG")
if RetVal = 0 then WScript.Echo "Log Backed Up"
Next
[To download an unabridged text file of this script, right-click here
and "Save As."—Ed.]
This is another short but essential script for your toolbox, and one that can
be easily modified for even more powerful behavior. For example, you could execute
the script remotely and have it use automation to e-mail the log file to you
(for, say, troubleshooting a location off-site). By simply changing the LogFileName
in the GetObject line, we can dump the system or security logs, as well. Priceless.
SendMail.vbs by Anonymous
I bet I had you a bit worried in the last script when I mentioned sending an
e-mail as if there were nothing to it, didn’t I? Not to worry. This script,
which comes from “anonymous” in the Windows Script Community at MSN.com, can
be used with the above event log script, or anywhere you need to send an e-mail
via automation. Let’s face it: Windows doesn’t always run perfectly. Errors
occur, servers go offline. Stuff happens. The ability to notify the on-call
administrator via an e-mail message will minimize down-time and make you look
indispensable in the eyes of your boss. (If it looks familiar, I wrote a script
almost identical to it for a previous “Scripting for MCSEs” column before finding
this one.)
' SendMail.vbs
' Published 2/27/01 - Windows Script Community, MSN.com
Set WSHShell = WScript.CreateObject("WScript.Shell")
Set appOutl = Wscript.CreateObject("Outlook.Application")
' Set a reference to the MailItem object.
Set maiMail = appOutl.CreateItem(0)
' Get an address from the user.
maiMail.Recipients.Add(InputBox("Enter name of message recipient"))
' Add subject and body text.
maiMail.Subject = "Testing mail by Automation"
maiMail.Body = "Message body text"
' Send the mail.
maiMail.Send
' Close object references.
Set appOutl = Nothing
Set maiMail = Nothing
Set recMessage = Nothing
[To download a text file of this script, right-click here
and "Save As."—Ed.]
Of course, you wouldn’t want to prompt for the recipient’s name in an automated
script—this would be either “hard-wired” into the script or accessed from a
file. Application automation is a wondrous thing. The sheer power of it all!
(If you don’t believe me, check out the “bonus” script at the end of this article.
Talk about power!)
ResetPassword.vbs by Chris Brooke
You didn’t think you were going to get through this without at least one script
written by yours truly, did you? In fact, this script was featured in my scripting
column some time ago. I’ve included it here for those of you who (shame, shame!)
haven’t been following my column. Besides, we’ve already noted that a primary
goal of scripting is to save time, and this one saves a bundle!
' ResetPW.vbs
' Copyright 2000, by Chris Brooke
Option Explicit
Dim objContainer, colUsers, lFlag
Set objContainer=GetObject("WinNT://domain")
ObjContainer.Filter=Array("User")
For Each colUsers in objContainer
IFlag=colUsers.Get("UserFlags")
If (lFlag AND &H10000) <> 0 Then
colUsers.Put "UserFlags", lFlag XOR &H10000
End If
colUsers.Put "PasswordExpired", 1
colUsers.SetInfo
Next
[To download a text file of this script, right-click here
and "Save As."—Ed.]
Small but mighty! It also checks to make sure that the “Password Never Expires”
flag isn’t set. No one should be exempt from the mandatory password policy.
Not even the CEO. This script is vital whenever your organization has a personnel
change or some other potential security hole that needs patching. Requiring
users to reset their passwords between normal, scheduled password resets is
something you’ve likely encountered several times before. I know I have—which
is why I wrote the script!
From Windows NT/2000 ADSI Scripting for System
Administration, by Thomas Eck
ISBN 1578702194, $45
Copyright 2000; reproduced by permission of New
Riders (To order, go to LearningStore.com.)
|
|
A Potpourri of Scriptlets by Thomas Eck
As our last featured script, I decided to include a flood of scriptlets from
Thomas Eck’s book, Windows NT/2000 ADSI Scripting for System Administration.
It’s packaged as a text file and includes a plethora of script tidbits that
you can add to your scripts. The functionality of each scriptlet is completely
self-contained, and you can simply cut-and-paste them as you see fit. These
scriptlets include everything from querying/setting AutoUnlockInterval for user
accounts to reporting disabled accounts and much more. They were all written
to be integrated into ASP pages, so you’ll have to change a few “Response.Write”
lines to “WScript.Echo” (as well as other minor changes), but most are equally
at home inside the Windows Script Host as in an ASP. Here are some examples:
' Querying AutoUnlockInterval Using a VBScript Active Server
Page
Dim Domain
Dim DomainName
DomainName = "Domain_Name_To_Manage"
Set Domain = GetObject("WinNT://" & DomainName)
Response.Write Domain.AutoUnlockInterval
' Setting a New Value for MaxBadPasswordsAllowed
' Using a VBScript Active Server Page
Dim Domain
Dim DomainName
Dim NewValue
DomainName = "Domain_Name_To_Manage"
Set Domain = GetObject("WinNT://" & DomainName)
NewValue = 5
Domain.MaxBadPasswordsAllowed = NewValue
Domain.SetInfo
' Querying MaxPasswordAge Using a VBScript Active Server
Page
Dim Domain
Dim DomainName
DomainName = "Domain_Name_To_Manage"
Set Domain = GetObject("WinNT://" & DomainName)
Response.Write ((Domain.MaxPasswordAge) / 86400)
' Removing a Computer Account Using a VBScript Active Server
Page
Dim Container
Dim ContainerName
Dim ComputerToRemove
ContainerName = "Container_Name_To_Manage"
Set Container = GetObject("WinNT://" & ContainerName)
ComputerToRemove = "Computer_Account_To_Remove"
Call Container.Delete("Computer", ComputerToRemove)
[To download a text file of these scripts, right-click here
and "Save As."—Ed.]
You What?! (A Bonus Script)
No discussion of scripting would be complete without mentioning arguably the
most famous VBScript ever to be created: “I Love You.txt.vbs.”. The “I Love
You” worm was a textbook example of the power of VBScript. Because, arguably,
all knowledge is morally neutral (only the application of knowledge can be good
or evil), I’d recommend you study this script, if you have it available (I’d
send it to you, but I’m pretty sure I’d get into serious trouble!), to gain
insight into the potential power of application automation. Just don’t send
it to anyone!
Now Get Out There and Automate!
I hope these scripts will find a place of honor in your administrative toolkit.
If you’re just now getting involved in scripting, these scripts will provide
an essential launch pad to help you learn while doing. I also hope I’ve given
you scripting veterans something of value, as well. If nothing else, you now
have some ideas upon which to build even better scripts. I can see the bulb
lighting up inside your head from here. Just don’t call me when it’s time to
change it.
About the Author
Chris Brooke, MCSE, is a contributing editor for Redmond magazine and director of enterprise technology for ComponentSource. He specializes in development, integration services and network/Internet administration. Send questions or your favorite scripts to chrisb@componentsource.com.