Scripting for small business admins

Those of us who have worked or are working in the small business end of the IT profession know that while automation is important it’s difficult to keep with the latest scripting technologies. Because we’re so busy trying to keep our business going, our time for learning scripting languages is limited so when we get hold of something that works we tend to go on using it even when the wheel of progress has moved on. As a result we tend to mix and match different scripting languages and techniques so we can focus on the most important task we need to do: generate revenue for our business.

Get-NetAdapter and Get-VMNetworkAdapter

I’ve done my own share of DOS-style batch scripting and VBScript in the past, and I’ve even played with other platforms like Perl and KiXtart when necessary. And while Windows PowerShell is now my go to language for Windows Server automation, there are still times when I find that a simple batch script or couple of lines of VBScript does the job quite easily. So in that sense I’m probably like most other admins who work with small organizations and use whatever I have close at hand to just do the job and move on. As a result my own scripts tend to be quite messy and not “elegant” as it really doesn’t matter to me whether a task I perform using 50 lines of code can be done using only 5 lines. The job is the job, and spaghetti fills your stomach regardless of how messy it looks on the plate.

Recently one of the readers of our WServerNews.com newsletter, Jeffrey Harris, shared some of his own scripting experiences with me and I wanted to share them here on WindowsNetworking.com so those of you who do IT for small organizations can glean a few tips and ideas you can use to better manage your environments. Jeffrey is an IT Professional with over 30 years’ experience who holds a CISSP certification and specializes in security and identity management. His previous work assignments have included the US Department of Homeland Security, the Pentagon, and the White House where he served as White House e-mail administrator. After almost 19 years working for a major IT services provider (the last 10 telecommuting from his home in upstate New York), Jeffrey is currently seeking a new position due to a recent downsizing so if you think you could use his skills and experience you should reach out to him soon before he gets snapped up by another organization.

And by the way, if you aren’t yet a subscriber to our weekly newsletter WServerNews you should hurry and join almost 100,000 other IT pros around the world by subscribing to it at http://www.wservernews.com/subscribe.htm.

Now let’s hear from Jeffrey Harris…

Scripting for Administrators

Scripting can be considered programming for the non-programmer. As a system administrator and systems engineer, I have used scripting for a variety of professional purposes, such as:

  • Automating procedures that are normally done by using a GUI tool, reducing the time required to perform the action, in some cases to seconds and minutes rather than hours, saving my customers money.
  • Sharing a procedure with IT staff; one can document a procedure and share the documentation, but scripting gives other IT staff the ability to simply and easily execute the procedure, reducing the possibility that staff implementing the procedure manually could execute it improperly, particularly if the documentation is wrong or incomplete.
  • Providing a reproducible process for configuring multiple systems, or different systems over time, thereby reducing the errors that can be introduced into systems.

Scripting can involve a variety of scripting languages. The basic script is a series of DOS or Windows commands, saved as a .bat or .cmd file (subsequently referred to as a command file). Other common Windows scripting languages are VBScript (extensively used before the release of Vista/Windows Server 2008) and PowerShell (now Microsoft’s preferred scripting language). This article will discuss some common script and command files I use for various administrative purposes. I presume a basic understanding of various Windows commands, such as bcdedit, diskpart, sc, etc.

Some uses for scripting

The main administrative areas in which I use scripts are system setup and operations management. Since I frequently set up Windows workstations or servers for clients in small organizations, without enterprise tools such as SCCM, I wanted to develop a repeatable process. I began years ago by creating a checklist of standard activities that I would perform on each system during the setup process. Although almost every system is different in some way, a number of settings are common for workstations, servers, or both. As time passed, I realized that rather than just define what the settings are in a checklist, and then implement them manually, I could automate the process by creating command files or scripts to actually implement most of the settings, and now I have a set of scripts that I run for every new system I set up.

For servers, some of my common settings are:

  • Dump files – since I almost never submit dump files to Microsoft, it makes no sense for me to configure systems to create full dump files, but there is useful information that can be locally analyzed in a mini-dump file.
  • Startup time – I think 30 seconds (the default time) for a server to boot is too long; 10 seconds seems more appropriate to me, as it gives an administrator time to click the F8 key if needed to start the system with alternative settings, such as Safe Mode.

To automate the process of configuring these two settings, I created the following command file. The command to configure the mini-dump file works with any version of Windows, but the commands to set the boot-up time for the system only work with Windows Vista/Windows Server 2008 or later operating systems. The command file also confirms that the startup time has been properly changed by displaying the boot-up time setting after the new time is configured (the first line is wrapped):

reg add HKLM\System\CurrentControlSet\Control\CrashControl /v CrashDumpEnabled /t REG_DWORD /d 3 /f
bcdedit /timeout 10
REM verify that the timeout setting was updated in the boot config
bcdedit /enum {bootmgr} |findstr -i "timeout"
pause

Another common change I make is moving the system CD-ROM/DVD drive letter for servers. I generally configure these systems with multiple partitions (or in some cases, multiple drives are configured as separate RAID 1 arrays). Since the system’s CD-ROM/DVD drive generally defaults to D:, I use the sc and diskpart commands (in combination with a secondary file that contains the specific diskpart actions to be performed) to change the CD-ROM/DVD drive letter to G: (this usually provides sufficient contiguous letters for hard drive partitions on the systems I set up):

@echo on
sc start vds
if errorlevel == 1 (sc config vds start= demand && sc start vds)
diskpart /s .\supportfiles\fixcd.txt
pause

The commands in the fixcd.txt file are:

select volume 0
assign letter g noerr
select volume 1
assign letter g noerr
exit

I will point out in passing that in the absence of multiple logical volumes, the CD-ROM/DVD drive is generally volume 0 or volume 1, depending on how the system prioritizes drive devices in the system.

For small scale deployments of multiple systems, or capturing an image of a system to use as a baseline for disaster recovery, I have used Microsoft’s ImageX utility (now replaced with dism) to capture and apply images (for larger deployments, in the absence of SCCM, I use the Microsoft Deployment Toolkit [MDT]). I have used variations of the following command file to apply or restore Windows Image files to a system (the commands below will apply an image file of the form “myimage-system.wim” to the system partition, and a second image of the form “myimage.wim” to a secondary [d:] partition or drive):

REM %1 is location and file name for image (i.e., e:\images\myimage.wim)
REM If there are spaces in file path or description, enclose text within quotes, as shown above
@echo off
if "%1"=="" goto end
for /f "tokens=1,2* delims=.", %%G in ('echo %1') Do imagex /apply "%%G-system.%%H" 1 c:\ /verify
imagex /apply "%1" 1 d:\ /verify
:end

On occasion, I have configured IPsec for securing connections between systems (such as using the Ajp13 protocol on port 8009 between an Apache Server and an Oracle server running the Application Express capability). The command file below applies an IPsec “Ajp Secure Connection” policy to the local system using a series of related netsh commands. Note that the one of the set and all of the netsh command lines below are wrapped.

REM @echo off
REM ********************************************
REM Start IPSEC Policy Documentation Section
Set PolicyName=Ajp13 Secure Connection
Set PolicyDesc=Ajp13 secure connection between Apache1 server and Tomcat servers hosted on Oracle RAC Servers
REM End IPSEC Policy Documentation Section
REM ********************************************
REM This file is only for the Second Tomcat Servers
REM ********************************************
REM Start IPSEC Policy NETSH Command Section
ECHO Hit any Key to apply IPSEC Policy -- %PolicyName%
pause
REM set source hostname or IP address for Apache proxy server
Set source-address=Apache1.mycompany.com
REM set destination hostname or IP address for destination Tomcat servers
Set Destination-address1=Oracle1.mycompany.com
Set Destination-address2=Oracle2.mycompany.com
@echo on
netsh ipsec static add policy name="%policyname%" assign=yes description="%policydesc%" activatedefaultrule=no
netsh ipsec static add filteraction name="Require Full Security" qmpfs=yes inpass=no soft=no qmsecmethods="ESP[3DES,SHA1]" action=negotiate description="No open communication"
netsh ipsec static add filterlist name="Ajp13 Filter List" description="Hostnames and Port for AJP Communications"
netsh ipsec static add filter filterlist="Ajp13 Filter List" srcaddr=%source-address% dstaddr=%destination-address1% description="Connection from Apache to Tomcat Server1" protocol=TCP mirrored=yes srcport=8009
netsh ipsec static add filter filterlist="Ajp13 Filter List" srcaddr=%source-address% dstaddr=%destination-address2% description="Connection from Apache to Tomcat Server2" protocol=TCP mirrored=yes srcport=8009
netsh ipsec static add rule name="Secure Ajp13 Rule" policy="%policyname%" filterlist="Ajp13 Filter List" filteraction="Require Full Security" conntype=lan activate=yes description="Secure Communications for Ajp from Apache to Tomcat" kerberos=yes
@echo off
REM End IPSEC Policy NETSH Command Section
REM ********************************************
echo.
echo Import completed.
echo.
echo Hit any Key to stop and restart IPSEC Service to activate IPSEC Policy
echo.
pause
net stop "ipsec services"
net start "ipsec services"
echo.
echo Restart complete
pause

Scripting for operational use

I have also created scripts and command files for operational use. The particular language I use for any given task has depended on the complexity of the action, and the languages supported by the operating system. Most of my scripting has been done using command files, or, where command files do not provide the necessary capabilities (for example, accessing Active Directory or WMI, reading input configuration files or extensively formatting output files), I have used VBScript. My extensive use of VBScript was due to a client environment that was predominately Windows Server 2003 based. PowerShell was an add-on to Windows Server 2003 whereas VBScript was built into that operating system, and in that client’s environment, I saw no reason to try and push for an add-on that increased the attack surface of mission critical systems.

Since Windows Server 2003 is now nearing its end of support, and most organizations have Windows Server 2008R2 or 2012/R2 servers, I am now focusing on using PowerShell almost exclusively, except where performing a task in a command file makes more sense. In some cases, it makes sense to combine a command file with a PowerShell script. A few days ago, I had a need for a “touch” command to reset modified dates on files, and so I used DOS combined with PowerShell code to create the following command file (DOS sets up the execution environment, with PowerShell actually making the change to the modified date of the file):

REM Parameter 1 - file name (with or without path) - required
REM Parameter 2 - date string - required
REM Parameter 3 - time string - optional
REM Parameter 4 - literal AM or PM - optional
REM Parameters 3 and 4 may be combined
REM Abort if Parameter 1 not specified
if "%1" == "" (
echo No file specified... aborting
Goto End
)
REM Abort if Parameter 2 not specified
if "%2" == "" (
echo No new date specified... aborting
Goto End
)
REM If we reach here, set last modified date/time
powershell (Get-Item "%1").LastWriteTime = '%2 %3 %4'
REM Verify last modified date/time was updated
powershell " & {(Get-Item '%1') | format-list -Property name, lastwritetime}"
:End

There was one tricky aspect to including the PowerShell commands directly in the command file versus using the command file to call a PowerShell script with the PowerShell commands stored in a separate script file: the use of single or double quotation marks. When I ran the commands within a PowerShell command line interpreter, they ran correctly with double quotation marks. Yet the exact same syntax caused an execution error when embedding within the command file. Simply changing the double quotation marks to single quotation marks for the new LastWriteTime value fixed the issue, even though there were no double quotation marks embedded in any of the Parameter 2, 3, or 4 text.

Another use in the last few days was to provide a mechanism for relatively unsophisticated users to change the DNS servers on wireless networks where the local DNS servers provided through DHCP proved unreliable. Although the actual netsh commands were relatively simple to implement, the need to account for UAC on users’ systems drove the use of PowerShell to create an elevated command shell to execute the netsh commands; executing the netsh command directly would cause the operation to fail when UAC was enabled. The following command (saved in a command file) calls a second command file to execute the netsh commands to set static DNS servers for the wireless network adapter on the system:

powershell Start-Process staticdns.cmd -Verb RunAs

The commands in the second command file (staticdns.cmd) are:

@echo off
netsh interface ipv4 set dnsservers name="wireless network connection" source=static 24.25.5.60 validate=no
netsh interface ipv4 add dnsservers name="wireless network connection" 24.25.5.61 validate=no
netsh interface ipv4 add dnsservers name="wireless network connection" 8.8.8.8 validate=no
netsh interface ipv4 add dnsservers name="wireless network connection" 8.8.4.4 validate=no
echo.
echo If the DNS entries shown after clicking a key are 24.25.5.60, 24.25.5.61, 8.8.8.8 and 8.8.4.4, the change worked
echo.
pause
netsh interface ipv4 show dnsservers name="wireless network connection"
echo.
echo Done!
pause

A more detailed example

Moving back to VBScript, I once found myself in need of a script that could interrogate network card adapter driver versions and generate a log file. The script needed to run against all servers in a specific OU. Once written, I realized that it provided a framework to allow other actions to be performed by simply encapsulating the action to be performed within a function called by the main script once a connection to each server was established. For example, the local administrator password could be changed on multiple servers by a domain administrator without having to access each server via remote console or remote connection tools (such as the server management MMC snap-in).

This VBScript processes a list of member servers within a specified OU in a particular Active Directory domain (or all member servers within the domain), and uses WMI Authentication to return a list of WMI attributes from each server (if the remote server can be connected to):

Conclusion

Scripting is a useful capability that all system administrators should know. Use of scripting can save time, reduce errors, and improve the ability of administrators to make changes to systems or collect information in the absence of enterprise systems such as SCCM. I agree with the quote from Jeffery Hicks in a recent WServerNews newsletter: “Those who forget to script are doomed to repeat their work.” As system administrators, would we not rather do something once and then move on to the next challenging project?

Additional Resources

There are numerous resources online that can help administrators learn how to script in various languages. Even better, there are many sites and blogs out there that have sample scripts you might be able to use with little or no customization to solve the IT challenges you’re facing. Here are a few of the top resources that come to mind:

Leave a Comment

Your email address will not be published.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Scroll to Top