As a Remote Administrator for a software vendor, there’s a popular challenge I often have to overcome; I need to deliver a software package to a lot of computers without using the more popular technologies available. Why the restriction? I generally only maintain the servers that house the software my company builds. That being the case, I don’t get access to such tools as Group Policy, SMS or the latest, greatest SCCM that a lot of IT staffs will use to push out and deploy application installs. You get the keys to a few locks, but they won’t give you the keys to the kingdom.
Recently, I was faced with a new challenge. I had a client that was needing to update over 50 workstations at the same time and the updates were starting to come in weekly. Citrix wasn’t an option for them. As a result, the client would have two people stay after work one night every week and walk to every machine (stretched out between 3 different buildings) and execute the new application install updates. After employing RDP, things went a little faster, as they could sit at their desk and login to every machine remotely. That’s still not a great option though.
The following is a batch file I wrote that allows an application to be delivered and installed to every computer on the domain by using nothing more than a text file with a list of the computer names. To start, you’ll need to download psexec, an awesome telnet utility from SysInternals (now owned by Microsoft) that allows you to run remote commands on other computers without the need for logging in. Just drop it in your C:\Windows directory on the server you’ll be running your batch file on. No client software to install!
The batch file simply loops through twice. Using a FOR /F loop, it loops through a text file (called computernames.txt in my example) and copies the install to every computer. The second time it loops through, psexec executes a command to install. With the -d option there, it doesn’t wait around to finish either. It starts the install and moves right on to the next computer in the list. Here’s my code:
@REM -- COPY FILES FROM SERVER TO ALL WORKSTATIONS AND DEPLOY -- @echo off setlocal EnableDelayedExpansion FOR /F %%N IN (computernames.txt) DO ( set line=!date! !time! %%N XCOPY \\servername\c$\Jeff\installfolder \\%%N\C$\Install\ /y /O /X /E /H /K /I /S >nul 2>&1 || set line=!line! Error echo !line! ) >>copyresults.txt FOR /F %%N IN (computernames.txt) DO ( set line=!date! !time! %%N psexec \\%%N -d -s msiexec /update "C:\Install\Update\MyProgramUpdate_01.1.12345.msp" /qb )
So there it is, complete with an error checking log on the copy to every computer. Since we’re using the -d paramater with psexec, we can’t use a log to see if psexec worked. I find this the best way though, as you will otherwise need to wait for each computer to verify execution before moving on… and this took about a minute p/ computer. With just a few short lines of code, I managed to give 2 employees the night off every week while I took on the task of installing the software to every workstation in a much shorter time frame. The install time went from 4 hours (for 2 people) to 1 hour (for 1 person).
OK.. so that works. But now, with nobody walking around to ensure the success of the installs, how to know if the machines really updated or if there was a problem? Most programs write to a log of some sort. I decided to simply use my loop again and go parse the “Install.log” file of each computer. I created the following batch file to achieve this:
@echo off setlocal EnableDelayedExpansion FOR /F %%N IN (computernames.txt) DO ( For /F "tokens=1* delims=," %%A in ('find "2010-02-17"^<\\%%N\C$\Program Files\MyProgram\Logs\Install.log') do ( >>MASTERLOG.log echo %%N %%A ) )
This will list every computer that has a line in Install.log that begins with the current date (update the date before running it), which is how my install log gets written. Change yours up accordingly. If you want to include computers that haven’t got the log written to it yet, you could include the following after the double-quote at the end of your logfile path: ^|^|echo ERROR
I was actually surprised how well this all worked when using it in a production environment. It was faster than I even imagined it would be and my installs can now all be happening at the same time (nearly.. as psexec takes about 3 seconds to execute before going to the next name in the list) with no other user intervention needed.