Nuance Dragon – Dictaphone PowerMic II Suddenly Not Working in Windows 7

Introduction

I currently manage Dragon Medical 10 Network Edition in our VMWare View VDI for about 85 dictating physicians. In this deployment, we use a combination of the Dictaphone-branded and Nuance-branded PowerMic II devices. These have worked great from day one, and we haven’t had many issues aside from getting around the “Audio Levels Wizard” that pops up in certain situations.

Recently, we have been planning and testing an upgrade of the DMNE client to an updated service pack in preparation for the update to version 12. During the testing phase, however, we found that somewhere along the process, the PowerMic II devices began malfunctioning. The observed behavior was reproducible and seemed to affect a large majority of the devices that we have in the field, so a major issue to say the least.

After debugging and investigating the setupapi.dev.log on the machine, I was able to determine that there were changes to the native USB and USB Audio drivers on the system. In moving backwards, I found that some Microsoft Windows security updates have been released that update kernel drivers on the system in order to resolve a reported vulnerability. The negative result of these updates is that it essentially breaks a large majority of our current dictation microphones, the Dictaphone PowerMic II.

The Issue

The behavior of the issue is seen in two cases. Let’s consider the two following situations:

Situation 1

  1. PowerMic II devices are all pre-installed on the VDI base image
  2. A user plugs in a PowerMic II and the device is detected
  3. The device is unplugged and plugged back in, or a user roams
  4. The device may briefly be detected in Device Manager, but then disappears
    Or…
  5. The device may not be detected at all

Situation 2

  1. PowerMic II devices are not installed on the base image
  2. A user plugs in a PowerMic II, the device is detected and installed
  3. The device then immediately disappears from Device Manager after installing
    Or…
  4. After disconnecting and reconnecting the device or roaming, it disappears from the system

Resolution

The reason for this breaking is because of the following Microsoft security updates. Microsoft has released updated USB drivers which will cause the older PowerMic II devices to not work. These updates, and information about the patches, can be found in the following security bulletins:

  1. MS13-081
  2. MS13-101
  3. MS14-015

To avoid or fix this problem, either uninstall or hide the following updates from your Windows 7 client:

  1. KB2847311
  2. KB2855844
  3. KB2862330
  4. KB2862335
  5. KB2863725
  6. KB2864202
  7. KB2868038
  8. KB2884256
  9. KB2883150
  10. KB2876284
  11. KB2887069
  12. KB2930275

The affected drivers from these releases are found in the following locations:

USB Audio

%systemroot%\inf\wdma_usb.inf
%systemroot%\System32\DriverStore\FileRepository\wdma_usb.inf_x86_xxx_xxx

USB

%systemroot%\inf\input.inf
%systemroot%\System32\DriverStore\FileRepository\input.inf_x86_xxx_xxx

Summary

There may be additional changes in these patches, but it is a larger issue to decommission half of our devices at once with a single set of updates. Be sure to read through the security bulletins to decide what’s best for you.

Let me know if you have any questions or comments!

~ScriptdEEZ

Advertisements

Deploying App-V to VMWare View Floating Pools using SCCM 2012 R2

Custom App-V Applications Installed and Ready on a View Linked Clone Pool?

This week I successfully designed and deployed a method for on-the-fly delivery of applications to VMWare View floating VMs using Microsoft’s App-V and SCCM 2012 R2. I wanted to achieve this in order to leverage the administrative capabilities of App-V while allowing the user experience to be enhanced by the upgradeable and customizable nature of the platform.

My design involves using a System Center Configuration Manager 2012 R2 server to deliver App-V 5.0 applications to targeted VMWare View pool device collections. This is in order to stage cloned machines, on-the-fly, and with specific group-based applications rather than deploying forked clones or one monolithic clone to each pool. The architecture allows for floating clones to immediately request a compliant  state as soon as the VM is finished cloning, using some special scripting.

The end result is a fully presentable, floating VM that is prepped and ready with all assigned applications available to a user at login.

Preparation

I haven’t found very much information in regards to setting up SCCM 2012 to do what we’re talking about here, but luckily the system is extensible and can be customized with just about any query that you can come up with. The problem is that SCCM wasn’t really designed to do what I wanted it to do.

The polling intervals, even at their most frequent, were too slow to get a discovered machine into a collection and push out all of the published apps all before a user grabs the desktop. There is just no built-in way to initiate a client check or software push, you have to wait for your configured schedules to pass or the schedule on the client in order for a health check to occur and for missing applications to be sent. I never let things like that stop me, though. I pulled up my bootstraps and went digging through logs, WMI, and process traces. After exhausting my resources and subjecting myself to hours of pouring through the different CCM namespaces and instances in the client and server WMI, I finally found some methods and queries to help accomplish my goal.

Because there isn’t much information on what we’re about to do here, and because it’s a somewhat non-standard architecture, I recommend performing a little extra development and stress testing to get a sense of how your network will handle the application deployment process. I’ve tested this deployment in a pool of machines that clone 10 at a time without any reaching any sort of limit on our 10GbE back-end network.

In the end this deployment can be used for group-limited or specialized apps that you need to deliver to only one set of users or many. In addition to locally installed applications on the base image, such as MS Office, and even ThinApp delivered applications using ThinReg, we can create a very customized and extensible experience for our users.

SCCM Configuration

There are a lot of great guides online for setting up SCCM 2012 R2. One in particular is this multi-part series on the windows-noob.com forum. I highly recommend using this or another guide for installing and setting up the initial configurations for your SCCM 2012 R2 system. If you use the guide linked above, follow it until Part 3 and stop. We’ll be configuring our Discovery Methods and everything else from this point on. If you use another guide, just get to the point where you have your initial site configured and all of your base roles installed.

The primary focus of our SCCM configuration will be in the discovery, client polling settings, application deployment settings, collection queries, as well as many other areas. We’ll also be assigning some scripts to the server and master clone image as well as to VMWare’s QuickPrep post-sync script assignment.

The CCM client also has to be installed in a particular way on a VMWare View master image in order for it to properly register with SCCM.

The idea is to have three collections of computers, one for Active and one for Inactive, and one for Non-Client machines. The active collection should be targeted towards the View pool OU where you’d like to deliver the applications, while the other two will help in management and keeping our deployment clean. We’ll want polling for discovery of systems and members of collections to happen very frequently in order to discover new machines as they’re being cloned. We will then have to customize our base image with a scheduled task and script in order for the policy request to happen, immediately, without waiting for the polling interval.

If you have your SCCM 2012 R2 server in place and have not yet configured it with any discovery or boundary settings, then we’re ready to begin.

Step 1 – Configuring Application Web Services

From a standard deployment, without any further configuration past install and site setup, we’ll require these two additional roles:

  1. Application Catalog Web Service Point
  2. Application Catalog Web Site Point

To add these roles, navigate to Administration> Site Configuration> Servers and Site System Role, right-click on the server that you want as your distribution point and select Add Site System Roles.

Add site role

Click Next through the General and Proxy pages to accept the defaults or enter your preferred values.

At the Role Selection screen choose the Application Catalog Web Service Point and Application Catalog Web Site Point and click Next.

Role Selection

In this example we won’t be using a certificate, only the default HTTP on port 80, but if you’d like to add a certificate and configure HTTPS please do so now.

Leave the IIS website and Web application name as the default and click Next to continue.

Web Services Point

 

Once at the Application Catalog Customization screen select a custom title to display on your Application Catalog web page. The color you select here will set the theme of the Application Catalog website only, not the Software Center.

Catalog Customization

After setting the name, click Next to add the roles and Close to finish.

To verify successful web services installation, use CMTrace to check C:\Program Files\Microsoft Configuration Manager\Logs\awebsvcMSI.log. Search for the message “installation operation completed successfully“.

Install Success

To verify that application web services are running and online, check C:\Program Files\Microsoft Configuration Manager\Logs\awebsctl.log for the message “previous state was 0” which indicates an Online status.

Previous State was Online

Step 2 – Configuring Discovery

The Discovery Methods I’ve chosen are only Active Directory User Discovery and Heartbeat Discovery. Navigate to Administration> Hierarchy Configuration> Discovery Methods. 

We will be enabling only the following Discovery methods:

  1. Active Directory User Discovery
  2. Heartbeat Discovery

Discovery

WARNING: DO NOT ENABLE FOREST DISCOVERY

If you enable Active Directory Forest Discovery in hopes of automatically setting boundaries you might find that every object in your forest has been replicated to the SCCM server. This is unnecessary bloat that can be avoided by not enabling Forest Discovery.

Right-click Active Directory System Discovery and select Properties. Click the star icon to create a new Target OU and select the container holding the users for your View environment.

User Discovery

Click OK to save and close.

Right-click and configure the Heartbeat Discovery to collect client data every 1 hour.

Heartbeat

 

Step 3 – Configure Boundaries and Boundary Groups

To configure Boundaries and Boundary Groups without Forest Discovery turned on is no problem.

Navigate to Administration> Hierarchy Configuration> Boundaries. Right-click and select Create Boundary. In the Type dropdown select Active Directory site.

Click Browse to view your AD sites and choose the one for this boundary. Click OK to save and close. If you have multiple AD sites, repeat the process.

AD Site Boundary

Boundary Group

Once the Boundary is set up, right-click on the Boundary Groups node on the Navigation pane and select Create Boundary Group.

Give this a general name and add all of your targeted boundaries.

Boundary Group

Click OK to save and close.

We’ll be using this Boundary Group to associate with the Distribution Point that clients will be streaming the App-V packages from.

Step 4 – Configure Distribution Point

A default Distribution Point should have been installed with the initial site installation. If you want to configure a separate DP, add the new CM server, role, and configure it with the settings below.

Navigate to Administration> Site Configuration> Distribution Points and right-click the DP server. Select Properties.

We want to enable the following few settings:

  1. On the General tab check Enable and configure BranchCache for this distribution point.
  2. On the Boundary Groups tab add the boundary created in Step 3 above

Step 5 – Configuring Device Settings

In order to adjust some of our client polling schedule and agent settings, we must create a custom Device Setting.

To do so, Navigate to Administration> Client Settings. Right-click and select Create Custom Client Device Settings.

Create Client Settings

Give the new Device Setting template a descriptive name and check the boxes for:

  1. Client Policy
  2. Computer Agent Policy
  3. Software Deployment
  4. State Messaging

Device Settings

We’re setting high frequency polling intervals, against all recommendations, for the policy and client checks. This is in order to maintain communication so that we can more accurately age and decommission deleted linked clones.

On the Client Policy tab set the polling interval to 3 Minutes.

Client Polling

 

On Computer Agent, click Set Website and select your default catalog website that we configured in Step 1. 

Catalog Website

Still in Computer Agent, set an Organization name to be displayed in the Software Center, much like the “Organization Name” on the Application Catalog webpage.

Organization Name

Name displayed in Software Center

On the Software Deployment tab set the schedule to every 5 minutes.

Software Deployment

On the State Messaging tab, set the State message reporting cycle to 1 minute.

State Message

Click OK to save and continue.

Step 6 – Configuring Collections

Now that we have the Server Roles and Discovery squared away, we can focus on our Device Collections.

Collection Issues

I first wanted to talk a little about the issues I had to overcome in order to get my collections working how I wanted.

SCCM is a great tool because it allows for customizable queries based on pretty much any WQL command you can think of and has a number of built in queries available to use. You can also use Powershell, VBS, and check registry keys for queries. These are great features! However the built in aging and deletion policies just couldn’t keep up with the churn of a floating desktop pool where machines are constantly being created with the same name over and over again. The built-in tools just couldn’t handle it.

To resolve this I engineered a set of scripts and tasks to either automatically re-register an inactive device or remove it from one of the two inactive collections.

There are three sets of collections that we need in order to handle our immediate discover and app distribution. We also need scheduled scripts to poll the inactive collections to delete or register them in a controlled way.

Collection One – The Easy Collection (Healthy VMs)

The first device collection will be for our healthy VMs that were discovered. This is a very simple collection and only requires a couple of queries depending on how many OUs you are targeting. Ideally, we should only be targeting one View pool per DP and per Application Collection in order to maintain a manageable load.

To create the first Collection go to Assets and Compliance> Device Collections. Right-click and Create Device Collection.

Give the collection a name like “App-V Pool VMs” or something for the application group. Click Browse and select the All Systems device collection as the Limiting collection.

Device Collection

Click Next.

Click the Add Rule dropdown and select Query Rule.

In the next prompt give the rule a name identifying it as an OU rule and click Edit Query Statement…

Create Query

Click on the Criteria tab and then click the little star button to create a new criteria.

Criteria

 

Select System Resource as the Attribute Class and System OU Name for the Attribute.

Select is equal for the Operator and click the Value button to find and select your target OU.

OU Query

Click OK to continue and save. Repeat for any other target OUs in this deployment.

Once all of the queries are added check the box for Use incremental updates for this collection. 

Now click Schedule and set the Custom Interval for 1 minute.

Click Next to continue and create the collection.

Once the Device Collection has been created, navigate back to Administration> Client Settings and right-click the Custom Device Setting that you created in Step 5 and select Deploy.

Select your App-V Pool VMs and click OK to apply your device settings to the new device collection.

Deploy Client Settings

Collection Two – The Problem Child (Aged Device Records)

The second collection will be a collection of old VMs that have been replaced by new clones. We want our old device discoveries to be cleared out of our active collection in order to clear administrative clutter and and prevent possible issues.

This collection will be based on a custom query that I built after digging through the WMI to find a suitable value to represent an aged machine. I decided to use the Last Policy Request time because active clients maintain regular policy requests based on our configured polling schedule.

Create a new Device Collection and name it “Unresponsive Devices”. Select All Systems as the Limiting collection and click Next.

Create a new Query Rule like in the previous collection. Name the query “No Policy Check for 10 Minutes” and click Edit Query Statement…

On the General tab, click Show Query Language. Copy and paste the following custom query into the field and click OK to save.

Note: I’m a bit new to WQL queries, so if you have a suggestion on how to clean this up feel free to post a comment. This is not the most pretty statement I’ve ever written.

select SMS_G_System_CH_ClientSummary.LastPolicyRequest from SMS_R_System inner join SMS_G_System_CH_ClientSummary as s1 on s1.ResourceID = SMS_R_System.ResourceId inner join SMS_G_System_CH_ClientSummary on SMS_G_System_CH_ClientSummary.ResourceID = SMS_R_System.ResourceId WHERE dateDiff(mi, SMS_G_System_CH_ClientSummary.LastPolicyRequest, GetUTCDate()) > 10

Once you click OK you will be notified that the query has a syntax error. Just ignore this and click Yes to continue.
Syntax Error

Once the query is added check the box for Use incremental updates for this collection.

Click Schedule and set the Custom Interval for 2 minutes.

Click Next to continue and create the collection.

Add Collection Exclusion to Active Devices

Now we need to exclude the Unresponsive Devices collection from the App-V Pool VMs device collection.

To do so, right-click the App-V Pool VMs collection and select Properties.

Click the Membership Rules tab. Click the Add Rule dropdown and select Exclude Collections.

Check the box next to the Unresponsive Devices collection and click OK to save.

Select Unresponsive Collection

Unresponsive Devices

Collection 3 – Inactive Devices that need Re-register or Delete

In the third collection contains devices that I’ve identified as either needing to be re-registered or deleted.

The cloned machines all come up so fast that sometimes the old device entry is still in the collection and it causes some problem with the aging calculation. A machine like this will sit as “Inactive” in the Configuration Manager and never receive its deployed apps.

Once we put these machines into a collection,we can run a re-register commands to attempt and discover the machine. The method we’ll use has built in logic to only attempt to re-register a machine if it has not been re-registered in the last runtime cycle. This helps ensure fault-protection.

If the machines continue to appear here, we will have a scheduled task to remove them in the same way the unavailable machines will be removed. More on that below.

Create a new Device Collection and name it “Non-Client Devices“. Select the App-V Pool VMs device collection as the Limiting collection and click Next.

Create a new Query Rule. Name the query “Non-Client Devices” and click Edit Query Statement

Click on the Criteria tab and click the star icon to create a new criteria..

For the Criterion Type, select Null Value. Click Select and set the Attribute Class to System Resource and set the Attribute to Client Type.

Null Client Type

Click OK and then select is NULL for the Operator.

Null Client Criteria

Click OK to save this criteria and click the star icon again to create a second criteria for this rule.

For the Criterion Type, select Null Value. Click Select and set the Attribute Class to System Resource and set the Attribute to Client.

Null Client

Click OK and then select is NULL for the Operator.

Click OK to save this criteria. You should now see two Criteria linked with an And operator. This makes sure that both queries are true for this rule to take effect.

No Client Rules

Click OK to save this query.

Now add an exclusion rule to this collection to exclude the Unresponsive Devices collection.

Set the Schedule for this collection to every 3 minutes.

Non-Client Polling

Step 7 – Automatically Remove Unresponsive Devices or Re-Register Machines

We have our collections set up, the custom client settings applied, our boundaries configured, our Distribution Point in place, and our application web services ready and waiting. But before we move on to the client and application delivery, let’s take one last look at the SCCM server to do a little more for our cleanup routine.

We moved all of our Unresponsive Devices into a new collection that detects clients that haven’t checked in with the SCCM server for more than 15 minutes. SCCM has no way of automatically deleting or clearing a collections with an automated task, so I had to come up with a custom script to perform this as a Windows Scheduled Task.

Device Removal Tasks

The script is a PoSH script that queries WMI for all members of a specified collection and deletes them.

We first need to retrieve the Unresponsive Devices Collection ID for the script. To find this, right click the Unresponsive Devices collection and select Properties.

Collection ID
Record the Collection ID.

Now create a new PowerShell script under the folder C:\Scripts named removeUnresponsiveDevices.ps1.

Create the script with the following code. As always, code that needs to be modified is in Bold and Italics:


############################################################################
# Scriptname: removeUnresponsiveDevicesMembers.ps1
# Description: Remove aged SCCM devices
# By: Adam Baldwin
#
# Usage: The script checks the Unresponsive device collection in
# SCCM and removes all members.
# Set as a scheduled task on the SCCM server
#
############################################################################

# Set site name and SCCM Server name

$SCCMServer = "YOURSERVERNAME"
$sitename = "YOURSITENAME"
$collID = "YOURCOLLECTIONID"

# Specify the Collection to target

$compObject = get-wmiobject -namespace "root\sms\site_$sitename" -query "select * from SMS_FullCollectionMembership WHERE SMS_FullCollectionMembership.CollectionID='$collID'" -computername $SCCMServer

# Delete each member of the Collection

foreach ($member in $compObject) {
	$resourceid = $member.ResourceID.ToString()
	
	If ($resourceid -eq $null) {
		Continue
	}
	Else {
		# Invoke delete() method on WMI instance sms_r_system
        
        $comp = [wmi]"\\$SCCMServer\root\sms\site_$($sitename):sms_r_system.resourceID=$($resourceid)"
		$comp.psbase.delete()
	}

}

Once this is ready and saved create a new scheduled task to run daily and repeat every 5 minutes, indefinitely.

5 minutes Delete

For the Action, select Start a program. Type in “powershell.exefor the Program/Script. Add the following Arguments:

-command "& c:\scripts\removeUnresponsiveDevices.ps1"

Action

The task should be set to run with an account that is local admin and is allowed to log on as service. Check the radio button to Run whether user is logged on or not and Run with highest privileges.

Run w privileges

 Non-Client Device Removal

Retrieve the Device Collection ID of the Non-Client Devices collection and create a second script called removeNonClientDevices.ps1. The script should be exactly the same as the one above except with the Non-Client Devices ID.

Create a Second Scheduled Task and set this one to run every 10 minutes to remove Non-Client Devices.

Remember to set the user account to run when not logged on and use the service account that we assigned in the previous task.

Re-Registration Task

Now we need to create a re-registration script and task to attempt to bring online Inactive devices.

Another PoSH script needs to be created and set to another scheduled task. You’ll need to fill in Both of the collection IDs for your Active App-V Pool VMs collection and for the Non-Client Devices collection.

You’ll also need a copy of Psexec for this to run, so pick it up in the Sysinternals suite and put it in your local scripts folder on the server where the task is set.

Here’s the script:

############################################################################
# Scriptname: registerDevices.ps1
# Description: Re-registers inactive SCCM devices
# By: Adam Baldwin
#
# Usage: The script checks the Non-client device collection in
# SCCM and re-registers them once per two rounds of execution.
# Set as a scheduled task on the SCCM server
#
############################################################################


# Creates registry keys if they do not already exist
# The key contains previously registered machines in the 
# Last iteration of the script 

if (!(Test-Path -Path "hklm:software\SCCM\register")) {
 new-item -path "hklm:software\SCCM"
 new-item -path "hklm:software\SCCM\register"
 New-ItemProperty "hklm:\software\SCCM\register" -Name "registered" -Value "" -PropertyType "String"
}



# Set site name and SCCM Server name

$SCCMServer = "YOURSCMSERVER"
$sitename = "YOURSITENAME"
$inactiveCol = "INACTIVEDEVICECOLLECTION-ID"
$activeCol = "ACTIVEDEVICECOLLECTION-ID"

# Assign the clients from the Active and Inactive device collections

$inactiveComps = get-wmiobject -namespace "root\sms\site_$sitename" -query "select * from SMS_FullCollectionMembership WHERE SMS_FullCollectionMembership.CollectionID='$inactiveCol'" -computername $SCCMServer
$activeComps = get-wmiobject -namespace "root\sms\site_$sitename" -query "select * from SMS_FullCollectionMembership WHERE SMS_FullCollectionMembership.CollectionID='$activeCol'" -computername $SCCMServer

$registered = ""

# Delete each member of the Collection

foreach ($inactive in $inactiveComps) {
 
   # Convert the member name to a string
   $inactiveName = $inactive.Name.ToString()
 
   # If active comps does not contain the inactive device
   # then perform the remote command


   # Collects the previously registered machines from registry
   # and splits it into an array
   $test = (Get-ItemProperty "hklm:software\SCCM\register").registered
   $test = $test -split ","

   # Check to see if the test array contains the current name
   # if so just run an empty statmenet. Else run the remote command
   if ($test -contains $inactiveName){}
   Else {
      # Use PSExec to run a re-register script on remote machines
      e:\scripts\psexec.exe /accepteula \\$inactiveName /d /s powershell.exe " \\contoso.local\netlogon\scripts\re-registerccm.ps1"
 
      # Record which devices have been registered this roud
      $registered += $inactiveName + ","
      $registered = $registered -Replace " ", ""
   } 


}

# Sets the registered devices from this round into the registry
Set-ItemProperty "hklm:software\SCCM\register" -Name registered -Value $registered

Make sure the account is able to write and read to the registry where we’re recording our hits. That key is HKLM\Software\SCCM\register.

Name the script registerDevices.ps1 and set the scheduled task to run every 5 minutes. Use the same service account that you used in the previous tasks.

Client Portion of the Re-Register

The above script references a PoSH script on the domain’s netlogon volume. It also requires Certmgr.exe in order to delete and re-register the certificates for the CCM agent. I’ve uploaded a x86 Visual Studio 2010 copy of certmgr.exe here. Save certmgr.exe it to the netlogon volume where your PoSH are sitting.

Place a new .ps1 there named “re-registerCCM.ps1” and use the following code:

############################################################################
# Scriptname: re-registerCCM.ps1
# Description: Resets the SCCM registration for the device
# 
# By: Adam Baldwin
#
# Usage: Place this script onto an accessible share and initiate 
# with the SCCM scheduled task that runs psexec against
# all inactive machines with this code
#
############################################################################

# Stop SMS AGEnt service
stop-service ccmexec

# Delete the SMS config
del c:\windows\smscfg.ini

# Reference shared volume for certmgr.exe
# to remove SMS certificates
\\contoso.local\netlogon\scripts\certmgr.exe -del -all -s -r localMachine "SMS"

# Starts the SMS Agent Service
start-service ccmexec

# Pause for 5 sec
sleep 5

# Run CCM Evaluation executeable
C:\Windows\CCM\CCMEval.exe

# Run local scheduled task to check-in with SCCM
SCHTASKS /Run /TN "Request Client Policy"

A Special Note about SCCM Service Account and Script Tasks

The service account used to drive these tasks on the SCCM Server should be assigned the Full Administrator role in SCCM and have had the PowerShell terminal launched from inside of SCCM.

*THIS IS IMPORTANT*

In order for the script to run with the specified account on the server, Powershell needs to be launched from the Configuration Manager while logged in with said account. Either log into windows with the account or right-click the Configuration Manager and run as different user.

Log into the Configuration Manager with the account that will be driving this scheduled task and click the Menu dropdown in the top left of the application. Select Connect via Windows PowerShell.

Connet via Powershell

This will enable your account to connect to and manage the SCCM namespace.

VMWare View Master Image Configuration

Now that the server components are all in place and ready we need to configure our master image to work with SCCM 2012 R2.

As part of the setup we need to configure the CCM agent to be available in a clean state until first run. We’re also going to implement a Computer Startup scheduled task that will initiate an immediate client policy health check, which in turn immediate requests missing software packages to be installed.

Part 1 – Configuring Master Image Check-in Task

To save time and avoid complication, we’re going to set the script and scheduled task first. This way we don’t have to repeat Part 2 of this process over again.

NOTE: The master image needs to be joined to the domain in this step and a service account needs to be configured to run scheduled tasks on the VM

Start up and log into your master clone image. Join it to the domain.

In order for our script to work we need to set the ExecutionPolicy to either Bypass, if you don’t plan to sign your script, or RemoteSigned if you plan to sign it. To set the ExecutionPolicy for the local machine, run PowerShell as an Administrator and type in following command:

Set-ExecutionPolicy -Scope MachinePolicy Bypass

This can also be applied in GPO under Computer Configuration> Policies> Administrative Templates> Windows Components> Windows Powershell. The setting is Turn on Script Execution. Set to Allow all scripts or Allow local scripts and remote signed scripts respectively.

Execution GPO

Now that ExecutionPolicy is set, create a new script PowerShell in C:\Scripts named requestMachinePolicy.ps1. 

The script should contain the following:

C:\Windows\CCM\CCMEval.exe

Invoke-WMIMethod -Namespace "root/ccm" -ComputerName "." -Class SMS_Client -EnableAllPrivileges -Name RequestMachinePolicy

This script immediately starts up the SMS Agent service and then performs a WMI method invocation that performs the Client Health and Policy check. This command will initiate communication back to the server and trigger software installation.

Now that the script is saved and ready we need to implement a Scheduled Task that runs at machine power on. Create a new Basic Task and name it Request Client Policy. Set the Trigger to run One Time. 

Since we’ll be running this on-demand, just set it to some time in the past and click Next.

One time task

 

For the Action, select Start a program and type “Powershell.exe” into the Program/script field. Add the following argument:

-command "& c:\scripts\requestMachinePolicy.ps1"

Check the box to bring up the task Properties once it’s been created.

For the User Account, use the service account that you created that has been set to allow Run as Service and is a local administrator on the VM. Check the radio button for Run whether user is logged on or not and Run with highest priveleges. Click OK and type in your Service Account credentials to save the task.

Policy Request Task AccountPolicy Request Task Account - Authenticate

Click on the Settings tab. Make sure that the box is checked to Allow task to be run on demand. I also set the retry to every 5 minutes.

Run on demand

Now that our scheduled task and client-side script is in place, Remove the VM from the Domain and continue along with Part 2 of the Client Setup.

Part 2 – Configuring CCM Agent for VMWare Master Image

Thanks to this article by Henk Hoogendoorn on his blog I was able to correctly configure the CCM client on a VMWare master image to show up properly in the management console. This was a big step forward in this project and the task is very straightforward.

The simple procedure is as follows:

  1. Install the CCM client on your master image with all of your site parameters
  2. Stop the SMS Agent service in the Services console or from command line
    (command line: net stop ccmexec)
  3. Delete %systemroot%\SMSCFG.ini
  4. Open Computer Account Certificates console. Start> Run> type “MMC” and press Enter.
  5. Click File> Add/Remove Snap-in and select Certificates. Choose Computer Account and click OK to continue.
  6. Navigate to SMS> Certificates and delete both SMS certificates.
  7. Immediately Shutdown and take a Snapshot

SMS Certs

This will make our master image so that it creates fresh identifiers in its CCM config for the server to identify it as a new machine.

Part 3 – Configure VMWare Script Timeout

In order for the next script to run for the amount of time we need it to we have to increase our master image’s quickprep script timeout in the registry. See this VMWare publication for more info.

We’ll be modifying a key in the registry so go to Start> Run – regedit.exe

Navigate to the key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\vmware-viewcomposer-ga

Change the DWORD value of ExecScriptTimeout to 900000

ExecScriptTimeout

This gives us a total script runtime duration of 15 minutes, even though we’ll only be allowing up to 13 minutes of runtime at first.

Part 4 – VMWare View Pool Configuration

The final piece of the puzzle is configuring the VMWare View pool to allow the newly cloned VMs a certain amount of time in order for the applications to download and install.

I’ve accomplished this task by using the QuickPrep post-synchronization script which allows execution of a remote script on a new clone immediately following customization.

In my script I’ve included a method for preventing the new clone from becoming available by simply disabling the View Agent service for a period of time before re-enabling it. I used sleep in order to pause the execution of lines and include a call to the local scheduled task to ensure Client Health Check has initiated and passed.

Once the script finds that the folder C:\ProgramData\App-V exists it knows that the software has been installed and it then stops requesting health checks. This occurs every 1 minute for a maximum of 13 minutes.

Put this .ps1 script on the Domain’s netlogon volume for the client to access:




############################################################################
# Scriptname: ccmstart.ps1
# Description: Start SMS Agent and initiate client check-in
# procedure to begin installing App-V packages
# By: Adam Baldwin
#
# Usage: Set as the VMWare View QuickPrep Post-syncrhonization script
# for your App-V deployment pool
#
############################################################################


# Stop and disable View Agent Service
stop-service WSNM
set-service -name WSNM -StartupType Disabled

# Start SMS Agent service and execute CCMEval.exe
start-service ccmexec
C:\Windows\CCM\CCMEval.exe

# Set loop counter
$count = 0
$ding = 0
# Loop the check-in task every 1 minute while the App-V folder
# does not exist or for 6 minutes

while (((Test-path C:\ProgramData\App-V) -ne $True) -or ($count -lt 5)) {

   # Initiate the local task on the VM to perform
   # client check-in
   SCHTASKS /Run /TN "Request Client Policy"

   sleep 60

   $count++

   # If the count reaches 13 (minutes) or more, start the View service
   # and Exit the routine
   if (($count -eq 13) -or ($count -gt 13)) {
 
      Write-Host "Reached 10 minutes. Starting View Services..."

      set-service -name WSNM -StartupType Automatic
      start-service WSNM
      Exit
   }

   # If the count reaches 8 (minutes), initiate re-register
   if ($count -eq 8) {
 
      Write-Host "Reached 8 minutes. Initiating re-registration..."
 
      # Stop SMS AGEnt service
      stop-service ccmexec

      # Delete the SMS config
      del c:\windows\smscfg.ini

      # Reference shared volume for certmgr.exe
      # to remove SMS certificates
      \\contoso.local\netlogon\scripts\certmgr.exe -del -all -s -r localMachine "SMS"

      # Starts the SMS Agent Service
      start-service ccmexec

      # Pause for 5 sec
      sleep 5

      # Run CCM Evaluation executeable
      C:\Windows\CCM\CCMEval.exe

      # Run local scheduled task to check-in with SCCM
      SCHTASKS /Run /TN "Request Client Policy"

      Write-Host "Re-registration Complete"
   }

   # If the App-V folder is detected, write output
   if (((Test-path c:\ProgramData\App-V) -eq $True) -and ($ding -ne 1)) {
   
      Write-Host "App-V Folder Detected"
      $ding = 1
   }
}

Write-Host "Script Fully Completed"

# Enable and start the View Agent service
set-service -name WSNM -StartupType Automatic

start-service WSNM
 

 

Save this file as CCMSTART.PS1 in a location that is accessible to all the VMs in the pool, again recommended to be your netlogon volume. Once the script is saved and ready configure your VMWare View Floating Pool to use a QuickPrep post-synchronization script.

Here are the instructions:

  1. Open View Administrator
  2. Navigate to Inventory> Pools
  3. Right-click your pool and select Edit (or wait until the Guest Customization screen if creating a new pool)
  4. Open the Guest Customizations tab
  5. Next to Post-synchronization script name, type “C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe”
  6. Next to Post-synchronization script parameters, type “-command & \\contso.local\netlogon\scripts\CCMSTART.ps1″ (Path to your CCMSTART.ps1 script)
  7. Click OK to save and close

Guest Customization

That’s it for the Client Setup!

 

SCCM App-V – Application Deployment Process

Now we need to move onto the process of distributing the App-V Applications. There are just a few specifications that we need to verify when publishing new App-V apps so be sure to review all areas closely.

Part 1 – Configure the Application

In the SCCM 2012 R2 console navigate to Software Library> Application Management. In this tree, right-click Applications and select Create Application.

Select Microsoft Application Virtualization 5 in the Type dropdown and Browse to your App-V application on the datastore.

Create Application

Click Next to continue and create the application.

Now that the application is added, we have to configure its distribution and deployment settings. To do so, right-click on the App-V program in the Applications window and select Properties.

On the General Information tab, check the box for Allow this application to be installed from the Install Application task sequence action without being deployed.

App Properties - General

On the Distribution Settings tab, check the radio button for Automatically download content when packages are assigned to distribution points and set the Distribution Priority to High.

Distribute Content

On the Deployment Types tab, select the Deployment type that is present and click Edit.

Deployment Type Edit

Click on the Content tab and make sure that the box for Enable peer-to-peer content distribution is checked. Click the dropdown for both Deployment options fields and select Stream content from distribution point. 

Deployment Type Properties

Click OK to save and close this window.

Now click OK to close and save the Properties window.

Part 2 – Deploy and Distribute the Application

Now that all the properties are set on the application, we need to distribute the content.

Still in the Applications window, right-click on the newly created application and select “Distribute Content“. Once you reach the Content Destination page, click Add and select Distribution Point.

Distribute

Select the Distribution Point we created earlier and click OK to save.

Select DP

 

Click Next to continue and distribute the content.

Now we need to Deploy the content to our VM device collection.

Right-click the Application and select Deploy. On the first page click Browse next to Collection to bring up the Select Collection window. Click the dropdown in the top left where it says User Collections and select Device Collections.

Select Collection

Select your App-V Pool VMs (or similarly named) device collection and click OK to continue.

Select Healthy Device Collection

Click Next. We’ve already distributed the content so you should be able to click Next through the Content page.

When you arrive at the Deployment Settings page, we need to change the Purpose field to have Required instead of Available. Click the dropdown and select Required. Click Next to continue.

Required

 

On the Scheduling page, keep the defaults with the radio button As soon as possible after the available time checked. click Next.

Scheduling

On the User Experience page, select your preferred User notifications option and check the box for Software Installation. This ensures that the software can be pushed to the desktops without intervention or waiting for a maintenance window.

User Experience

Click Next to continue through the remaining sections and deploy the software to your device collection.

Completion and Testing

Congratulations! You’ve just implemented a staging solution for App-V applications for VMWare View Floating VMs, which is no easy task!

Our implementation will cause a delay on the availability of newly cloned machines, but once they become ready the user should have access to all of the provisioned applications at login.

To test simply delete all available machines from your App-V View pool and delete all discovered devices in the SCCM 2012 R2 management console.

Once the new VMs have cloned they should sit in an Agent Unreachable state for the specified period of time. If they come up immediately, without a pause, then the post-synchronization script didn’t run or something went wrong.

Monitor the SCCM to make sure that new devices are being discovered and added to the App-V Pool VMs device collection. The devices must be discovered and available in the proper device collection in order for the correct policies and applications to be assigned.

A good way to ensure that your devices are checking in and communicating is to look at the timestamp of the Policy Request for a client. If the timestamp is within the last 10 minutes (according to our query rule) then the client is most likely active. A healthy client should be checking in every 5 minutes with our configuration.

Policy Request

 

Once machines are Available in your View pool, log into the pool to make sure you have your application load. If not, there are a couple of places to check for logs and see if the client ran all of the proper scripts and requests.

Logs

To check the VMWare QuickPrep logs, navigate to C:\Windows\Temp on the client machine and find the vmware-viewcomposer-ga-new.log. 

Search this file for the word “Post” and you should find the post-synchronization script results. All of the output from the script, including any errors returned from the PoSH script, are displayed here. You should see a similar output for a machine in the App-V pool. If the script completes successfully you will see “Script Fully Completed” at the end of the Post-script logging message, otherwise the machine will simply be enabled in View with or without applications:

2014-04-27 18:58:04,328 [336] INFO  Ready  -  [Ready.cpp, 128] Running the PostSync script: "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -command & \\dcprod1\netlogon\scripts\ccmstart.ps1 with timeout: 900000
2014-04-27 19:07:31,218 [336] INFO  Guest  -  [Guest.cpp, 509] Script "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -command & \\dcprod1\netlogon\scripts\ccmstart.ps1 standard ouput: WARNING: Waiting for service 'VMware View Agent (WSNM)' to stop...
SUCCESS: Attempted to run the scheduled task "Request Client Policy".

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

Reached 8 minutes. Initiating re-registration...
CertMgr Succeeded

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

Re-registration Complete
INFO: scheduled task "Request Client Policy" is currently running.

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

SUCCESS: Attempted to run the scheduled task "Request Client Policy".

Script Fully Completed

To verify the scheduled task ran successfully, check the task in Task Scheduler for the Last Run Time and the Last Run Results.

If you would like to investigate the CCM logs, navigate to C:\Windows\CCM\logs and use CMTrace.exe to check through the following log files:

  1. AppDiscovery.log
  2. CAS.log
  3. CCMEval.log

You should find recent messages about successfully running checks and retrievals of application packages.

Increase Timeout

If you find that VMs are coming available after the timeout but still don’t have the requested applications installed, increase the timeout on the CCMSTART.PS1 file that we set in the Post-synchronization scripts field.

The default is to allow for 13 minutes until forcing a VM online, whether apps are available or not. Increase this timeout to give the machines more time to install the application load. Look for the line of code where $count -eq 13 and $count -gt 13 and set the values to 15 minutes before forcing the VM to an available state:

# If the count reaches 15 (minutes) or more, start the View service
# and Exit the routine
if (($count -eq 15) -or ($count -gt 15)) {
 

Also monitor the App-V Pool VMs device collection during the cloning phases to see how your devices are showing up and if they become Active. You may see duplicates and the old machines will be moved to the Unresponsive Devices collection, this is normal. Verify the timestamps on the Policy Request for a client in order to verify that the new machine is active in the correct device collection.

Manually Initiating Client Policy Request

In order to manually initiate a client policy request to check for new software, you can run the PowerShell script that we created on the local VM or run the scheduled task that we created. Either one will perform the Client Health Policy check-in to request missing software and compliance states. If you’d like to just run the PowerShell command that initiates this client check, simply use the following:

Invoke-WMIMethod -Namespace "root/ccm" -ComputerName "." -Class SMS_Client -EnableAllPrivileges -Name RequestMachinePolicy

Summary

That’s it! Some of the components of this deployment aren’t necessarily the most pretty, but they’re functional. If you’d like to hang on to your VMWare View linked-clone pools while still using an App-V solution then this is the design for you.

We delayed the availability of our VMs a bit but the resulting effect is a fully staged VM with App-V applications delivered and ready. I feel that this App-V deployment, in conjunction with both locally installed and other virtualized applications, can deliver the benefit of increased flexibility to the user experience. We hope to cut down the time it takes to make changes or upgrade applications in our environment, to the point where testing and configuration can occur in the same cycle and be released much sooner than the cycle of image change requests that we currently use.

I’ve considered testing full download and deployment rather than streaming for some of the Applications, because of the delay when first launching, so stay tuned for any new advice or tricks that I might come up with.

Please let me know if you have any trouble, questions, or suggestions. This is still a work in progress that I hope to improve upon as time goes by!

Happy Scripting!
~ScriptdEEZ

 

 

VMWare View & Teradici PCoIP – USB Microphone Issues

Introduction

This is a brief post about issues that I recently experienced while trying to set up a new USB microphone in our View 5.2 environment to be used with Nuance Dragon Medical 10.1. All of our users use the Samsung NC241 & NC242 zero clients to connect to their View desktop, and the zero clients use Teradici chips for PCoIP communication.

Currently, our Dragon users mainly utilize the Nuance PowerMic II for dictation. This device works really well with View and Dragon, and is the preferred device by Nuance. However, some users are unable to use the device because of physical limitations and therefor I was tasked to find an alternate solution for such circumstances. In comes the Buddy DesktopMic 7G. I used a chart comparison of different microphones, found Here, to finally decide on the Buddy microphone since it has high quality audio throughput and is mounted on a desktop stand which requires very little physical interaction.

The Issue

After having received the device, I was quick to start testing. However I immediately encountered an issue with my Windows 7 guest VM recognizing and installing the device. While the device was connected via USB to the Samsung zero client, it was not showing up in Device Manager in Windows. This was the case in both Win7 and XP. Bummer.

I started digging around, looking for where the disconnect had occurred. I logged into the Teradici PCoIP management interface on the zero client and found that the device was indeed connected and being identified properly. So why no connection in Windows?

Connected devices in zero client's Teradici interface

Connected devices in zero client’s Teradici interface

I attempted to uninstall/reinstall all of the View and VMWare components (ie View Agent, VMWare tools, Feature Pack 2) but with no success. The device just wouldn’t show up in Windows at all. I plugged the mic into a physical workstation and it came up just fine, so I knew it wasn’t an issue with drivers or the device. This was perplexing to say the  least.

Resolution

After exhausting my options, and not coming up with anything on Google, I decided to dig through the PCoIP settings a little more. I went through each section of the configuration and found a particularly interesting, and seemingly relevant, area. The Permissions tab in the console had a page for USB. I figured I’d take a look here since there were no other leads.

USB Permissions page on the PCoIP management screen

USB Permissions page on the PCoIP management screen

Everything looked good from what I could see. No unauthorized devices were listed, and the Any Device Class was set in authorized devices. There was one thing here that caught my eye, though, and so I figured I would give it a shot. That is when I decided to add my device to the Bridged Devices option. Lo and behold, once this was set with my microphone’s VID and PID, we were in business.

Here’s how I found the VID and PID of the microphone and added it to the Bridged Devices setting under Permissions>USB in the PCoIP management console.

  1. Log into the zero client using the IP address
  2. Highlight the Info tab and select Attached Devices from the dropdown

    PCoIP management console attached devices

    PCoIP management console attached devices

  3. Find your device by the Model name and record the VID and PID values
  4. Highlight the Permissions tab and select USB from the dropdown
  5. Enter the values recorded in step 3 and click Apply

    Adding a Bridged Device

    Adding a Bridged Device

  6. When prompted, reset the PCoIP processor

Once this is complete, the device should now show up within the Windows guest operating system on the VM.

Summary

This is the only device with which I’ve had to do this. According to this Teradici Article, device bridging is used in order to terminate HID device connections directly to the VM rather than at the zero client level. I assume if anyone is having trouble getting a USB device to register on a Windows VM that they might try configuring their zero client to use device bridging. It certainly helped in this situation!

Cheers~
ScriptDEEZ

 

If you found this article helpful or interesting, please donate a tip!
(LTC): LYs82mDXPohGDCzeRct8HkJwUCfwveGH1t
(BTC): 19JUVeefSBNbxUZNNmbDqfcctU6im6vsNg

 

 

Duplicate View Sessions

Introduction

This week we’re going to be looking at a simple export of current VMWare View sessions into a SQL table and the following SQL code to identify multiple single-user logins.

Many times we find that users are being given multiple sessions, whether due to issues on the previously connected VM, preventing full log off, or because of load balancing issues. We find that users who have multiple sessions eventually begin noticing unusual behavior such as Persona Management profiles not updating or account lockouts occurring after a password change.

We’ll be using a VMWare PowerCLI cmdlet to pull the session data, a mechanism that works relatively quickly and shows us exact associations of VMs and users.

The Situation

At OMC we run two distinct View 5.2 deployments. We’ve made it so that all of the pools and entitlement groups that we use are on each system so that regardless of where a user logs in they will still be able to connect. Each “side” of our View architecture has two Connection servers, and one from each side is presented to the users by an F5 load-balancer.

The load-balancer uses Active Directory group membership to decide whether a user goes to View Deployment “Side-A” or View Deployment “Side-B”. This works well a large majority of the time, but it’s possible that users can bypass the F5 and use FQDN to go to any of the connection servers from either deployment directly. In this situation, a user might have already been logged in on one View system and thus could have created a duplicate session on the “other side”.

Normally this shouldn’t be a problem, but if a user’s password is reset while they have a machine still logged in and that machine never gets logged out there will be eventual issues with the account locking.

In addition, we find that multiple user sessions show up on each deployment individual. I suspect the cause of this is some error that is preventing the VM from logging off, and so it sits in a disconnected state yet still allows the user to get a new desktop. This script will also identify these types of sessions.

The Script

In my previous write-ups I queried View’s ADAM database to analyze the View data. This time I just used the get-remotesession cmdlet which gives a list of logged in users and the associated VM names.

Preperation

To start, configure an MS SQL server and create a new database for the tables to be written to. Name the database ViewDuplicates.

The account used to drive the script requires permission on the database and also must be an administrator on the View Connection server(s). The script should be configured to run locally on both environments’ connection servers if you have two independent View deployments.

In the example below we’re comparing two distinct sets of sessions from independent environments. If you have only one environment you won’t require the Second View Deployment Server script or the Cross Duplicate SQL View code mentioned below. Just use one PoSH script and one SQL script to find duplicate sessions within a single View Deployment.

I have highlighted the portions that need to be reviewed in bold and italic. Please check and update where necessary.

PoSH Scripts

First View Deployment – Connection Server Script

############################################################################
# Scriptname:     Pool_Duplicates.ps1
# Description:     Export View sessions to SQL table
# By:            Adam Baldwin
#
# Usage: Will parse and export all View sessions to a SQL table 
# 
############################################################################

Import-Module -Name 'C:\Program Files\VMware\VMware View\Server\extras\PowerShell\add-snapin.ps1'
$Sessions = get-remotesession
$domain = "YOURDOMAIN.local\\"

$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Data Source=SQLSERVER;Initial Catalog=ViewDuplicates;Integrated Security=SSPI;"
$conn.open()
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.connection = $conn

$cmd.commandtext = "DELETE FROM ViewServer1"
$cmd.executenonquery()

foreach ($Session in $Sessions)
{
    $state = $Session.state
    $pool = $Session.pool_id
    $username = $Session.username -replace ($domain, "")

    $conn = New-Object System.Data.SqlClient.SqlConnection
    $conn.ConnectionString = "Data Source=SQLSERVER;Initial Catalog=ViewDuplicates;Integrated Security=SSPI;"

    $conn.open()
    $cmd = New-Object System.Data.SqlClient.SqlCommand
    $cmd.connection = $conn
    $cmd.commandtext = "INSERT INTO ViewServer1 (username,constate,pool) VALUES('$username','$state','$pool')"
    $cmd.executenonquery()
    $conn.close()

}

Second View Deployment – Connection Server Script


############################################################################
# Scriptname:     Pool_Duplicates.ps1
# Description:     Export View sessions to SQL table
# By:            Adam Baldwin
#
# Usage: Will parse and export all View sessions to a SQL table 
# 
############################################################################

Import-Module -Name 'C:\Program Files\VMware\VMware View\Server\extras\PowerShell\add-snapin.ps1'
$Sessions = get-remotesession
$domain = "YOURDOMAIN.local\\"

$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Data Source=SQLSERVER;Initial Catalog=ViewDuplicates;Integrated Security=SSPI;"
$conn.open()
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.connection = $conn

$cmd.commandtext = "DELETE FROM ViewServer2"
$cmd.executenonquery()

foreach ($Session in $Sessions)
{
    $state = $Session.state
    $pool = $Session.pool_id
    $username = $Session.username -replace ($domain, "")

    $conn = New-Object System.Data.SqlClient.SqlConnection
    $conn.ConnectionString = "Data Source=SQLSERVER;Initial Catalog=ViewDuplicates;Integrated Security=SSPI;"

    $conn.open()
    $cmd = New-Object System.Data.SqlClient.SqlCommand
    $cmd.connection = $conn
    $cmd.commandtext = "INSERT INTO ViewServer2 (username,constate,pool) VALUES('$username','$state','$pool')"
    $cmd.executenonquery()
    $conn.close()

}

Once you’ve confirmed that the data is being dumped into SQL (this will include username, pool, and state), we will need to create three new SQL Views named View-Env1-Dups, View-Env2-Dups, and View-Cross-Dups. (If you only have one deployment just create the first SQL View).

SQL Code

The first two SQL View codes compare each individual connection environment’s table. The third SQL View is used to compare the tables from both connection environments for “cross-duplicates”.

Note: If you have only one VMWare View Deployment then you’ll just need to use the first SQL View below to check for duplicates in a single table. Ignore the Cross Duplicate SQL View.

Attention to items in bold and italic. Check and update where necessary.

SQL Views

First View Deployment – SQL View Code

 SELECT username, pool, COUNT(*) AS DupCount
FROM dbo.ViewServer1
GROUP BY username, pool
HAVING (COUNT(*) > 1)

Second View Deployment – SQL View Code

 SELECT username, pool, COUNT(*) AS DupCount
FROM dbo.ViewServer2
GROUP BY username, pool
HAVING (COUNT(*) > 1)

Cross Duplicates – SQL View Code

SELECT DISTINCT dbo.ViewServer1.Username, dbo.ViewServer1.Pool

FROM dbo.ViewServer1 INNER JOIN
dbo.ViewServer2 ON dbo.ViewServer1.Username = dbo.ViewServer2.username AND dbo.ViewServer1.Pool = dbo.ViewServer2.pool

These Views compare data between all of the tables to output sessions that match on both Username and Pool ID. The SQL View can then be presented as a DataSource for creating tables or web forms for intranet content.

I decided to use a GridView in VisualStudio to display the info in a table, allowing desktop support and other technical staff to monitor which users have duplicate sessions in which pools.

Summary

This might seem confusing since we have a unique setup in having two mirrored View environments and a custom load balancing solution, but the concept and script can still be applied to just a single environment, or possibly many more.

Since the data is presented as a SQL View, it can be delivered to tech staff in any number of ways. Through a dashboard, a website, or more. It’s up to you.

Please let me know if you have any questions, comments, or trouble figuring it out below.

Cheers!
-ScriptdEEZ

If you found this article helpful or interesting, please donate a tip!
(LTC): LYs82mDXPohGDCzeRct8HkJwUCfwveGH1t
(BTC): 19JUVeefSBNbxUZNNmbDqfcctU6im6vsNg

VMWare View – Status Email Alert for Non-Provisioning Pools

Introduction

In this article we’ll be exploring a method for creating an email reporting tool for pools that have entered a non-provisioning state. It can be troublesome to have pools go down and not know until users start reporting they can’t connect. By that time you’re already entering production issues and are pressed to find a quick resolution to start cloning more machines.

Luckily, ADAM comes to the rescue again. Since all of the attributes associated with View and View objects are stored in our ADAM database, we can simply set a script to poll for disabled pools and report any non-provisioning states.

This is tested and working in View 5.xx

The Script

This script is built off previous methods that I used to query the ADAM database for attribute sets and objects. Particularly, we will be querying pae-serverpool, pae-vmprovenabled, and pae-vmproverror. These attributes tell us whether a pool (pae-serverpool objectCategory) is in a non-provisioning state (pae-provenabled = 0) and the error associated with provisioning (pae-vmproverror).

Similar to my last script, we are using a few hash tables to build lists of Pools, concurrent with their provisioning statuses and error messages. We are also querying and building registry entries in HKLM:\software\VMWare, Inc.\ under our own custom key. This will ensure that if we receive an alert for a non-provisioning pool, we will not receive the same alert over and over again.

Please make sure that the account that will drive the script is a Local Administrator on your VMWare View server and set with the Administrator role in the View console. This can be found at View Configuration> Administrators.

I have put all of the areas that need to be modified in Italics & Bold, so please update where necessary.

And now, the script:

############################################################################
# Scriptname: Pool_Prov.ps1
# Description: Checks against ADAM for Pool Status
# By: Adam Baldwin
#
# Usage: Use this script to check whether any pools are in a non-provisioning
# state and send an email alert with a list of errors
#
############################################################################

# Specify the LDAP path to bind to, here the Pools OU (Server Group)
$LDAPPath = 'LDAP://YOURVIEWSERVER.YOURDOMAIN.local:389/OU=Server Groups,DC=vdi,DC=vmware,DC=int'
$LDAPEntry = New-Object DirectoryServices.DirectoryEntry $LDAPPath

# Create a selector and start searching from the path specified in $LDAPPath
$Selector = New-Object DirectoryServices.DirectorySearcher
$Selector.SearchRoot = $LDAPEntry

# Run the FindAll() method and limit to only return what corresponds to Pools
# in VMware View

$Pools = $Selector.FindAll() | where {$_.Properties.objectcategory -match "CN=pae-ServerPool"}

# Creates registry keys if they do not already exist
if (!(Test-Path -Path "hklm:software\VMWare, Inc.\stats")) {
     new-item -path "hklm:software\VMWare, Inc.\stats"
     New-ItemProperty "hklm:\software\VMWare, Inc.\stats" -Name "enabled" -Value "" -PropertyType "String"
     New-ItemProperty "hklm:\software\VMWare, Inc.\stats" -Name "disabled" -Value "" -PropertyType "String"
}

# Instantiate variables
$list = ""
$pool_ids = @()
$pool_state = @()
$pool_error = @()
$error_msg = ""
$a = 0
$hash = @{}
$error_hash = @{}
$error_count = 0
$disabled = ""
$enabled = ""

# Loop thru each pool found in the above $Pools
$count = $pools.count
for ( $i = 0 ; $i -lt $count; $i++ ) { $pool_ids += @($i) }
for ( $i = 0 ; $i -lt $count; $i++ ) { $pool_state += @($i) }

if ($count -eq $null)
{
     $Pool_ids = @(1)
     $Pool_state = @(1)
}

foreach ($Pool in $Pools)
{
     $attribute = $Pool.Properties
     # Define what value we are looking for, here we are retrieving pool name
     $value = 'name'
     $status = 'pae-vmprovenabled'
     $msg = 'pae-vmproverror'
     $pool = $attribute.$value
     $state = $attribute.$status
     $error_msg = $attribute.$msg

     # Check if pool is provisioning or disabled and record into hash table
     if (!$error_msg) {
          $error_msg = "No error to report."
     }

     $pool_state[$a] = $state
     $pool_ids[$a] = $pool

     if ($($pool_state[$a]) -eq 0) {
          $hash.add("$pool", 0)
          $error_hash.add("$pool", "$error_msg")
          $Error_count += 1
          $disabled += $pool + ","
          $disabled = $disabled -Replace " ", ""
     }
     elseif ($($pool_state[$a]) -eq 1) {
          $hash.Add("$pool", 1)
          $enabled += $pool + ","
          $enabled = $enabled -Replace " ", ""
     }
     $a++
}

# Build hashtables for error count and messages, build variables
$Pool_msg = @()
for ( $i = 0 ; $i -lt $error_count; $i++ ) { $pool_error += @($i) }
for ( $i = 0 ; $i -lt $error_count; $i++ ) { $pool_msg += @($i) }
$a = 0
$exc = 0

$ds_test = (Get-ItemProperty "hklm:software\VMWare, Inc.\stats").disabled
$ds_test = $ds_test -split ","
$disable_new = $disabled -split ","

foreach ($id in $pool_ids)
{
     if ($($hash[$id]) -eq 1) {
     }
     elseif ($($hash[$id]) -eq 0) {
          $pool_error[$exc] = $id
          $pool_msg[$exc] = " - " + $error_hash["$id"]
          $exc++
     }

     $a++
}

# count to determine whether email should be sent
$send = 0
$exc = 0

foreach ($fail in $pool_error) {

     if ($ds_test -contains $disable_new[$exc])
          {}
     else {
          $list += $pool_error[$exc] + $pool_msg[$exc] + "`n"
          $send++
     }

     $exc++
}

$EmailTo = "YOUREMAIL@YOURDOMAIN.ORG", "YOUREMAIL2@YOURDOMAIN.ORG"
$EmailFrom = "SERVICEEMAIL@YOURDOMAIN.ORG"
$EmailSubject = "POOL - PROVISION ERROR"
$SMTPSRV = "YOURSMTPSERVER.YOURDOMAIN.LOCAL"
$MyReport = "The following Pools have stopped provisioning due to one or more errors: `n`n" + $list

Set-ItemProperty "hklm:software\VMWare, Inc.\stats" -Name enabled -Value $enabled
Set-ItemProperty "hklm:software\VMWare, Inc.\stats" -Name disabled -Value $disabled

if ($send -ne "0"){send-Mailmessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -SmtpServer $SMTPSRV -Body $MyReport}

As you can see, we are making queries on all pools, building a hashtable, querying the registry for previously reported pools, and building an error log. In order for SMTP to work, you should have a functioning mail server that will allow passthru. There are no other special requirements for this script as all the data we needed was easily expanded from the ADAM database.

Summary

The script will email upon catching ANY non-provisioning pools, including ones that were manually disabled. If you see any alerts for disabled pools that have “No error to report” appended, then it is most likely due to an administrator manually disabling provisioning. As with all of the alerts, these will only occur once upon the event. More specific error data is included in the reporting email when an exception occurs that resulted in error.

Here is the result of our report:

pool_error

If you found this article helpful or interesting, please donate a tip!
(LTC): LYs82mDXPohGDCzeRct8HkJwUCfwveGH1t
(BTC): 19JUVeefSBNbxUZNNmbDqfcctU6im6vsNg

VMWare View – Retrieve Count of Available/Customizing/Cloning VMs

Introduction

This series is intended to help VMWare View administrators take greater control of their own reporting mechanisms through use of the View ADAM database. In this article I will show you how to take a count of all available/customizing/cloning VMs and export the data in clean XML format using PowerShell. This script is tested and confirmed working on VMWare View 5.xx.

The reason for developing this solution is in order to help resolve a simple problem. WHERE DO I FIND MY AVAILABLE VM COUNT IN VIEW? Ideally as an admin we would like to know this kind of info at a glance, on the dashboard. This helps us to spot problems or prevent issues as they occur. Using the current method an admin will have to click into a pool, click on Inventory, sort by available, and yes, count each available VM in the list. In View’s Flash console this can be very time consuming, and what a waste! But little did you know you had something sitting right under your nose. It sits on the View Connection server and holds all of the information and attributes you could ever want from View… The ADAM database.

I know you may be asking yourself, “Why not use the built-in PowerCLI cmdlets like get-pool, get-vm, etc to build a simple reporting script???”… Well, if you’ve played with these tools at all, you may have noticed that they take particularly long to load and are cumbersome. While these tools would be great to use, they seem to be geared  towards one-off queries rather than the scheduled tasks that we want. Not to mention reporting becomes pretty useless if it takes 10 min+ to run!

That’s where ADAM comes to the rescue. The View ADAM database contains a full set of attributes about pools, parent images, vms, and their statuses within View as well as other great data that’s rich for mining. Connecting to this database on the View server using ADSI Edit will show a database similar to Active Directory. Each OU represents a portion of View. For instance, the Server Groups OU contains Pool objects and the Servers OU contains all of the VM objects. Taking a closer look, though, we’ll see that this data is not quite easy to read and very ambiguous. VMWare falls a bit short in their documentation of these attributes… But that’s when we kick in the door and take what we want! It is, after all, Our House 🙂

So before we begin let’s make sure to have a few things set up. The account that you use to drive this script must be a Local Administrator on the View server that you are querying, and be assigned the Administrator role in the View Administrator Console. This can be found at View Configuration> Administrators.

The Script


Now to the meat of our project. It begins with some simple LDAP queries (findall) that dumps lists of objects from ADAM into our environment. We query, first, all of the pools in order to build some tables that we will use to match counts later. We’re also defining our attributes that we’re going to query against each object. Most of View’s more useful attributes have ‘pae-‘ prefixed. A couple of the very first lines in this script are:

$object = “pae-serverpooltype”

$Pools = $Selector.FindAll() | where {$_.Properties.objectcategory -match “CN=pae-ServerPool” -and $_.Properties.$object -eq “4”}

As we can see, we’re querying to check if an object matches the objectcategory of pae-ServerPool, and also if the value of  pae-serverpooltype is 4. There was no documentation as to what the value of pae-serverpooltype equates to in terms of Automated vs Manual pools, but through testing I found that 4 is an Automated pool whereas 5 is a Manual pool. As you can see, these attributes are somewhat vague, but no worries as we can easily determine their use through observation and testing.

I can walk through the rest of the functions and methods within the script, but let’s see it first! I have put all of the values that need to be updated for your environment in Italics & Boldso please modify where necessary.

############################################################################
# Scriptname:     Pool_Stats.ps1
# Description:     Checks against ADAM for Pool statistics
# By:            Adam Baldwin
#
# Usage: Use this script to count all machines in the View ADAM DB and query
# pae-dirtyfornewsessions attribute for availability, then make a count for 
# each pool of available vs unavailable machines.
# 
#
############################################################################

# Specify the LDAP path to bind to, here the Pools OU (Server Group)
$LDAPPath = 'LDAP://VIEWCONNECTIONSERVER.YOURDOMAIN.local:389/OU=Server Groups,DC=vdi,DC=vmware,DC=int'
$LDAPEntry = New-Object DirectoryServices.DirectoryEntry $LDAPPath

# Create a selector and start searching from the path specified in $LDAPPath
$Selector = New-Object DirectoryServices.DirectorySearcher
$Selector.SearchRoot = $LDAPEntry
$object = "pae-serverpooltype"

# Run the FindAll() method and limit to only return what corresponds to Pools in VMware View
$Pools = $Selector.FindAll() | where {$_.Properties.objectcategory -match "CN=pae-ServerPool" -and $_.Properties.$object -eq "4"}

# Loop thru each pool found in the above $Pools

$a = 0
$pool_ids = @()
$available = 0
$unavailable = 0

$count = $pools.count
for ( $i = 0 ; $i -lt $count; $i++ ) { $pool_ids += @($i) }

if ($count -eq $null)
{
$Pool_ids = @(1)
}

foreach ($Pool in $Pools) 
{
	$attribute = $Pool.Properties

	# Define what value we are looking for, here we are retrieving pool name
	$value = 'name'
	$pool = $attribute.$value
	$pool_ids[$a] = $pool
	$a++
}

# Build hash tables for counting VM states

$hash = @{}
$hash_clon = @{}
$hash_cust = @{}
$hash_ready = @{}

foreach ($id in $pool_ids) 
{
	$hash.add("$id", 0)
	$hash_clon.add("$id", 0)
	$hash_cust.add("$id", 0)
	$hash_ready.add("$id", 0)
}

# Specify the LDAP path to bind to, here the VM OU (Servers)
$LDAPPath = 'LDAP://VIEWCONNECTIONSERVER.YOURDOMAIN.local:389/OU=Servers,DC=vdi,DC=vmware,DC=int'
$LDAPEntry = New-Object DirectoryServices.DirectoryEntry $LDAPPath

# Create a selector and start searching from the path specified in $LDAPPath
$Selector = New-Object DirectoryServices.DirectorySearcher
$Selector.SearchRoot = $LDAPEntry

# Run the FindAll() method and limit to only return what corresponds to Virtual Desktops in VMware View
$VMs = $Selector.FindAll() | where {$_.Properties.objectcategory -match "CN=pae-VM"}

# Loop thru each desktop found on the above $VMs
foreach ($VM in $VMs) {
	$attribute = $VM.Properties

	# Define what value we are looking for, here we are checking VM availability
	$value = 'pae-dirtyfornewsessions'

	$ProvStatus = $attribute.$value

	if ($ProvStatus -eq "1") {
	$unavailable++
	}
	else {
	$available++
	}

	# Below pulls VM Path name to perform logic
	# If your View directories have extra levels, use the additional
	# directory parser string that is commented out below.
	# This would be used if you had more than one level on the standard
	# \"cluster"\VM\View directory structure

	$attribute = $VM.Properties
	$Value = 'pae-vmpath'
	$path = $attribute.$value

	# Reassign $value to the displayname in order to perform string parsing
	# The below removes the VM name from the full path in order to leave
	# the Pool name as a parsed value after removing the directory names
	$value = 'pae-displayname'
	$name = "/" + $attribute.$value
	$path = $path -Replace ($name, "")

	# Parser strings for directory paths to the Pool folder - THIS ONE IS NECESSARY
	$extPath = "/VIEWPATH/vm/View/"

	# THE BELOW LINE IS ONLY NECESSARY IF YOU
        # HAVE EXTRA ANY SUBDIRECTORIES IN VCENTER FOR A POOL
        # Just comment out the line below to use
        # $dirname = "EXTRADIRECTORYIFMORETHANONELEVEL/"

	$path = $path -Replace ($dirname, “”) 
        $path = $path -Replace ($extPath, “”)

	# Define what value we are looking for, here we are checking VM state
	$value = 'pae-vmstate'
	$state = $attribute.$value

	# Upon verifying the machine is available, increment a count to the 
        # $hash table for the corresponding pool

	if ($ProvStatus -ne "1") 
	{
		foreach ($id in $pool_ids) 
		{
			if ("$path" -eq "$id") 
			{
				if ("$state" -eq "READY"){
					$hash_ready["$id"]++
				}
				if ("$state" -eq "CUSTOMIZING") {
					$hash_cust["$id"]++
				}
				if ("$state" -eq "CLONING") {
					$hash_clon["$id"]++
				}
				$hash["$id"]++
			}
			Else 
			{
			}
		}

	}

}

# This method used to export all data sets into a csv table
$exp = @("Pools,Available,Ready,Customizing,Cloning")
$hash.keys | sort | %{$exp += (@($_) + $hash.$_ + $hash_ready.$_ + $hash_cust.$_ + $hash_clon.$_) -join ","}

# This method used to export csv table. Here, we had to Out-File first
# and then Import/Export the csv to correct delimeter issues
Remove-Item c:\Scripts\Pool_Stats.csv
$exp | Out-File c:\Scripts\temp.csv
$imp = Import-Csv c:\Scripts\temp.csv
$imp | Export-Csv -NoTypeInformation c:\Scripts\Pool_Stats.csv
Remove-Item c:\Scripts\temp.csv
(Get-Content C:\Scripts\Pool_Stats.csv) | foreach {$_ -replace '"'} | Set-Content C:\Scripts\Pool_Stats.csv

# Create XML template with single tag in an empty string
# append all pool data from previous csv
$xml = "<Pools>"

# Below is a useful method for creating xml objects in an already existant xml file
$xml += $(import-csv c:\scripts\pool_stats.csv | foreach {'<Pool Pool="{0}" Available="{1}" Ready="{2}" Customizing="{3}" Cloning="{4}"/>' -f $_.Pools, $_.Available, $_.Ready, $_.Customizing, $_.Cloning})
$xml += "</Pools>"

$xml | out-file c:\scripts\view_pools.xml

There are some important things to note about what markups need to be made to this. One of the problems that I encountered in the beginning was the fact that there was no attribute for the VM’s pool name. This was problematic, so in order to solve this I took the pae-vmpath and parsed the directories to leave just the pool name from the directory listing.

If you review the script, you will see that I have an area to set path variables for parsing. These strings should be the View directory leading up to the machine name (ie \”cluster”\VM\View). I added a second string, which is commented out, in case there are any pools that have an extra directory level. Please modify these as needed to match your structure. If all of your pools are made in the default manner, then you should only need the first string as they all should be in a single vCenter directory. See below for an example of where your VM Pool folders should sit. I am just selecting the base directory for View.

top_level

Other than that, be sure to update your View server address in the LDAP strings and you’re good to go! The XML that is produced will have each pool as a tag with all of the cloning/customizing/available attributes and values inside. This can be easily read as a dataset for gridview in ASP, or can be translated using any method of your choice.

**UPDATE**

For one reason or another, there are times when a pool builds up with “Agent Unreachable” machines. Now, the “Agent Unreachable” status in itself is not a bad thing, it’s to be expected during the cloning process. However we’ve had occasional problems with these machines never coming out of this state to become truly available. There is a setting that you can modify on pools in ADAM to delete “Already Used” machines, but that doesn’t help us here.

In order to help our team, I’ve added some lines to this script to send an alert email when a pool is found to have 0 “Available” machines. This uses the same alerting mechanism in my Provisioning Error script, with similar logic. Simply add the following lines of code to the end of the script to receive email alerts when 0 available machines are found in a pool.


# Creates registry keys for 0 available alert, if they do not already exist
if (!(Test-Path -Path "hklm:software\VMWare, Inc.\availability")) {
	new-item -path "hklm:software\VMWare, Inc.\availability"
	New-ItemProperty "hklm:\software\VMWare, Inc.\availability" -Name "available" -Value "" -PropertyType "String"
	New-ItemProperty "hklm:\software\VMWare, Inc.\availability" -Name "noneAvailable" -Value "" -PropertyType "String"
}

$wksAvailable = ""
$noneAvailable = ""

# Check if any pools have 0 available and send an alert if so

$send = 0
$list = $null

foreach ($item in $hash_ready.keys) 
{
	if ($hash_ready[$item] -eq 0) 
	{
		$noneAvailable += $item + ","
		$noneAvailable = $noneAvailable -Replace " ", ""	
	}
	else 
	{
		$wksAvailable += $item + ","
		$wksAvailable = $wksAvailable -Replace " ", ""	
	}
}

# Perform availability logic to prevent repeat alerts

$available_test = (Get-ItemProperty "hklm:software\VMWare, Inc.\availability").noneAvailable	
$available_test = $available_test -split ","
$available_test = $available_test -Replace ",", ""
$noneAvailable_new = $noneAvailable -split ","
$noneAvailable_new = $noneAvailable_new -Replace ",", ""

foreach ($unavailable in $noneAvailable_new) {

	if ($available_test -contains $unavailable)
	{}
	else {
		$list += $unavailable + "`n"
		$send++
	}
}

Set-ItemProperty "hklm:software\VMWare, Inc.\availability" -Name available -Value $wksAvailable
Set-ItemProperty "hklm:software\VMWare, Inc.\availability" -Name noneAvailable -Value $noneAvailable

$EmailTo = "YOUREMAIL@YOURDOMAIN.ORG", "YOUREMAIL2@YOURDOMAIN.ORG"
$EmailFrom = "SERVICEEMAIL@YOURDOMAIN.ORG"
$EmailSubject = "POOL - NO AVAILABLE MACHINES IN ONE OR MORE POOLS"
$SMTPSRV = "YOURSMTPSERVER.YOURDOMAIN.LOCAL"
$MyReport = "There are 0 available desktops in the following pool(s): `n`n" + $list 

if ($send -ne "0")
{send-Mailmessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -SmtpServer $SMTPSRV -Body $MyReport}

Summary

This script can be set to a scheduled task and your XML will update at the frequency of your choice. There are many more useful things we can do with the View ADAM database, so be sure to keep an eye out for more scripts and more administrative EEZ!

(The final product)

final_product

Leave any comments or questions below. I’d also love to hear about any success stories.

Happy Scripting!

If you found this article helpful or interesting, please donate a tip!
(LTC): LYs82mDXPohGDCzeRct8HkJwUCfwveGH1t
(BTC): 19JUVeefSBNbxUZNNmbDqfcctU6im6vsNg