A Technique to Run Scripts Asynchronously via AppSense Environment Manager

Whilst AppSense Environment Manager (EM) has a rich set of trigger points, such as logon and reconnect, where you can run built-in actions or scripts, until very recently there was no built-in way of running something say five minutes after logon or every thirty minutes.

I’ve seen techniques that create scheduled tasks, which you can do directly in a PowerShell script rather than having to modify XML task templates, but with these you still have to get the script you want to run onto the local file system. Whilst I came up with a method of embedding any arbitrary file into an AppSense EM configuration, such as image files (see here), that approach is a little cumbersome, particularly if you need to change the embedded script.

I find an easier way to achieve this goal is to write a script inside an EM logon action that takes a copy of itself to a local file and then invokes powershell.exe to run it so that it runs outside of EM thus allowing the logon to finish but the newly launched script is still running as it is hosted inside an asynchronous powershell.exe process. Inside this script you can then wait for any period of time and then perform an action and either exit or loop around ad infinitum, or at least until logoff.

It works by looking to see if the -async option has been specified when the script was run. When EM runs the script, it won’t specify any options so we detect this and make it fire off a copy of the same script, since EM will delete the original when the script it invoked finishes, via powershell.exe which will take a different path through the code because the -async parameter is specified this time.

There’s an example version 10 EM configuration available for download here although the technique will work with any EM version. You will need to put your code into the PowerShell custom action, after the comment line reading “we are now outside of EM so can trigger later”, to perform whatever you need it to do. You might just want it to do something every few minutes or hours or you could put a file system or registry watcher in to trigger when something specifically changes (now there’s an idea for a future post if ever there was one!).

If you need the script to run elevated then simply set the action to run as System in the configuration but remember that environment variables and HKCU will not be for the logged on user but for the System account.

If you are also using AppSense Application Manager to prohibit powershell.exe then as long as you have configured it to ignore restrictions during logon then  it should be ok but you could always add a Process rule for EMUser.exe to allow powershell.exe child processes.

Embedding files in an AppSense Environment Manager configuration

If you use AppSense EM for copying files from central locations to your end-points to use in logon actions, I’ve come up with a nice and easy way to embed these files into the configuration itself so that there is no need for or reliance on a file server to copy these down to the end-point. What you lose is the ability to compare file timestamps and so on since we will be dynamically creating the content on the end-point so its timestamp will be the current time, more or less.

We achieve this by simply encoding the source file, which may be a binary such as a wallpaper image, inserting that encoded data into a PowerShell custom action which decodes it and writes it to file and then using the file we’ve just created in other actions such as setting the “wallpaper” registry value.

To encode a file, we use the following lines of PowerShell that we put in a .ps1 file somewhere since this code isn’t going in to the EM configuration:

$inputFile = 'Path to your file for encoding'
[byte[]]$contents = Get-Content $inputFile -Encoding Byte
[System.Convert]::ToBase64String($contents) | Set-Content –Path c:\encoded.txt -NoClobber

The more adventurous among you might want to wrap this into a PowerShell script that takes parameters for ease of reuse. This gives us a text file “c:\encodedfile.txt” whose contents we need to embed into an EM custom action so open the file in your preferred text editor, select all content and copy to the clipboard. It should look something like this:

encoded

Now in your EM configuration create your custom action like the example below, where you paste the text copied above into the definition of the variable $encoded between the quotes, making sure that it all goes on one line.

$encoded = 'Paste your encoded data in here'
$newfile = ( $env:temp + '\myfile.jpg' )
[System.Convert]::FromBase64String($encoded) | Set-Content -Path $newfile -Encoding Byte

Which will result in a file “myfile.jpg” in the user’s temporary folder which can then be used as required. Obviously use the same file extension as for the file that was originally encoded.

If the file needs to be written somewhere where the user doesn’t have write access then simply run the custom action as system.

And that’s all there is to it – nice and easy thanks to good old PowerShell. I haven’t tried it for huge files but it certainly works fine for files that are up to hundreds of KB in size.

The base64 encoding will result in data which is four thirds the size of the original file.

If it’s a PowerShell script that you want to embed, then I’ll show you a different technique for doing this in a later post which allows that script to be used asynchronously outside of EM without the need to create scheduled tasks or similar.