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 strServerName(1000)
Dim ErrMsg
Dim StrGroupToAdd
Dim strServer
Dim Result
Dim strInputFile
Dim LogFile
Dim strLocalGroup
Dim szPathtoexe
Dim szSMSexe
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")
Wscript.Quit(1)
ElseIf Err Then
ErrMsg = AdsiErr(strInputFile)
MsgBox ("Error: "& ErrMsg)
Wscript.Quit(1)
End if
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")
Wscript.Quit(1)
End if
ErrMsg = "Logging Started"
Result = WriteLog(,LogFile,ErrMsg)
If Err Then
ErrMsg = AdsiErr(LogFile)
MsgBox ("Error: " & ErrMsg)
Wscript.Quit(1)
End if
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")
Wscript.Quit(1)
End if
If Err Then
ErrMsg = AdsiErr(LogFile)
MsgBox ("Error: " & ErrMsg)
Wscript.Quit(1)
End if
Separate the actual name of the .exe from the rest of the pathiPathLen = 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))
else
ErrMsg = strServerName(xCounter) & " has been updated successfully"
end if
' Write results to the log
Result = WriteLog(strServerName(xCounter),LogFile,ErrMsg)
xCounter = xCounter + 1
End if
loop
This 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 Function
This function writes the results of each installation attempt to a log file supplied by the user.Function WriteLog(ServerName,strLogFile,strMsg)
Dim strTextStream
Set strTextStream = objFS.OpenTextFile(strLogFile, 8, true)
strTextStream.WriteLine(strMsg)
strTextStream.WriteLine("Time: " & Time)
strTextStream.WriteLine("Date: " & Date)
strTextStream.WriteLine("----------------------------------------")
strTextStream.Close
End Function
This 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.Function AdsiErr(ServerName)
Dim e
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
Else
e = Hex(Err.Number)
AdsiErr = "Unexpected Error on " & ServerName
End If
End Function
This 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.Function LaunchInstall(strServer)
Dim RunTime, Comp, svc, fso
Set fso = CreateObject("Scripting.FileSystemObject")
Copy the SMSInstaller .exe to the root of C: on the machinefso.CopyFile szPathtoexe, "\\"&StrServer&"\c$\"
Use ADSI to query the ADSI accessible objects on the remote serverSet 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
svc.Start
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
end if
Next
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 ,false
Add 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
End Function
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.

