The Collection

A collection of useful information.

PowerShell: DSC, Custom Modules, Custom Resources, and Timing...

Hypothetical situation, you want to accomplish the following:

  • Install the App-V 5.0 SP2 client.
  • Configure the client.
  • Restart the service.
  • Import App-V sequences.

If that sounds simple to you, then you haven't tried it in DSC.

If fairness it isn't THAT complex, but it isn't very straight forward either, and there is little real reason for it to be complicated beyond a simple lack of foresight on the part of the DSC engine.

The first three tasks are very simple, a Package resource, a Registry resource, and a Script resource. But that last bit is tricky, and that is because it needs the modules installed in step 1 in order to work, but given that the DSC engine loads the ENTIRE script (which is normal for powershell, but given the nature of what the DSC engine does this is, IMO, a BIG hinderance) they aren't there when it first processes, so it pukes. You can tell this is your problem if you see a "Failed to delete current configuration." error (the config btw should at that point be visible right where WebDownloadManager left is, C:\Windows\Temp\<seriesofdigits>) as well as a complaint that the module at whatever location could not be imported because it does not exist.

So what is the solution?

Sadly kind of convoluted. First lets look at the config. Pretty simple, I call a custom resource that imports the App-V sequences and in that custom resource I have a snippet of code at the very top:

$modPath = "C:\Program Files\Microsoft Application Virtualization\Client\AppvClient"

if((Test-Path -Path $modPath) -eq $true){ Import-Module $modPath }else{ $bypass = $true }


Now lets look at the script baked into the PVS image:

  • Enable WinRM. Easier to do this than undo it in the VERY unlikely case we dont want it, DSC needs it so...
  • Create a scripts directory. Not terribly important, you could just bake your script into this path.
  • Find this hosts role/guid from an XML file stored on the network.
  • Create LCM config.
  • Apply LCM config.
  • Copy modules from DSC Module share. This overlaps with the DSC config but the DSC config will run intermittently, not just once, for consistency.
  • Shell out a start to the Consistency Engine.

This last bit is VERY important in two regards. The first is that if you just run powershell.exe with that command it WILL exit your script. The only way I've found to prevent this is to shell out so that it closes the shell, not your script. StartInfo.UseShellExecute is thus very important.

The second important bit is wiping out the WMI provider, without this it waited three minutes and ran again and promptly behaved like the $bypass was still being tripped, even though I could verify the module WAS in fact in place, I do not like this caching at all.

So the first time I run consistency I know it wont put everything in place, because it needs the client installed before the client modules exist and even with DependsOn=[Package]Install it still pukes, depends on doesn't seem to have any impact on how it loads in the resources.

I wait three minutes because I want to give the client time to install, I don't love this but this is just example code, in reality you would mainly be concerned with two things:

  1. Is the LCM still running.
  2. Is the client installed.

So I would probably watch event viewer and the client module folder before making my second run, timing out after ten minutes or so (in this case 15 minutes later the scheduled task will run it again anyway, don't want to get in the way).

Why bother with this? Mainly because I don't want to wait half an hour for my server to be functional. I run them initially back to back because I can either bake the GUID in, or use a script to "provision" that, while I'm there why rely on the scheduled task? This is on a Server 2008 R2 server so I can't use the Get-ScheduledTask cmdlet, and while yes I could bake in the Consistency task with a shorter trigger and change it in my DSC config...but that is just as much work and more moving parts.

I want to configure and make my initial pass as quickly as is safe to do so, and then allow it to poll for consistency thereafter.