Now that StoreFront 3.0 is out, I wanted to return to the topic of Approvals Workflows and give some more guidance as to what can be built using the new Receiver APIs.

My previous post showed what was possible with the tech preview, but it also shone the light on a few teething problems. I think I can safely say these have all been resolved.

For this post, I want to give a relatively prescriptive “How To.”

You can, of course, tweak my code examples, but I’m going to do my best to give a complete recipe. The one gap will be the approvals system itself–the piece that takes a pending request and either marks it as either approved or denied. You can build this any way you want, and an upcoming post by my colleague John McBride will give an example using Octoblu as the workflow system.

STEP ONE: Decide What to Cook

How do you want to manage workflow? There are really two choices:

  1. Publish applications in XA/XD to groups of users that are marked as ‘needing approval’ and then use the StoreFront subscription service to record when approval is granted.
  2. Use an active directory group to record who is approved to use an app.

The first approach is ideal for a PoC and perfectly good for production, but in practice, many customers I’ve talked to already have a backend system they wish to leverage, and that tends to use the second approach. I’m going to do both here; 90% of it is the same.

I’m going to use the (slightly unrealistic) example of workflow to enable access to the “Paint “app. I want all users in the ‘Design’  group to see the app, and be able to request it.

For the first approach, I’ll use the StoreFront subscription store to record who is allowed access. For the second approach, however  (using AD for approval), I’m going to assume there is a group called ‘PaintUsers’, and that the back end workflow system will add people to this group as access is approved.

Behind the scenes for this second approach, we are going to use two apps: the “real” Paint app with access controlled in the usual way, based on AD group membership and a “surrogate” Paint app that is purely there to trigger the workflow process.

Why two apps? Well, the real app is only shown to users who have approved access (i.e. members of the PaintUsers AD group), so we need something to show to potential users (i.e. members of ‘Design’).

STEP TWO: Prepare App(s)

The next step is to publish the Paint App on XenApp. For the most part, this is a regular app, but we are going to use keywords to mark it out as special. The WFS keyword is recognized by the StoreFront workflow plugin (see later) to indicate the app needs approval. The other keywords are new and used by code in this blog. I’m using XenApp 7.6 for the screenshots below.

For Approach 1 (StoreFront approvals):

Publish the Paint app to the ‘Design’ group, and add the following to the end of the description.

KEYWORDS: WFS WFQuestion="Please explain why you need this app?"
<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;"> </span>

For Approach 2 (AD approvals):

Publish the Paint app to users in the ‘PaintUsers’ group (this is the app that will ultimately be granted to users). It isn’t going to have the workflow keyword, but we will mark it as workflow-related, so we can automate the creation of a second, surrogate app to be shown to users. We also add the ‘AUTO’ keyword so that when it is granted to a user, it will be subscribed automatically.

Add the following to the end of the description.

KEYWORDS: AUTO WFGroup="Design" WFQuestion="Please explain why you need this app?"

<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">We will to automate the creation of the surrogate apps, and on XA 7 this is a few dozen lines of PowerShell.  </span><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">This PowerShell is provided in the download bundle and is called CreateSurrogateApps.ps1.</span>

It does the following:

  • Searches for all apps with keyword WFGroup
  • For each of these apps, creates a surrogate app (if it doesn’t already exist) in the specified Delivery Group (note this delivery group must already exist)

You can run this from the PS command shell on the DDC.

<a href="/blogs/2015/08/17/citrix-recipe-box-storefront-approvals/createsurrogateapps/" rel="attachment wp-att-174213394"><img class="alignnone size-full wp-image-174213394" src="/blogs/wp-content/uploads/2015/08/CreateSurrogateApps.png" alt="" width="408" height="94" /></a>

For those only earlier versions, or who prefer a manual approach, publish a surrogate app with the following characteristics (or clone/edit the real one)

  • Name, folder, icon etc.: identical to the real Paint app
  • Executable: Any – will never be run. E.g. C:\nothing.exe
  • Description should be the same as for the real Paint app, but with the following keywords at the end:
    KEYWORDS: WFS WFQuestion="Please explain why you need this app?"

    (i.e. identical keywords to case 1)

Regardless of how you create the surrogate, here is what it should look like in Studio:

STEP THREE: Turn On the Oven

This is a change to the StoreFront Server. You need to install and enable the simple workflow handler, as we did in the previous blog. This will ensure that any workflow app that is subscribed to is put in a pending state.

Install details are as for tech preview:

  1. Find the store directory for the store you want to enable workflow for. For example C:\inetpub\wwwroot\Purple
  2. Edit web.config in this directory and add the following to the end of the <components>section
    &lt;component id="SimpleWorkflowHandler" 
     SimpleWorkflowHandler" service=
     Citrix.DeliveryServices.DazzleResources.Workflow" /&gt;
  3. Download the SimpleWorkflowHandler DLL from the download bundle and save it in the bin directory (e.g. C:\inetpub\wwroot\Purple\bin). Note that this has a few bug fixes over the version in the previous blog.


This is where your custom integration comes in.

As with the tech preview, we will add code to StoreFront to monitor the subscription store for pending requests, and fire off some custom logic to start the approval process. That will ultimately lead to us updating the subscription with state=approved (for the non-AD  case), or adding the user to the appropriate group (for the AD case). For both cases, if the approval is denied, the app must be marked with state=denied. Again we will use PowerShell to monitor for apps entering the “pending” state, and you should update the code below to call your workflow system (or see Kevin’s upcoming blog on using OctoBlu for workflow).

Note that as with the tech preview you need to download the SubscriptionStoreHelper dll from the download bundle (at the end of this post), and add the current user to the CitrixSubscriptionsStoreServiceUsers local group to enable access. See the previous workflow blog for details.

The PowerShell below is included in the download bundle and can be run by calling Purple

From the PowerShell command line, assuming ‘Purple’ is the name of your store.

Note that this script has a big hole in the middle, which you need to fill with a call out to your workflow system to replace the following.

Write-Host " User     : " $entry.User
Write-Host " App Name : " $appName
Write-Host " Reason   : " $answer

For testing (Senario 1) you could manually approve or deny an app from the command line using the SSClient app: For example
SSClient.exe –store Purple –update –user “WWCO\Richard” –resource “Paint” –status approved
For scenario 2 testing, just add the requesting users to the ‘PaintUsers’ AD group.

STEP FIVE: JavaScript Icing

There are several little, but important, details that we will tidy up with some client side script.

  1. Ask the user why they want the app, and make that info available to the workflow system
  2. Let the workflow system know the name of the app, rather than just its internal ID
  3. Let the approver say why they denied a request
  4. Make sure that in the AD case we tidy up the surrogate app that was requested, so the user doesn’t see two copies.

To achieve these we are going to add a bunch of small extensions, and leverage the ability to store arbitrary properties in the subscription store. All these extensions are included in a single script.js file in the downloads section of the blog.

First, we need to make sure the extended app and subscription properties are sent to Receiver. In script.js, we enable this by adding:


Next, we want to ask the user a question when they request the app, and store their answer in the StoreFront subscription store.

We are going to examine the custom properties on the app to decide what to ask, and add a subscription property to store the answer. For the dialog I’m reusing the ‘ShowMessage’ extension API but setting its contents to a snippet of HTML with an input box. This gives us the following (without any restyling – which I could have added via style.css). Note that if the WFQuestion is not specified in the app’s keywords, we won’t show the dialog.

I’ve also added in some code to pass the name of the app back as a subscription property. This is because by default our subscription store just records the internal app ID – and if you are going to link to a back end workflow system, it probably won’t use our identifiers!

See script.js for full code, but the essence of this is:

 // Check the app has a subscription properties array 
 app.subscriptionproperties = app.subscriptionproperties || {};
 // set properties for the app name and the answer to the workflow question
 app.subscriptionproperties["appname"] =;
 app.subscriptionproperties["WFAnswer"] = answer;

For the AD case, we want to check for surrogate apps that have been requested and have matching “real” apps. When we find them, we cancel the request and hide the surrogate. For the non-AD case (scenario 1), this code is harmless, but unnecessary.

The easiest place to spot duplicates is by hooking <span style=’font-family:courier’>preProcessAppData</span>, as we get all app data at once. This is too early to actually update the apps, as we have to wait until the app objects are created, so we also hook the <span style=’font-family:courier’>excludeApp</span>  call to mark the duplicate as hidden, and<span style=’font-family:courier’>afterDisplayHomeScreen</span> as a good point to to call the server to tell it to mark the surrogates as no longer requested (putting then back into their default state ready for next time).

I’ve included the code to do all this inline below, as it is a useful example of a common pattern – examine apps and then process them later:

<span style="color: green;">// Create a list of surrogate apps that have subscribed 'real' apps</span>
<span style="color: green;">// This is used for AD case only</span>
var surrogateAppsToUnsubscribe = {};

CTXS.Extensions.preProcessAppData = function (store, json) {
 <span style="color: green;">// find all apps that are pending</span>
 var pending = {};
 var i;
 for ( i = 0; i &lt; json.resources.length; i++) {
   if (json.resources[i].subscriptionstatus == "pending") {
     pending[json.resources[i].name] = json.resources[i];
 <span style="color: green;">// find all subscribed apps that have a pending app of the same name</span>
 for ( i = 0; i &lt; json.resources.length; i++) {
   if ((json.resources[i].subscriptionstatus == "subscribed") &amp;&amp;
       (pending[json.resources[i].name])) {
     surrogateAppsToUnsubscribe[json.resources[i].name] = true;

<span style="color: green;">// Exclude surrogate apps, and as a side effect remember the app objects</span>
CTXS.Extensions.excludeApp = function (app)
 if ((app.subscriptionstatus == "pending") &amp;&amp; 
      surrogateAppsToUnsubscribe[])) {
   surrogateAppsToUnsubscribe[] = app;
   return true;
 } else {
   return false;

<span style="color: green;">// Our own function to unsubscribe surrogate apps</span>
function unsubscribeSurrogateApps() {
 for (var i in surrogateAppsToUnsubscribe) {
   var app = surrogateAppsToUnsubscribe[i];
   app.subscriptionstatus = "unsubscribed";
   app.subscriptionproperties = {}; <span style="color: green;">// discard any properties</span>

<span style="color: green;">// a suitable time to call our unsubscribe function is before</span>
<span style="color: green;">// the main UI is shown</span>
CTXS.Extensions.beforeDisplayHomeScreen = function (callback) {

Last, but not least, we need to let a user know why an app was denied (assuming a reason is given). The first part is to arrange that the reason for denial is recorded in the subscription store. Getting that info is out of the scope of this blog, but recording in the store is simply a case of adding a property when you set the app to denied.

For example using the CLI tool from last blog (also in the download bundle):
SSClient.exe –store Purple –update –user “WWCO\Richard” –resource “Paint” –status denied –props DeniedReason=”Because you can’t draw”
Finally, we need to present the message to the user the next time they run receiver.

There is no direct hook into the ‘denial ui’ (something for v2…) but we can hook elsewhere and achieve the same effect. As the generic ‘some apps were denied’ message is shown when the UI is first opened, so we need to get in early, show the custom ui and mark the apps as no longer denied, so that the regular UI is not also shown.

See the download bundle for the code (all in script.js) but here is the end result:

STEP SIX: Enjoy 

Apart from the obvious omission of a workflow backend, this should be pretty much all you need. Note this blog built on the previous blog, and please look at that for more step by details of how to install the workflow helper and subscription store dlls. I’ve updated those dlls and included all the code used in this blog in a download bundle here. These are available under the normal Citrix Developer Network license (again see previous blog)

Please let me know if this works for you – and any extra’s you’d like me to cover in future.

Blogs in this series