AppV5 - Virtualizing and running Local Services stand-alone

10 Jul 2015

AppV5 allows you to virtualize Services that run under the LocalService account. The issue I’ve seen with this approach is AppV5 will load the service for a user upon environment launch (which is good for VDI / desktop deployments) and the service will terminate upon that AppV environment exiting. For remote desktop / Citrix XenApp deployments, this can be a big bother. If the service is required for running the application the standard answer is to extract it and install it using DeploymentConfig.xml or some other on application publish.

But what if you have a service that is NOT required to run an application, just needs to run in the background, AND can run as the LocalService? This service would be prime for being virtualized!

Do I have an example of such an application? Why yes I do.

Epic SystemPulse is an application that captures performance information and uploads it to a DB. It only runs under a LocalService account.

Using my Citrix PVS Sequencer, here is how I sequenced it:

These next screenshots illustrate the simplicity of this application:

Virtual Registry Hive For Epic SystemPulse

 

All files required for Epic SystemPulse

 

Lastly, the single service we need running

Now, the problem.

I am running this service on a RDS/& server. This service needs to run while an application, Epic Hyperspace, is running. My first thought was the use a connection group so that whenever Hyperspace is launched, SystemPulse will launch with it.

This, initially, worked. The first user who launched HyperSpace also had SystemPulse running at the same time. Subsequent user whom launched HyperSpace, had their SystemPulse service start and terminate, with the first service continuing to run. It looked like a success!

Second user launches app, SystemPulse (EpicSvcMaster.exe & EpicSvcHost.exe) starts and terminates, the original processes still running.

Eventually, that first user logged off. And the SystemPulse processes exited with it.

I thought being SYSTEM processes they were started in some way that a ‘SYSTEM’ process wouldn’t terminate willy-nilly. This is not the case. A quick test of ‘get-appvclientpackage’ shows the package as ‘In Use’, signifying that AppV5 is tracking the processes. The users logging off will turn that ‘True’ into a ‘False’.

Now, we had to wait until the next user started the application to have the service start back up. This was an unacceptable solution.

But we know that this AppV5 package will work as a service. We can reap all the benefits of AppV; we can deploy this package on a whim, it’s not a local/permanent install, it works! So now the question becomes how can we keep these advantages and what are the drawbacks?

The first drawback I encountered was ‘how do I open a AppV Virtual Environment (appvve) so this service will start?’

I tried several things.

I created a script/scheduled task that would check to see if the process was running and if not, open a appvve. This failed. I tried this using the LOCAL SERVICE to start my script to open my environment and it would not. I tried using the SYSTEM account and it failed. It turns out you can’t use either of these accounts to open a appvve.

With this failing, I moved to another method. AppV has the ability to start a script upon application publishing, could I use this to start my service when the application is published? It turns out that the account that runs when this context is started is the SYSTEM account, and it failed same as the scheduled task.

At this point I figured my problem is the account I’m trying to open the appvve with. I need to launch it with a service account. My first attempt I made a script with a hard-coded username/password string with PSEXEC.exe to open my appvve. I put this script in deploymentconfig.xml.

And it worked! It opened my appvve environment, the services started, and everything looked great! The only issue I had now is the hard-coded username/password combo. There is no way having that would be acceptable in a plain text file.

So I created a exe with AutoIt, ‘RunAsWait.exe’.

;~ This file is used by PVS for the auto-update feature to enable it to run under a context that is not the MACHINE context
;~ By Trentent Tye May 02 2013
;~ The password in this plain text file has been obfuscated.  Please change it to the correct password
 
 if $CmdLine[1] = "/?" then 
 MsgBox(0x40000, 'Help', "Please execute this command with the path to a cmd or bat file." & @CRLF & "The file will execute with elevated credentials" & @CRLF & @CRLF & "eg, RunWait.exe C:\test.cmd")
  EndIf
   
RunAsWait ( "USERNAME", "DOMAIN", "PASSWORD", 1, "cmd.exe /c " & '"'&$CmdLine[1]&'"' ,"","",""
It may not be the most secure thing when compiled, but internally it's good enough...
With this I set my deploymentconfig.xml with the program and arguments I was looking for:

The CMD file looked like so:

:: ===========================================================================================================
::
:: Created by:  Trentent Tye
::   Intel Server Team
::   IBM Canada Ltd.
::
:: Creation Date: Nov 20, 2014
::
:: File Name:  runme.cmd
::
:: Description:  Launches Epic System Pulse
::                      Parameters accepted...   e.g. <none> and /RESTART
::
:: ===========================================================================================================
 
@ECHO OFF
CLS
 
IF /I [%1] EQU [/RESTART] (
    taskkill /im SystemPulseConfigEditor.exe /f
    taskkill /im EpicSvcHost.exe /f
    taskkill /im EpicSvcMaster.exe /f
 
  > "%TEMP%\SYSPULSE.ps1" ECHO ipmo *appv*
 >> "%TEMP%\SYSPULSE.ps1" ECHO get-appvclientpackage Epic_SystemPulse_2014_x86 ^| Stop-AppvClientPackage -Global
 >> "%TEMP%\SYSPULSE.ps1" ECHO get-appvclientpackage Epic_SystemPulse_2014_x86 ^| Remove-AppvClientPackage
 >> "%TEMP%\SYSPULSE.ps1" ECHO Sync-AppvPublishingServer 1
 START "" powershell.exe -windowStyle hidden -executionPolicy byPass -command "&("%TEMP%\SYSPULSE.ps1")"
 exit
)
 
> "%TEMP%\1.ps1" ECHO Start-Process -WindowStyle Hidden "D:\AppVData\PackageInstallationRoot\294A4DC7-2528-4C7E-832E-12D6053B4B6C\6A22D374-9682-4F48-B6B7-CC25BA16A943\Root\VFS\ProgramFilesX86\Epic\System Pulse\WindowsDataProvider\SystemPulseConfigEditor.exe"
 
ping 127.0.0.1 -n 10 >NUL
::Check for SystemPulse process
:LOOP
 
START "" powershell.exe -windowStyle hidden -executionPolicy byPass -command "&("%TEMP%\1.ps1")"
ping 127.0.0.1 -n 30 >NUL
 
REM need the second check as we can't escape the brackets in the first check
tasklist  /fi "IMAGENAME eq SystemPulseConfigEditor.exe" | findstr /i /c:"SystemPulse"
if '%ERRORLEVEL%' EQU '1' GOTO LOOP
 
EXIT /b 

With this, my service will start upon application publish.

I set it to launch a exe that gets installed with the package (SystemPulseConfigEditor.exe) as I needed a unique name I could key in on to determine if the package started successfully. This has a drawback though, the exe I chose has a GUI and I launch the process with -WindowStyle Hidden; if the service crashes, this EXE prompts for attention. When RDP’ed into a server this notice comes up as “Interactive Service” something-or-other and clicking on it brings this exe visible. I have considered making a AutoIT app that has no GUI and just sits in the background doing nothing, but time has not permitted me this yet. Sometimes the SystemPulse service will crash so I added the /RESTART to this batch file to get it to kick off. By removing the package then ‘rsync’ing with the publishing server we trigger PublishPackage script which launches the service.