Automating Citrix Provisioning Server Install with RES AM

Here is a blog post I put together on automating the build of Citrix Provisioning Services using RES Automation Manager 2012. Before we get into the details I thought I’d mention a few resources and solutions I found on the way which helped me out. A big thanks to:

Before you can begin you will need to make sure you have the following prerequisites in place:

  • Provisioning Server Software (PVS 6.1 used for this example);
  • Windows Server 2003 upwards (Windows 2008 R2 SP1 used in this example);
  • NET 3.5 or higher is installed;
  • RES Automation Manager 2012;
  • Use the latest Citrix Licensing server.

I’ve split the automated process in to two distinct parts; creating the PVS database and installing PVS to make it easier to digest. If you’re lazy or just want to crack on you can just download the building blocks and get going! Note: you will need to update the resource references to the PVS 6.1 installation files.

Creating the PVS Database

Before you can automate the PVS installation we need to have a database in place for the PVS servers to connect to. Unfortunately for us there’s not an easy way to accomplish this as we need to generate an SQL script with our required database values. As we’re invoking the creation process from RES Automation Manager 2012 we can utilise parameters so we can prompt the administrator for these values at run time.

To create the SQL script we first need to install the Provisioning Services software on a clean Windows 2008 R2 server or if you have an install already you can obtain from here. Once installed we can run C:\Program Files\Citrix\Provisioning Services\DBscript.exe to launch the Provisioning Services Database Script Generator. Exciting stuff I know !!!


If we complete the details with placeholders (as above) for the database name and farm name, DBscript will create the required .SQL script with values that we can use within our RES Automation Manager jobs. Click OK and it will create the CreateProvisioningServerDatabase.sql file in the path specified, complete with embedded placeholders.

We can now import this file as a resource into the RES Automation Manager console. Note: remember to tick the ‘Parse Environment variable and parameters’ checkbox. If you forget to do this we’ll attempt to create a database with a name of $[PVSDB] which probably won’t work (not that I’ve checked!).

To create the required SQL database we can utilise the CreateProvisioningServerDatabase.sql file with the built in RES Automation Manager database connector task(s) or via SQLCMD on the local Microsoft SQL instance. As we’re cheap and can’t assume that you’re licensed for the relevant connector, we’ve utilised SQLCMD in the building blocks. For more details on this, download them and have a look.

After the database has been created we need add SQL permissions to the database (if using a network user for the SOAP and STREAM services). This is achieved with a couple of SQL statements (see the building blocks for more information). If we’re using an Windows service account to run these services, the user will be configured later during the install… And now the fun begins;

Installing and Configuring PVS

Now that the database is created we can move on to installing the software, configuring and adding servers to the farm. Installing the software is no problem however configuring and adding servers to the farm is a bit more involved. The method I used for configuring the servers was by utilising the configwizard.ans file which holds all the configuration items. By running the %PROGRAMFILES%\Citrix\Provisioning Services\configwizard.exe /s the answer file is in turn created here C:\ProgramData\Citrix\Provisioning Services\configwizard.ans.

Once we have the configwizard.ans file we can edit it and embed our RES Automation Manager 2012 parameters within it. If you’d like to know what options can be configured in the answer file, run configwizard.exe /c. The configuration wizard will write a C:\ProgramData\Citrix\Provisioning Services\configwizard.out file. Again, all this information is in our building blocks.

I used two different answer files one for the first server joining the farm and the other for all subsequent servers. Below is an example of the first server configwizard.ans file:

DatabaseInstance= FarmExisting=$[PVSFARM]
BootstrapFile=C:\ProgramData\Citrix\Provisioning Services\Tftpboot\ARDBP32.BIN

Once the answer file/files have been created and modified, import them into the RES Automation Manager resources. Note: remember to select the ‘Parse Environment variable and parameters’ checkbox!

Finally to automate the actual PVS install, we need to make sure we download these resources to the C:\ProgramData\Citrix\Provisioning Services\ directory on the target server. Then we kick off the configuration wizard which will apply the configuration, by running configwizard.exe /a. Once complete the services should start automatically and when you start the PVS console and connect you should be presented with the new farm, well hopefully anyway !!

Problems Encountered

If you do have problems using the answer file and the install fails the best place to start troubleshooting is under C:\ProgramData\Citrix\Provisioning Services\Log directory. If all goes wrong you will notice that there will be only one file here;  configwizard.log. And at the end of this file hopefully it should give you some meaningful reason as to the failure. If all works fine and the services start you should see around 8 Log files and have a big smile on your face :D.

I did have other issues whilst getting this to work. Here are a few notes in case they help:

  • No device License available when a new machine is booted using provisioning server you will see the error in the streamprocess log on the PVS server and also on the device a pop message will say “No device License currently available for this computer a system shutdown will be initiated in 96 hours. I found the resolution to this problem was to upgrade the license server to the latest build.
  • PVS Console install does not install via AM job – ensure that UAC is disabled and use a security context to run the job instead of the local System account.
  • After a server install I could not mount Vdisks on PVS server and might get an error similar to “Cannot mount Vdisk mapi error”. Looked at device manager and noticed that the Citrix virtual hard disk Enumerator driver was not installed correctly. To resolve this first remove the device and then go to %PROGRAMFILES%\Citrix\Provision Services\Drivers right hand click and install cfsdep2.inf and then go back to device manager and add legacy hardware and select “I have disk” and then point to same location and the file is cvhdbusp6.inf. It should then hopefully install this device without any issues. Or the Preferred option with RES AM create a module to download the following, CFSDep2.inf and CFSDep2.sys to C:\windows\system32\drivers before installing provisioning server and all should be okay.
  • When using a service account make sure that this user is given the required permissions i.e read/write on the PVS store directory on the PVS servers / db_datareader and db_datawriter on the database although the latter can be done if you select configure user for database.

Building blocks now updated as there was a problem with the Service Account password passing through to the answer file, this should be resolved. Have also added a module to remove the answer file as the password is in plain text.

Hope this helps, Enjoy ! Smile Simon

[wpdm_file id=7]

RES Automation Manager Emergency Patch Management

I previously covered the reasons why you probably wouldn’t use RES Automation Manager for patch management (see here). Max Ranzau (AKA @RESguru) made a great point that you can certainly use Automation Manager to push a patch out individual patches easily. With the release of the Microsoft RDP critical patch MS12-020 and an exploit apparently in the wild, this proves that RES Automation Manager certainly still has its place in your patch management strategy.

Assuming that you haven’t exposed port 3389 directly to the internet you may feel that you’re somewhat “safe.” I actually think that the greater risk comes from worms that will be run from within the corporate network firewalls. All it takes is for one machine to be compromised… How many desktops and servers do you have inside the corporate network that have RDP access enabled?

Microsoft provides some workarounds that will give you time to test the patch prior to deployment. Fortunately, RES Automation Manager gives you the following options in dealing with this exploit using the built-in Automation Manager tasks/tasklets:

    1. Deploy the patch within minutes and/or
    2. Disable RDP connections completely and/or
    3. Enable/modify the Windows firewall rules to block RDP connections and/or
    4. Enable Network Level Authentication for RDP connections.

One thing is for certain, you need to be acting and mitigating this risk now. I think it’s only a matter of time before things get interesting. Who remembers Slammer?! I know people who are still mentally scarred by its long lasting effects!

GPOs could help you with some of this, but nothing is going to be able to deploy any of (or a mixture of) the above workarounds within minutes. How will you be sure that your workarounds are in place on all machines? RES Automation Manager will give you near instant feedback on what tasks failed and provide you with the data to target those computers. Remember, if you use RDP/Remote Assistance for support then you’re probably limited to option #1 (or maybe #4).

If you don’t have RES Automation Manager today, you probably wish you did! You’ve been warned Smile with tongue out..


RES Automation Manager 2012 Global Variables

Unfortunately, this post is a mixture of both good and bad news. In my humble opinion, I feel that RES have missed a trick with their implementation of Global Variables in RES Automation Manager (AM) 2012 and here’s why.

In all the furore surrounding the RES AM 2012 release, Global Variables are supposed to herald the completion of multi-tenancy implementations. For example, multiple departments and/or customers can be co-located on the same database and share the platform without any visibility or potentially any knowledge of who else is utilising the infrastructure. If you’re after an introduction into the RES AM Global Variables I suggest you take a look at Rob Aarts’s article on RESguru or watch Grant Tiller’s demonstration on REStutorials.

Resources and Global Variables

It was my assumption (obviously incorrectly) that we would be able to use Global Variables with file server resources. In a multi-tenant implementation, I wouldn’t necessarily want all administrators uploading file resources to the database and bloating the tables with BLOBS. When we add files stored on a file share to the RES Automation Database, the UNC path is stored along with the entry in the database. This isn’t necessarily a problem, assuming that all RES Automation Manager agents can resolve this path. Unfortunately, in a multi-tenant environment this may not be the case.

Enter Global Variables. Wouldn’t it be a great idea if we could use a Global Variable in the UNC path of a file resource?! As long as we make sure that folder structure is the same for each “customer” site we could set the Global Variable to the customer’s file server at the Team or if needed, Agent level. Even within a single organisation, Global Variables would enable us to use local file servers without having to implement DFS-R etc.

Being RES Consultancy Partners we could also use this process when designing our Building Blocks. For example, we could upload the required resources for a XenApp build to a file server, import the RES Automation Building Blocks and change the Global Variable(s) to point to the customer’s file server instead. No longer would we need to either perform a mass “find and replace” within the Building Block files or upload 5GB of data into a database. Happy days Smile.

As you’ve probably guessed, this doesn’t work. DOH! When we attempt to insert the Global Variable by right-clicking the file path we’re not given the option:


Manually entering the Global Variable placeholder, e.g. ^[GlobalVariable] doesn’t work either. There is, however, a workaround.

Resources, Global and Environment Variables

Now that we know we can’t use Global Variables at the resource level, I do know that we can use Environment Variables. If we just so happen to use an environment variable and that environment variable just so happens to be set to a Global Variable’s value, it just might work…

Firstly we need to pick a variable to use and in this example I’ll use ’RESAMRESOURCES’ as it’s unlikely to clash with any other environment variables. We define the Global Variable and set the value to our file server’s share (you can always override this at a Team/Agent level or when importing Building Blocks where needed):


Next, when adding a file resource we can browse the target file and override the UNC path and enter an environment variable. In this example I’ll use the %RESAMRESOURCES% to point to the required file server.


All that’s left to do is assign the environment variable before any module that we want to use this resource. Fortunately, RES Automation Manager has a task to do just this. In my example I’ve created a job-based environment variable. We could always set this as a persistent machine-based variable via AM too.


Once we’re done, our completed module will look a lot like this. Note: the job-based environment needs to be set before we execute a task that references the file server resources, in our case, the Unattended Installation of Foxit Reader task.


When we export our Module as a Building Block we now have a fully portable module that can be imported into any environment without storing the resource(s) in the database! All we need to do know is use Global Variables to define the credentials used to connect to the file server..

Resources, Global Variables and Credentials

This is where the house of cards falls down around us.. We’ve managed to trick RES AM into using file resources with Global Variables. However, as the RES Automation Manager service runs under the Local System account, it has no access to file resources located on file servers. To overcome this issue, we need to embed the credentials in with the resources. Again, you would assume that you could use the Credentials type of Global Variables to achieve this.


I’ve tried unsuccessfully to get this work, even my manually specifying the ^[GlobalVariable] placeholder. Perhaps I’m the only one, but what about password changes? If we embed the credentials with the resource, using a Global Variable for this would make perfect sense. Currently, we don’t change the password associated with the RES Automation Manager resources as this requires us to update each individual resource. If they were based on a Global Variable we’d have a simple way to update the password, maintain security and pass an audit with flying colours!

I can only assume that this is either technically difficult to implement or is an oversight. As a result, we’re still left have to either do a mass “find and replace” in our Building Block files when implementing RES Automation Manager at customer sites or uploading large binaries into the database. Other than this, I think Global Variables are a brilliant edition and hopefully they will be coming to RES Workspace Manager too Smile with tongue out.

Many thanks for reading. Iain

RES AM Passing Values Between Scripts

You don’t need to be told how great RES Automation Manager, but there are some things that we can only achieve via scripts; be it VBscript or PowerShell. In my example, it is scripting XenDesktop and XenServer for the demo showcase platform (more on this at a later date). There is currently no way to automate these products without using scripts. Unfortunately (for me) it’s always been problematic to pass values in and out of scripts to other modules. We can certainly pass a value into a script, but then we can’t return it to be used elsewhere.

My problem required creating an AD user (not via the built in task) in one script and then passing the username/password into another script. To overcome this particular issue, I started down the route of temporarily writing the information to the registry so that it could be read by the other script later in the Project. This is where I stumbled across a little gem hidden in RES Automation Manager. I don’t know whether it’s intentional and/or undocumented, but it certainly works!

I attempted to use a Parameter using the built-in @[REGISTRY] Function. In essence this instructs the RES Automation Manager agent to populate the Parameter with the contents of the registry key. This bit is simple to understand and you probably already knew this. However, what I didn’t realise is that this Parameter is updated/re-evaluated at every task within a Module. I assumed that it would only be evaluated when the Module is invoked by the RES AM agent. I’m certainly glad that this is not the case as we can now write values to the registry and AM will automatically pass the updated value to the next Task(s).

[wpdm_file id=8]

Here is an example building block that contains a single module with a single registry-based, emtpy Parameter value. The first script writes the current date to temporary location in the registry (just so happens to be where RES Automation Manager is reading the Parameter value from). The second script receives its Parameter value from RES AM (not directly from the registry within the script), adds a day (in US format!) and writes it back to the registry. The final task displays a pop-up message with tomorrows date from the RES AM Parameter.

What this does prove is that the Parameter is re-evaluated before each task is executed and therefore passed through all tasks. Never in this example module do we enter the date. Here is the status of the parameter before and after each task.

Task 1 – BEFORE: <Empty>, AFTER: <Empty> (We write today’s date to the registry, but it’s not re-evaluated until the next task)
Task 2 – BEFORE: <Current Date>, AFTER: <Current Date> (We write tomorrow’s date to the registry, but it’s not re-evaluated until the next task)
Task 3 – BEFORE: <Tomorrow’s Date>, AFTER: <Tomorrow’s Date>

I’m sure you can think of more ingenious ways of using this functionality. Enjoy! Iain