Since I spend a fair amount of time developing, updating, troubleshooting, and managing the deployment of: scripted packages, I figured I'd try to apply some analytical voodoo and give my jaws something to work on that might be semi-useful. There is quite a lot of forethought that goes into the decisions to:
A. Use a script to accomplish the task (as opposed to a simple command line, or a GPO)
B. Choose a particular sequence
C. Choose particular built-in or third-party utilities
D. Assess each step for consecutive steps, as well as the final outcome
Some of that comes with direct, hands-on "trial and error" discovery. But more often it boils down to years and years of experience that rub my little brain cells together like two water-logged sticks on a desert island, desperately trying to start a fire to make a cup of coffee.
One thing I have noticed over the past few years, is that I spend more time thinking about the problem, the various ways the problem could be constrained, the various ways the constraints can be approached, the various probabilities of success or predictability for approaching each constraint, and aggregate probability of predictable outcomes and the gaps resulting against the desired outcome. After years of developing API frameworks, I've always preferred drawing on paper before writing code. Now I just do the math in my head and nod a lot and make people wonder if I'm losing my sanity as my eyes drift off into oblivion.
Let's get started...
What is a “Package”?
For the purposes of supporting my half-brained diatribe (ok, monotribe): a "package" shall be defined as a logical unit of software that can be deployed and executed in a single operation. A package may (and often does) consist of multiple sub-tasks or steps, which are typically executed in sequential fashion, but all within the scope and context of a single executable entity (a file).
What is a “Re-Package”?
A "re-package" is (again, indulge my feeble intellect here) a special treatment applied to, or wrapped around, an existing "package". A common example would be using a script to execute a vendor "setup.exe" or "setup.msi" to include additional parameters to control custom options during the execution. Another example would be performing a snapshot "capture" and producing a new .exe or .msi installer file. There are many other examples obviously.
What is a "Script"?
Oh my God. After having the definition pounded into my puny skull for years in college, by professors, by visiting professors, technical fellows, DoD engineers, vendor architects, and text books, and 12 year old kids war-driving with iPods and six-packs of Red Bull, the current definition has been stolen and modified and put back on the shelf looking almost nothing like what we were told for twenty-odd years.
The "traditional" definition *I* was given was more or less, to paraphrase and regurgitate: Software code which is human-readable and executed by an interpreter, rather than compiled into intermediary or bit-level machine code. There is (was) no need to compile or otherwise prepare the source code to be executed. There is (was) no need to decompile, or reverse engineer, in order to edit the code after execution. The interpreter reads, parses, and executes the source code with only a temporary execution cache (stack, pipeline, queue, etc.). After execution, there were no new compiled files, just the original source code files.
PowerShell changes that definition in a few ways. CmdLets. Then again, so do other contemporary languages which employ Kelvination, tokenization, pre-compilation, and dedicated processors (soft or chip-based). I started on LISP and C/C++, and I hardly give a shit anymore about keeping up with trends. The kids are in control now. I am the old fart, making wise comments from the back corner of the room, laughing with my flapping lips while my dentures are soaking in a shot glass filled with cheap Bourbon. Whatever. Where was I? Oh yeah...
I suppose in some ways, there are still parallels between PowerShell and LISP in that cmdlets are analogous to LISP engine constructs (pre-compiled components which backended elements and atoms in LISP code, such as mapcar(), apply() or assoc() ), even though some LISP implementations used hardware (firmware) to manage the constructs, allowing for faster retrieval and execution. Oh geez. As if I really care. Why am I blabbering about this? Whatever, let's move on...
Options to Consider
When you are faced with using (or making) a script to accomplish a multi-step deployment or "un-deployment", there are some common aspects to consider. These will play a HUGE factor in what language(s) you choose, how many script files will work best, what built-in (operating system environment) commands to invoke, what third-party utilities to employ, and finally: how to architect and construct your script code. It may even impact your decisions that guide resource planning (people, schedules, etc.)
The basic considerations are described below, but these are by no means a complete or comprehensive list. Every day I am faced with a new deployment or un-deployment challenge that brings into focus new criteria and new approaches. We never stop learning, do we?
Install -vs- Uninstall
Are you installing a software product? Multiple products? Are you uninstalling software products? Are you combining both uninstalls and installs in the same package? Does the installer take care of removing or upgrading the existing components for you? Does the vendor even provide an easy "unattended" uninstall feature? Or did they shoot up a gallon of heroin and package their code using their feet while hanging naked, upside down from trees?
Are you deploying the same package to only one common operating system or multiple operating systems? (I'm assuming here we are talking about Windows since I can't name one corporate environment deploying something like Office to 80,000 Linux or OSX clients). Windows XP? Windows Vista (yuck!)?, Windows 7? Are you deploying to Windows Servers also?
32-bit -vs- 64-bit
Are you deploying (or un-deploying) to 32-bit and 64-bit clients? Have you compared the installation differences?
MSI -vs- EXE -vs- Custom
Are you deploying or un-deploying only MSI installation files? Only .EXE files? Both? Are you pushing down (or collecting) raw files and folders? Are you extracting .CAB, .ZIP or .7Z files?
Are you creating new registry keys and/or values? Are you modifying existing keys and values? Are you deleting existing keys and values? Do you need to verify the keys/values exist before performing other actions? A common tool for this is Windows REG.exe. However, keep in mind that REG.exe is slightly different on XP and Windows 7 clients (as well as Windows Server 2003 vs 2008 R2).
Do you need to modify security permissions on specific registry keys? A common scenario is the need to relax permissions on keys under HKLM or HKCR for applications which are not Windows 7 compliant. A common tool for this is REGINI.exe. Some people say that I too am a common tool, but I'm not going there.
Do you need to modify security permissions on specific files or folders, or entire folder trees with all files? A common scenario, like that described above, is relaxing permissions for non-administrators under the %ProgramFiles% folder, or other locations which require elevated rights. Common tools for this are CACLS.exe and ICACLS.exe
Do you need to manually register or un-register DLL components? A common tool is REGSVR32.exe
Do you need to stop, start, restart or delete services? There are quite a few applications that create their own services and often do not do a great job of removing them during uninstalls. Two useful tools for this are the built-in SC.exe command, and the Sysinternals PSService.exe utility.
Do you need to stop or kill a running process? The built-in TASKKILL.exe command is useful, as is the Sysinternals PSKILL.exe utility. Incidentally, when uninstalls fail to delete certain files or folders, it quite often ties back to a running process that couldn't be terminated by the vendor's own uninstall process. This happens with Services as well. During complex uninstall package executions, it's not uncommon to find it necessary to stop services and kill processes at specific points before continuing in order to successfully remove all related components.
If your package manipulates DLL components, or services, you may want to execute a forced restart at the very end of your package execution. Also, if any of your MSIExec exit codes return 3010, you should do a forced restart at the very end. If you perform any modifications to the system which impact the Side-by-Side cache or modifying .NET system configurations, and things of that sort, you should consider a forced restart afterwards.
After you size up your objectives, and comb through this list (as a starting point, anyway), you should be able to quickly connect the dots and arrive at a rough idea of what you need to do. In a future blog post I will dive into aspects that may help to decide which scripting languages/tools to employ for various tasks.