When a pool is created in Horizon View, the connection servers will create an AD Object in its lightweight ldap database with a list of attributes matching the settings configured during the pool creation.

These ldap object are easy to see for yourself, you just need to log on one of the connection servers and open “ADSI edit”, note that you can also do it from a management station and target a connection server (You can connect to any connection server as the ldap database is replicated between them). You need to be logged as a user that is a member of the View administrators or specify adequate credentials in ‘Advanced’. Connect to localhost if running it from a connection server and DC=vdi,DC=vmware,DC=int in the connection point. You will then find all your pools in OU=Server Groups .

Protect Your Data with BDRSuite

Cost-Effective Backup Solution for VMs, Servers, Endpoints, Cloud VMs & SaaS applications. Supports On-Premise, Remote, Hybrid and Cloud Backup, including Disaster Recovery, Ransomware Defense & more!

pae-dirtyvmploicy

pae-dirtyvmploicy

Issue

If you are an administrator of a Horizon View VDI infrastructure, you probably encountered the situation when a bunch of virtual machines are stuck on the “Already used” or “Agent disabled” status.

Download Banner

This may happen on desktops that are set to refresh or delete after log off in several cases:

  • Not “cleanly” logged off or reset
  • Desktop restarted on another host following an HA event
  • shut down without reporting to the broker that the user had logged out

This behaviour is aimed at preventing users to access other users’ data as well as keeping the VM from being deleted to allow administrators to troubleshoot.

Resolution

To fix it you can simply refresh or delete the desktop but you would have to do this all the time which is less than ideal. Luckily there is a setting you can set on the pool’s ldap objects that is not available in the web ui.

The procedure is documented in KB1000590

In short, you need to open the ADAM ldap database like mentioned above, find your Server group (pool) and set the value of the attribute pae-DirtyVmPolicy which will change the behaviour of horizon when the situation occurs.

  • pae-DirtyVMPolicy=0: Mark virtual machines that were not cleanly logged off as Already used and block user access to them. This is the default behavior in View 4.6 and later releases
  • pae-DirtyVMPolicy=1: Allow virtual machines that were not cleanly logged off to become available without being refreshed. View Client users can access these desktops
  • pae-DirtyVMPolicy=2: Automatically refresh virtual machines that were not cleanly logged off. View Client users can access these desktops after the refresh operation is completed

Setting this attribute to 2 is probably a safe choice as it will automatically try to resolve the issues.

pae-dirtyvmploicy

Automating it with PowerShell

As you can see the fix is very easy but at the moment it remains a manual process that you will want to execute every time you or your Desktop team creates a Pool, which is easily missed in the processes after a few months.

With this PowerShell script you will be able to automatically set the attribute on one, several or all the Pools quickly and easily as well as query the current policy.

PowerShell script

Function Get-PaeDirtyVmPolicy {

<# .NOTES ------------------- EMail : Xavier.avrillier@gmail.com Date : 8/5/2018 ------------------- .SYNOPSIS Display Pae-DirtyVmPolicy attribute of the ldap object of Horizon View Pools. .Description paeDirtyVMPolicy=0: Mark virtual machines that were not cleanly logged off as Already used and block user access to them. This is the default behavior in View 4.6 and later releases. paeDirtyVMPolicy=1: Allow virtual machines that were not cleanly logged off to become available without being refreshed. View Client users can access these desktops. paeDirtyVMPolicy=2: Automatically refresh virtual machines that were not cleanly logged off. View Client users can access these desktops after the refresh operation is completed. .EXAMPLE PS C:\Users\hzn-user> Get-PaeDirtyVmPolicy

Server PoolNames pae-DirtyVmPolicy
—— ——— —————–
localhost V-JL01-01-S-XXX 2

.EXAMPLE
PS C:\Users\hzn-user> Get-PaeDirtyVmPolicy -Server localhost -PoolNames V-JL01-01-S-XXX -Credential (Get-Credential)

Server PoolNames pae-DirtyVmPolicy
—— ——— —————–
localhost V-JL01-01-S-XXX 2
#>

param(

[string] $Server = “localhost”,

[string[]] $PoolNames = “*”,

$Credential
)

$PaeAttribute = ‘pae-DirtyVmPolicy’

foreach ($Pool in $PoolNames) {

$params = @{
SearchBase = “OU=Server Groups,DC=vdi,dc=vmware,dc=int”
Filter = ‘CN -like “‘+$Pool+'”‘
Server = $Server
SearchScope = 1
Properties = $PaeAttribute
}

if ($Credential) {$params.Add(‘Credential’,$Credential)}

$AdObject = Get-ADObject @params | select DistinguishedName,Name,ObjectGUID,pae-DirtyVmPolicy

if ($AdObject) {

if (!$AdObject.$PaeAttribute) {$AdObject.$PaeAttribute = 0}

[pscustomobject]@{

Server = $Server
PoolNames = $AdObject.Name
$PaeAttribute = $AdObject.$PaeAttribute
}

} else {Write-Warning “Pool $Pool not found in ldab base”}

}

}

Function Set-PaeDirtyVmPolicy {

<# .NOTES ------------------- EMail : Xavier.avrillier@ext.consilium.europa.eu Date : 8/5/2018 ------------------- .SYNOPSIS Change Pae-DirtyVmPolicy attribute of the ldap object of Horizon View Pools. .PARAMETER PaeDirtyVmPolicyValue paeDirtyVMPolicy=0: Mark virtual machines that were not cleanly logged off as Already used and block user access to them. This is the default behavior in View 4.6 and later releases. paeDirtyVMPolicy=1: Allow virtual machines that were not cleanly logged off to become available without being refreshed. View Client users can access these desktops. paeDirtyVMPolicy=2: Automatically refresh virtual machines that were not cleanly logged off. View Client users can access these desktops after the refresh operation is completed. .EXAMPLE PS C:\Users\hzn-user> Get-PaeDirtyVmPolicy | Set-PaeDirtyVmPolicy -PaeDirtyVmPolicyValue 1

Server PoolNames pae-DirtyVmPolicy
—— ——— —————–
localhost V-JL01-01-S-XXX 1

.EXAMPLE
PS C:\Users\hzn-user> Get-PaeDirtyVmPolicy -Server localhost -PoolNames V-JL01-01-S-XXX -Credential (Get-Credential) | Set-PaeDirtyVmPolicy -PaeDirtyVmPolicyValue 2

Server PoolNames pae-DirtyVmPolicy
—— ——— —————–
localhost V-JL01-01-S-XXX 2
#>

param(
[Parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyname=$True)] [string] $Server = “localhost”,

[Parameter(Mandatory=$true,ValueFromPipeline=$True,ValueFromPipelineByPropertyname=$True)] [string[]] $PoolNames,

[validateset(0,1,2)] [int] $PaeDirtyVmPolicyValue = 2,

$Credential
)

Process {

$PaeAttribute = ‘pae-DirtyVmPolicy’

foreach ($Pool in $PoolNames) {

$params = @{
SearchBase = “OU=Server Groups,DC=vdi,dc=vmware,dc=int”
Filter = ‘CN -like “‘+$Pool+'”‘
Server = $Server
SearchScope = 1
}

$GetParams = @{Server = $Server; PoolNames = $Pool}

if ($Credential) {
$params.Add(‘Credential’,$Credential)
$GetParams.Add(‘Credential’,$Credential)
}

Get-ADObject @params | Set-ADObject -Replace @{$PaeAttribute=$PaeDirtyVmPolicyValue}

Get-PaeDirtyVmPolicy @GetParams
}

}

}

Script usage

Parameters

Server IP or FQDN of a connection server.
Default : localhost
ValueFromPipelineByPropertyName
PoolNames Name(s) of the pool(s) in Horizon view.
Default : *
ValueFromPipelineByPropertyName
Credential Horizon View Administrator user account. Leave blank if you are logged as one.
PaeDirtyVmPolicyValue Attribute value to set (see overview for details) possible values 0, 1, 2
Default : 2

Example 1: Retrieve the attribute value for all the pools while logged on a connection server as a View administrator.

PS C:\Users\View-Admin> Get-PaeDirtyVmPolicy

Example 2: Retrieve the attribute for a specific pool, remotely, logged as standard user.

Enter the credentials of a View admin in the prompt.

PS C:\Users\Std-user> Get-PaeDirtyVmPolicy -Server localhost -PoolNames My-Pool-xyz -Credential (Get-Credential)

Example 3: Set the attribute on all the pools remotely logged as View admin.

PS C:\Users\View-Admin> Get-PaeDirtyVmPolicy –Server Srv-ConnSrv01 | Set-PaeDirtyVmPolicy -PaeDirtyVmPolicyValue 2

Automation (to avoid forgetting to run the script)

Having a script to automate the setting of this attribute will sure make it much faster and easier but it will be even better when it runs automatically. For this you will need to create a scheduled task somewhere that you schedule to run (every day for example) which will do it for you without even having to start PowerShell.

pae-dirtyvmploicy

Follow these steps to ensure a clean setup and don’t forget to document it internally.

  • Define a server on which you run the scheduled task. A server dedicated to running tasks is probably a good idea, I wouldn’t recommend a connection server to preserve consistency across them
  • Save the script as Pae-DirtyVmPolicy.ps1 in the location of your choice (C:\Scripts in this example)
  • Open Pae-DirtyVmPolicy.ps1 and add the command that will change the attribute at the end of the scripts (See examples). I personally use and recommend Example 3
  • Create a scheduled task with the following properties:
    • User account : user member of View administrators
    • check “Run whether user is logged on or not”
    • Set a trigger at your convenience (once a day?)
    • Action > New > Program/Script : Powershell.exe -file “C:\Scripts\ Pae-DirtyVmPolicy.ps1”
  • Test the scheduled task by changing the setting in the script and running it manually

Follow our Twitter and Facebook feeds for new releases, updates, insightful posts and more.

Rate this post