Installing Packages Automatically
by John Loomes
This script is in effect a 'poor mans SMS' in such that it allows you to distribute a packaged application, produced say, with SMS Installer, to a number of target machines. The Command Scheduler is then employed to kick off the package after a predetermined time period. This script checks the status of the Command Scheduler before and after the job to ensure that the service is not left running on a machine on which it was previously stopped, or not stopped or a machine on which it was previously started.This script is in effect a 'poor mans SMS' in such that it allows you to distribute a packaged application, produced say, with SMS Installer, to a number of target machines. The Command Scheduler is then employed to kick off the package after a predetermined time period.
This is a somewhat crude replacement for the Package Distribution part of SMS, and doesn't provide much in the way of status reporting or error checking. But if you don't use SMS at your site and want to get a patch out to a large number of machines in a short period of time, then this is going to be much quicker than sending and engineer out with a CD!
This script requires WSH and ADSI to be installed on the machine running it, it will also need to run under and account with Administrator rights over all the target machines.
It contains quite a few usefull code snippets you might want to use elsewhere, such as querying NT Services on Remote Machines, writing to log files, asking for user input etc etc.
Continue on any runtime errors (comment this line out whilst troubleshooting)
On Error Resume Next
Setup all the main variables to be used in the script, Ive allocated an array large enough for 1000 machines, expand/reduce this as necessary
Dim iPathLen, iExeLen
Create a new File System Object so we can start working with the various control files the script uses
Set objFS = CreateObject("Scripting.FileSystemObject")
Prompt the user of a file containing a list of machines to send the package to. This section could be modified to pull this list from a spreadsheet, or an SQL database even
strInputFile = InputBox("Enter name of file containing machines to modify (Including full path)",,"TestServer.Txt")
Check that the file supplied exists by trying to open it, if we find any errors here then quit the script.
Set ServerList = objFS.OpenTextFile (strInputFile)
If strInputFile = "" Then
MsgBox ("Operation Cancelled, no input file supplied")
ElseIf Err Then
ErrMsg = AdsiErr(strInputFile)
MsgBox ("Error: "& ErrMsg)
Ask the user for the name/path of a file to log the results of the script to. Again, check that the file exists and can be written to. If any error occur, then quit the script.
LogFile = InputBox("Enter name of Installation Package (Including full path)",,"H:\log.txt")
If LogFile = "" Then
MsgBox ("Operation Cancelled, no log file supplied")
ErrMsg = "Logging Started"
Result = WriteLog(,LogFile,ErrMsg)
If Err Then
ErrMsg = AdsiErr(LogFile)
MsgBox ("Error: " & ErrMsg)
Ask the user for the Path/Name of the package to be distributed, this should be a single .exe file that requires no user intervention when launched. Check that the file exists, quit if any errors are found.
szPathtoexe = InputBox("Enter name of Log File (Including full path)",,"\\eulon-appw1\swlibrary$\")
If szPathtoexe = "" Then
MsgBox ("Operation Cancelled, no Executable file supplied")
If Err Then
ErrMsg = AdsiErr(LogFile)
MsgBox ("Error: " & ErrMsg)
End ifSeparate the actual name of the .exe from the rest of the path
iPathLen = Len(szPathtoexe)
iExeLen = iPathLen - InstrRev(szPathtoexe,"\")
szSMSexe = Right(szPathtoexe,iExeLen)Extract the machine names from the input file supplied earlier, copy the .exe to each machine and call the routine to schedule the installation (LAUNCHINSTALL(StrServer)). Attempt to trap any errors, and pass the results to the log file supplied earlier.
do while ServerList.AtEndOfStream <> True
strServerName(xCounter) = ServerList.ReadLine
If not Isblank(strServerName(xCounter)) then
StrServer = StrServerName(xCounter)
' Reset Errors
Err = ""
ErrMsg = ""
' setup the installation on this server
Result = LaunchInstall(strServer)
' If not successfully then try to find out why
If Err Then
ErrMsg = AdsiErr(strServerName(xCounter))
ErrMsg = strServerName(xCounter) & " has been updated successfully"
' Write results to the log
Result = WriteLog(strServerName(xCounter),LogFile,ErrMsg)
xCounter = xCounter + 1
loopThis function is used to trim leading and trailing spaces from the machine names in the input file. Function IsBlank(strInput)
IsBlank = not CBool(Len(trim(strInput)))
End FunctionThis function writes the results of each installation attempt to a log file supplied by the user.
Set strTextStream = objFS.OpenTextFile(strLogFile, 8, true)
strTextStream.WriteLine("Time: " & Time)
strTextStream.WriteLine("Date: " & Date)
End FunctionThis function compares any errors to a list of known error codes, and then uses this information to write more meaningful error messages to the log file.
If Err.Number = &H80070562 Then
AdsiErr = ServerName & " has already been updated."
ElseIf Err.Number = &H80070005 Then
AdsiErr = "Access Denied to " & ServerName
ElseIf Err.Number = &H1A8 Then
AdsiErr = "Couldnt Connect to " & ServerName
ElseIf Err.Number = &H800708B2 Then
AdsiErr = ServerName & " is a Domain Controller, cant update"
ElseIf Err.Number = &H8007056B Then
AdsiErr = "Group " & ServerName & " Doesnt Exist"
ElseIf Err.Number = 53 Then
AdsiErr = "File " & ServerName & " Doesnt Exist"
ElseIf Err.Number = 70 Then
AdsiErr = "Cant Write to " & ServerName
e = Hex(Err.Number)
AdsiErr = "Unexpected Error on " & ServerName
End FunctionThis function handles the distribution of the package to the server(s) in the input file. It also controls the Command Schedule Service on the remote machines.
Dim RunTime, Comp, svc, fso
Set fso = CreateObject("Scripting.FileSystemObject")Copy the SMSInstaller .exe to the root of C: on the machine
fso.CopyFile szPathtoexe, "\\"&StrServer&"\c$\"Use ADSI to query the ADSI accessible objects on the remote server
Set Comp = GetObject("WinNT://DOMAIN/" & strServer & ",Computer") Set Shell=wscript.createobject("wscript.shell")
Filter out all the NT Services from the list of objects on the remote machine
Comp.Filter = Array("Service")
For Each svc in Comp
Query the Command Scheduler Service. If the service is currently stopped, then start it.
If svc.Name = "Schedule" AND svc.Status = "1" Then
If we started the service up, then add a scheduled command to stop the service again in 6 minutes time.
RunTime = Time + (6/1440)
Shell.Run "cmd /c at \\"&strServer&" "&RunTime&" net stop Schedule",0 ,false
Set Comp = Nothing
RunTime = Time + (2/1440)Now add a scheduled command to run the SMSInstaller .exe silently (/S) in 2 minutes time.
Shell.Run "cmd /c at \\"&strServer&" "&RunTime&" c:\"&szSMSexe&" /s",0 ,falseAdd another scheduled command to delete the SMSInstaller .exe in 4 minutes.
This assumes that the install will only take 2 minutes maximum to complete. For larger packages, increase this time accordingly.
RunTime = Time + (4/1440)
Shell.Run "cmd /c at \\"&strServer&" "&RunTime&" cmd.exe /c del c:\"&szSMSexe,0 ,false
Set Shell = Nothing
As I said at the beginning, some aspects of this script are somewhat 'crude' and could do with improvement - such as using a simply time delay to determine when the source files should be removed from the target machine, for example. I didn't spend any more time making this more elaborate because I wanted a quick solution to the problem. And it works! We sucessfully rolled out backup software agents to several hundred NT4 servers using this very script, and it only took about half and hour!
Some of you might want to improve on this script to include better error checking and reporting.
Please let me know how you get on! And if you've any suggestions for features you think would be useful additions to this, then let me know too! But bear in mind that this script isn't intended to replace SMS, that would be a pointless 'reinventing the wheel' exercise.