App-V 5 Scripting: Change
It is almost easier to pretend you know nothing about App-V 4.x scripting when looking at scripting in 5.0. That is often my first response to many rumblings about difficulty getting scripting set up in 5.0. These rumblings come mostly from users of App-V 4.x. Change is hard, I know, but we have a better set of options and frankly, in my opinion, it’s much better this way in the long run. It is just a paradigm shift that does require somewhat of a translation process for bringing older scripts from 4.x over to 5.0.
No More SCRIPTBODY (Embed in the Package, not the XML)
First of all, there are no more <SCRIPTBODY> elements. Instead of embedding the script inside the XML, the intent is to have the script embedded inside the package in the .\Scripts folder. This is the ideal place for it as it will be one of the default search paths when calling a script. If you have already sequenced a package (or converted a package) and want to add a SCRIPT element into the dynamic configuration, you will need to specify the path to the script interpreter and script file. So in essence you have as the command and argument the script interpreter as the command and the script (and options) as the argument.
Not Enabled by Default
Before you even begin to start testing scripts, you will have to make sure the client is set up to allow for package scripts. The quickest way to do this is through PowerShell using the following
Set-AppvClientConfiguration -EnablePackageScripts $true
You can also do it by GPO or manually in the registry. Always verify this before testing scripts.
Security Context
Some scripts will run in the SYSTEM context or the currently logged on user depending on the scripts package event. Later I’ll discuss workflows that will also help you determine the context (Machine or User) for running the script (which is another new concept in 5.0.) The following table outlines how the security context relates to the package event.
Event |
Runs As |
When the package is added |
Local System |
When the package is published globally |
Local System |
When the package is published to a user |
Current User |
After a Virtual Environment (Package) is built |
Current User |
After a virtual application is started |
Current User |
After a process exits |
Current User |
When the VE shuts down |
Current User |
Right before a package is unpublished to a user |
Current User |
Right before a package is unpublished globally |
Local System |
Right before a package is removed |
Local System |
Package Events?*
You need to forget the concept of PRE and POST events. Scripts can be called based on specific events in the lifecycle of the package. You will need to know at what point you want the script to be triggered. If your script needs to run in a specific context, this will help determine your event. If you decide your script needs to be triggered during application initialization (at the start of the virtual environment, the start of the process, when a process exits) you will need to also specify the specific application, rollback options, and whether you want to run it inside or outside the virtual environment.
*I so want to call this “event trigger” but it would be confusing.
To explain how the PRE and POST Options of before evolved into Package Event Elements – but not directly – look at the table below:
Package Event |
4.6 |
5.0 |
When the package is added |
N/A |
AddPackage |
When the package is published globally |
N/A |
PublishPackage |
When the package is published to a user |
N/A |
PublishPackage |
Before a Package is Streamed |
PRE STREAM |
N/A |
After a Package is Streamed |
POST STREAM |
N/A |
After a Virtual Environment (Package) is built |
N/A |
StartVirtualEnvironment |
When a virtual application is started |
PRE LAUNCH |
N/A – but same effect can be achieved with StartVirtualEnvironment |
After a virtual application is started |
POST LAUNCH |
StartProcess WAIT=TRUE |
After a process exits |
POST SHUTDOWN |
ExitProcess |
When the VE shuts down |
N/A |
TerminateVirtualEnvironment |
Right before a package is unpublished to a user |
N/A |
UnPublishPackage |
Right before a package is unpublished globally |
N/A |
UnPublishPackage |
Right before a package is removed |
N/A |
RemovePackage |
The key is to know what package/app event you want to trigger execution: (AddPackage, PublishPackage, StartVirtualEnvironment, StartProcess, ExitProcess, TerminateVirtualEnvironment, UnPublishPackage, RemovePackage)
Wait Options
In addition to the command <Path> and <Arguments> you have your “Wait” options (Wait, RollbackOnError, Timeout.) If you have a <Wait> element with nothing else, it will default to RollbackOnFailure=”True” and Timeout=0.
Element Flow
When we put it all together, you will see the element flow follows this basic process:
[MACHINE OR USER CONTEXT?]
[EVENT TO TRIGGER EXECUTION?] [IF VE or Process Event – Also the option of running inside the VE]
[MY Command Path]
[MY Command’s arguments]
[My Wait Options]
[Optional AppID EXE w/ tokenized path if this is a Process event]
Where an example of a machine targeted script looks like this:
<MachineScripts>
<PublishPackage>
<Path>PowerShell.exe</Path>
<Arguments>-f file.ps1 </Arguments>
<Wait RollbackOnError="true" Timeout="30"/>
</PublishPackage>
As another example, if a script was targeted for a user, set for PRE LAUNCH/ABORTRESULT=1/PROTECTED=TRUE in 4.x, and was only needed for one app, you could call it this way in 5.0
<UserScripts>
<StartProcess RunInVirtualEnvironment="true">
<Path>cscript</Path>
<Arguments>myscript.vbs</Arguments>
<Wait RollbackOnError="true"/>
<ApplicationId>[{AppVPackageRoot}]\Directory\Subdirectory\App.EXE</ApplicationId>
</StartProcess>
Deployment Configuration and Dynamic User Configuration
Many of the problems with testing scripts come with proper placement in deployment and dynamic configuration files and how they are targeting. Scripts that need to be called during package add and global publishing events should be part of the DeploymentConfig.XML file. These scripts will also run in the context of the local system account so scripts that map drives, for example, should not go here. Those need to be called as a <UserScript> element. I use the following visual workflow to help me determine targeting and publishing.
In the case of global publishing, the deployment configuration also has a UserConfiguration element in addition to MachineConfiguration which means that scripts appearing during these events will apply to all users when the package is published globally. This would be the appropriate place to have scripts which map network drives.