AppSense and VMWare View Horizon Linked Clones

The move from persistent physical desktops, to non-persistent linked clones (with a separate user personalisation layer) requires rethinking the way in which machines are configured and software is deployed. The challenge is to deliver a consistent, highly available platform with the maximum efficiency. In this case efficiency means utilising Horizon View’s ability to dynamically provision just enough desktops, while ensuring that the necessary configuration changes are delivered by AppSense.

Computer configuration

We do the bulk of computer configuration via GPO. This includes things like removing unnecessary Windows Features, optimisation of machines for use as VDI hosts, and using Group Policy Preferences to configure local groups and accounts.

Generic platform software (Java, Flash, Microsoft Office, the App-V Client, etc.) and Windows hotfixes are installed to the all of the Pool Masters via SCCM. Pool specific applications are also deployed to specific pool masters via manually configured SCCM Device Collections. This ensures consistency within pools and – where possible – between pools. Consistency is obviously important for the users and the people supporting them, but also helps with storage de-duplication.

This process effectively takes a vanilla Windows 7 machine as input, and outputs a configured corporate virtual machine desktop. This means that the majority of changes have been applied before AppSense gets involved.

Deploying AppSense Agents

The AppSense Client Configuration Agent and the Environment Manager Agent are also deployed to all of the pool masters. We do this via an SCCM package, which configures the MSI so that the clients look towards either the Production, or BCP, AppSense instance (based on their OU).

MSIEXEC /I "ClientCommunicationsAgent64.msi" /Q WEB_SITE="http://appsense:80/"

To avoid all linked-clones sharing the Pool Master’s AppSense SID, we need to remove the SID from the pool master. This is done via a shutdown script on a GPO linked to the pool master’s OU.

User configuration

User configuration is done via AppSense on the linked clones themselves.

As the Environment Manager Configuration is modified at a greater frequency than the pool masters are updated, we don’t want it installed on the pool master prior to deployment. Rather we want the machines to download the newest configuration as it becomes available. AppSense allows us to deliver the latest version of the configuration.

AppSense Configuration Schedule

Remember, that as we’ve already applied computer settings via GPO, we don’t need to worry about restarting the computer after the AppSense configuration has been installed (which we would need to do in order to apply AppSense start-up actions). We’ve also pre-deployed the agents (Environment and Client Communication), which means that the installation of the configuration should proceed fairly quickly.

Ensuring the machine is “ready” for the user

However, this approach did introduce an issue where View was provisioning Linked Clones and marking them as “Available” (and potentially allowing users to log on) before the configuration had been downloaded. This would result in users getting machines which had not yet had the configuration applied. In order to give AppSense enough time to deploy new configurations, we introduced an artificial delay to the start-up of the VMware View Agent (WSNM). The OUs which contain the linked clones, have the following start-up script:

Get-Service -Name "WSNM" | Stop-Service | Set-Service -StartUpType "Manual"
Start-Sleep -Seconds 300
Get-Service -Name "WSNM" | Start-Service | Set-Service -StartUpType "Automatic"

This script results in machines showing as “Agent Unreachable” for the first five minutes after starting-up which gives AppSense enough time to deploy the configuration.

Displaying vSphere disk properties (including provisioning & persistence)

I was doing some tidying of old scripts and came across something I thought it might be useful, so I tidied it up and added some documentation.

Screenshot showing results of script

This PowerShell script uses the vSphere PowerCLI to display a list of virtual machine disks, file-names, modes (persistent or non-persistent), sizes and whether or not the disk is thinly provisioned. You’ll need to connect to one or more vSphere servers first.


It’s likely that I based the original version of this script on someone else’s work as it contained a couple of techniques which I don’t tend to use (like using Select-Object to create object properties), but I’m afraid I can’t remember where, and searching for a couple of keywords brings back no results.

Accessing SCSM 2012 using PowerShell (without SMLets)

SCSM2012I wanted to use PowerShell to create a simple report of open (active & pending) incidents in System Center Service Manager 2012, but the only examples I could find online used the SMLets. Sometimes this wasn’t obvious, but soon became apparent when PowerShell choked over CMDLets like Get-SCSMObject.

While I’m sure the SMLets are handy for ad-hoc reports by administrators, I wanted the option for my report to be generated by a group of users on their own machines, so I was wary about deploying unnecessary (beta) software to a group of desktops. It was therefore preferable to do this using the native SCSM 2012 CMDlets (which the users already have installed as part of the SCSM 2012 installation).

Anton Gritsenko‘s mapping of the SMLets to their SCSM 2012 native commands was invaluable in the creation of this.

This script should work on any machine with PowerShell and SCSM Console installed. As with all PowerShell objects, this can then be output to HTML, CSV etc.

This is the first component of a more involved MI framework I have in mind, and was good practice with the SCSM 2012 API.

Remove machine objects from VMWare View’s ADAM database with PowerShell

26/02/14 –  I’ve updated this script to accept pipeline input and work a little more efficiently when removing multiple machines.

It’s one of those things that shouldn’t happen, but which inevitable does. Someone removes a View managed VM from vSphere, and View refuses to realise it’s gone. It also sometimes happens when machines fail to provision correctly (i.e., due to lack of available storage). The procedure is easy enough to follow, but it’s time-consuming and prone to error. In order to make the cleanup operation easier, I wrote up a quick function below. It relies on the free Quest AD CMDLets. You’ll want to change yourconnectionserver to, er, your connection server. Obviously the normal caveats apply: the ones about running scripts you downloads from the internet in your Production environment.

Invoke a PowerShell script directly from Subversion

I’ve been thinking about doing something like this for a while. By adding this to PowerShell profiles, I can ensure that other people who use my scripts/functions are using the latest versions by having them run directly from a Subversion URL. This negates the requirement for them to have a local SVN repo (and for them to keep it up to date).

Our Subversion is set up for basic, rather than AD integrated, authentication, but I imagine AD integrated authentication would be easier to implement (probably using Invoke-WebRequest with the UseDefaultCredentials parameter). Rather than prompt the user at each use, I set up a service account which has only Read permissions on the repository, and hardcoded the Base64String encoded username and password into the internal version of this script.

The terrifying looking regex was based on SqlChow‘s example, with PS1 appended to the end.

Using AppSense to deliver Microsoft App-V 5 applications to non-persistent VDI desktops at logon

I’m currently working on user-centric application delivery to non-persistent VDI desktops. The rationale for this is that the more applications which can be delivered dynamically, the fewer pools we need to provision. This suits applications like Microsoft Project and Visio, which both tend to be used by a small number of people on each pool. These apps are too expensive to deploy to non-users, and need locked-down to fulfil license requirements if deployed under Citrix. User-based deployment via App-V allows the application to be targeted to users in existing pool; however, the non-persistent nature of the desktop means that the application needs delivered quickly (and silently) at each logon.

While App-V integrates with SCCM, user-targeted applications can take a couple of minutes to be available. This isn’t really an option for this kind of non-persistent desktop deployment as it’s likely to result in confused users whose “missing” applications appear as they’re on the ‘phone to the service desk.

We needed a way to deliver applications to users, based on their AD group membership during logon. AppSense (which we already had deployed) seemed ideal. AppSense has wizard-based integration for App-V, but the dialog only allows you to select App-V 4’s SFT files, not the new APPV files created by version 5.

App-VDialog

I started by running a custom (PowerShell) script, using the new CMDLets. The script itself is pretty straightforward, checking to see if the CMDLets are loaded (and loading if necessary), adding the client-package from the file, and publishing it. We need to set the -Global paramater on the Publish-AppvClientPackage as we’re running the script under System context, and we want it to be visible to the user.

CustomAction

The problem was that AppSense would wait until the script executed before completing the logon. With some of the larger applications, this resulted in a delay of 10-15 seconds before the desktop was usable.

Invoking the PowerShell executable with the command above passed as an argument above worked as intended, but required encoding the path argument in order to escape the pipe character. This meant that a simple installation like

PowerShell.exe –Command {Add-AppvClientPackage -Path "\\apps\app-v$\Microsoft Office Project 2010\Microsoft Office Project 2010.appv" | Publish-AppvClientPackage –Global}

Would turn into this:-

PowerShell.exe –EncodedCommand {QQBkAGQALQBBAHAAcAB2AEMAbABpAGUAbgB0AFAAYQBjAGsAYQBnAGUAIAAtAFAAYQB0AGgAIAAiAFwAXABhAHAAcABzAFwAYQBwAHAALQB2ACQAXABNAGkAYwByAG8AcwBvAGYAdAAgAE8AZgBmAGkAYwBlACAAUAByAG8AagBlAGMAdAAgADIAMAAxADAAXABNAGkAYwByAG8AcwBvAGYAdAAgAE8AZgBmAGkAYwBlACAAUAByAG8AagBlAGMAdAAgADIAMAAxADAALgBhAHAAcAB2ACIAIAB8ACAAUAB1AGIAbABpAHMAaAAtAEEAcABwAHYAQwBsAGkAZQBuAHQAUABhAGMAawBhAGcAZQAgAC0ARwBsAG8AYgBhAGwA}

Not the most human-readable command, and it would be difficult to manage more than a few applications.

I got around this by writing up a script which would accept the APPV file path as an argument. I converted it to an EXE (using PowerShell Studio) and put it on a share. Conversion to EXE avoided changing the security policy to Bypass from RemoteSigned (or the requirement to deploy a certificate), and also simplified the command-line used in AppSense. The result looks like this:-

Execution

This action is run under the UserLogon node with a condition based on the user’s AD group membership. All assigned App-V installations are run synchronously.

As the user’s logon no longer waits for the execution to complete, the applications can take around 5-10 seconds to become available after the user is presented with their desktop. The delivery is quick, silent, and easy to document for handover to the team members who will be doing the day-to-day application management.

I’m sure AppSense will add support for App-V 5 in a future release, and it’ll be interesting to see what kind of functionality they build-in.

Get logon timings from AppSense client log

I’m doing logon tuning just now for non-persistent VDI desktops – seeing long it takes to deploy App-V packages on a per-user basis to a generic desktop based on AD group membership. To assist with the tuning, I thought it would be useful to write a quick PowerShell function to gather information from the AppSense event logs in a way that allowed easy sorting, display, recording and comparison.

Example output, sorted and piped to Out-Gridview

Get-AppSenseLogonTimes_ExampleOutput

You need to have Send events to the AppSense event log set to Yes.

AppSenseEvents

If you’re using persistent desktops, the function might not be as useful as-is unless you clear the AppSense log at log-off, or parse the results in some way that you only get events for a single logon.

As for the results of the tuning, it looks like we can get 4-5 larger applications (Project, Visio, etc) deployed on a per-user basis while only adding 7-12 seconds to the logon. However, this required a custom installer, which executes the App-V installation in a child-process. Executing the Add-AppvClientPackage CMDlet with a Custom Action caused AppSense to wait until the package had completely loaded before continuing. The slight (3-4 second) delay between logon completing and the application appearing in the Start Menu is acceptable, and delivery using AppSense in this manner is still better than SCCM which can take a minute or two.

Server Uptime (2013 Scripting Games practice)

I’m not sure yet if I’ll take part in the 2013 Scripting Games; but as the the practice exercise dovetailed nicely with a requirement I had, I thought I’d give it a shot.

It was quite different to write something “properly”, rather than just knocking something out that’d do the job. I’m not sure that’s exactly the way I would have done it without the supplied specification; I would have probably just returned total hours and total days, that would have made it easier to sort on a single column when output to an HTML table with formatted with the jQuery DataTables plugin (which is how I tend to format the output from most scripts). Also the exercise allowed you to assume that the server was online, whereas in the real world, I’d probably use a simple check to see whether the server was responding to Ping or not.

This is though, the first time that I’ve written a function which would accept pipeline input, and the first time I’ve use the ValidateNotNullOrEmpty parameter validation; as such it’s pushed me a little out of my comfort-zone, which is definitely a good thing.

Using the WebSphere MQ PowerShell library

WebSphereMQLogo

I’ve been doing some work with IBM WebSphere MQ recently. Specifically I’ve been helping to create a (temporary) reporting and monitoring tool which will periodically check channel status and depth of certain queues. While the application has it’s own command-line utility (MQSC) I’ve been trying to work using the WebSphere MQ Windows PowerShell library.

After downloading the file I followed the installation instructions, and immediately ran into the following error when trying to load the snap-in.

Add-PSSnapin : The Windows PowerShell snap-in 'IBM.PowerShell.WebSphereMQ' is not installed on this machine.
At line:1 char:13
+ Add-PSSnapin <<<<;  IBM.PowerShell.WebSphereMQ
    + CategoryInfo          : InvalidArgument: (IBM.PowerShell.WebSphereMQ:String) [Add-PSSnapin], PSArgumentException
    + FullyQualifiedErrorId : AddPSSnapInRead,Microsoft.PowerShell.Commands.AddPSSnapinCommand

It seems that this was because I was running it in the (default) 64-bit PowerShell session, rather than an x86 session. This means that if you’re running reports, you need to specify the x86 version of PowerShell (on my system this is in %SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe).

The CMDLets also require some server components to be installed (although it will not indicate that there’s anything wrong until running a command crashes your Powershell.exe session. I got it working with the options shown below (although I’m not entirely sure which were the critical components).

WebSphere MQ Setup Options

The documentation provided is pretty good, espcially if you’re not used to PowerShell. There’s a “cookbook” provided in the ZIP file, which is filled with examples. These do tend to focus on running commands locally on the server, if you’re running commands remotely, it takes a bit of work to string the necessary commands together to check a remote queue manager.

Get-WMQChannelStatus -Channel (`
      Get-WMQChannel -Name "NAME.OF.CHANNEL" -Qmgr (`
            Get-WMQQueueManager -Connections (`
                  New-WMQQmgrConnDef -Name "QMGR" -Hostname "SERVER" -Port "nnnn" -Channel "CHANNEL"`
            )
      )
)

I also noticed an issue with the Get-ChannelStatus CMDLet, where some channels return a valid status, others return nothing, but most would give me the following error:-

Get-WMQChannelStatus : String was not recognized as a valid DateTime.
At D:\WebSphereMQAppMonitor\WebSphereMQAppMonitor.ps1:134 char:41
+                     Get-WMQChannelStatus <<<<;  -Channel $channel
    + CategoryInfo          : NotSpecified: (:) [Get-WMQChannelStatus], FormatException
    + FullyQualifiedErrorId : System.FormatException,WebSphereMQ.GetWMQChannelStatus

I believe this is a culture/localisation issue. I tried a number of workarounds based on that assumption, but couldn’t get anything to work. I ended up using a horrible cludge; using PowerShell to invoke the standalone MQSC client, then parsing the results back into a PowerShell object.

We eventually managed to get something which would get the required data, and – as it’s PowerShell, it could be presented easily in a any of a number of formats.