vmware-logo

VMware Horizon View is an amazing tool for remote access.  The struggle is making sure that the proper users have access to View from anywhere, while only allowing some users to consume their virtual desktop on-site.  The reasons for this run the gamut.  Some companies have hourly employees that can’t be allowed to access remotely for payroll/legal reasons.  Some desktops can’t be used remotely because of security concerns.  The list goes on and on.

VMware has a solution for this problem.  As an administrator, you can create separate pools for your user base.  One pool for internal only access and one pool for internal and external access.  Then using their pool tagging feature, you can designate who can connect from where.  For many organizations, this is kludgy at best.  It means that if your pools are separated by department, function, group, or anything else, you now have to have double the pools.  While its a workable solution, it creates additional overhead and work for the administrator.

Another solution available is to use some sort of two factor authentication.  One could use something like a (soft/hard) token from RSA.  Anyone without a token obviously won’t be able to get in externally, while still allowing everyone the ability to log in from the internal network.  There are others available as well that can text a passcode to the user.  These additions basically move the gatekeeper from the Horizon View environment itself into the two factor product.  While a fantastic idea, this not only costs money but can also lead to problems with end user acceptance.

What is needed is an easier way to control external access.  It doesn’t get much easier than an Active Directory group, so let’s do that.

There are three basic pieces that are needed to make this idea happen.

1. An Active Directory group

2. A Group Policy

3. A script (we’ll be using PowerShell)

The first part should be easy, depending on the organization.  We need a simple Active Directory security group.  For this example, we’ll call this group ViewRemoteAccess.

Next a Group Policy is needed.  VMware has some seriously underutilized and not very well documented Group Policies that are fantastic.  Specifically, I’m talking about ‘CommandsToRunOnConnect’ and ‘CommandsToRunOnReconnect’.  To utilize these policies, its required to add the View .adm template into your Active Directory environment.  It’s a relatively simple process, but outside the scope of this document.  For more information on that, please see the VMware documentation here.

Finally, we need a script.  This PowerShell script will be set to run automatically by the Horizon View agent (using the SYSTEM account) every time a user logs into the virtual desktop.  It basically checks for two things.  First it checks if the internal broker is being used.  This is done by loading the logged on user’s registry hive and checking the ‘Volatile Environment’ key.  The View agent uses this key to store some basic information about the session.  One of those things is the name of the connection broker.  If the script finds that it isn’t using the internal broker, it goes out and queries an Active Directory group to see if the user is a member of the remote access group.

Here’s where it gets interesting.  If the user is external and not in the remote access group, we’ve got to do something.  In this case, the script does a few things.  Once the desktop loads, a warning box pops up telling the user that they are not allowed to access the desktop remotely.  This is set as the top most box, so that nothing can accidentally cover it up so the user can’t see it.  Now, we need to make sure the user can’t do anything, so next it blocks keyboard and mouse access.  We’ve covered all the bases here by warning the user and preventing them from doing anything to the desktop, the only thing left is to disconnect him or her.  We now stop the View Agent service, wait 10 seconds to ensure a disconnection, and restart the service.  Finally we unblock keyboard and mouse access after they are gone.

So that’s it.  Here are a couple of notes.  The PowerShell script logs what it does to the Application event logs.  Also the warning box takes about two minutes to disappear.  And finally, if there are more than one internal connection brokers, the variable will need to be changed into an array and the array checked.

Here is the command that needs to go into the GPO.  I suggest putting the script into the Horizon View Agent scripts folder, but it really can be placed anywhere.

-C powershell -ExecutionPolicy unrestricted -windowstyle Hidden -File "C:Program FilesVMwareVMwareViewAgentscriptsviewConnectScript.ps1"

And finally, here is the PowerShell script itself:

<#

File: viewConnectScript.ps1
Created: 05/01/14
Updated:
Version: 1.0
Author: Zac

Purpose: This script is designed to detect the connection broker being
used and determine if a user is in the correct Active Directory group
for remote access. If they aren’t, the script is set to disconnect them
by restarting the agent service.

Notes: This works in conjunction with VMware’s View ADM templates listed
below. Specifically, ‘CommandsToRunOnConnect’ and ‘CommandsToRunOnReconnect’
http://pubs.vmware.com/view-50/topic/com.vmware.view.administration.doc/GUID-AB42F842-BD66-4856-9E61-1A392BF93B6F.html

All actions are logged to the Applications eventlog.
#>
######################################################
# Set these variables as needed
$strRemoteAccessADGroup = “ViewRemoteAccess”
$strInternalBroker = “https://internalbroker.corporate.com:443”
######################################################

Import-Module activeDirectory

# Create eventlog source for logging, if it doesn’t exist
[System.Diagnostics.EventLog]::SourceExists(“viewConnectScript”) |
% {if ($_ -eq $false) {New-EventLog –LogName Application –Source “viewConnectScript”}}
Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Information `
–EventID 1 -Category “” –Message “Starting viewConnectScript.”

# Get logged on username from WMI
$strFullAccount = (Get-WMIObject -class Win32_ComputerSystem | select username).username
$arrUser = $strFullAccount -split “” # Split out domain and user into array

# Convert NT account to SID, so we can read HKU registry values
$objUser = New-Object System.Security.Principal.NTAccount($arrUser[0], $arrUser[1])
$arrUser = $arrUser + $objUser.Translate([System.Security.Principal.SecurityIdentifier])
# $arrUser now contains 0:Domain, 1: Account, 2:SID

Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Information `
–EventID 1 -Category “” –Message “Domain: $($arrUser[0]), User: $($arrUser[1]), SID: $($arrUser[2])”

# Get current broker from View registry key using SID of logged on user
# See http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1021020
New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS | out-null # Start script crashes if there is ANY output
$strRegisryBroker = (Get-ItemProperty -Path “HKU:$($arrUser[2])Volatile Environment” -Name ViewClient_Broker_Tunnel_URL).ViewClient_Broker_Tunnel_URL
Remove-PSDrive HKU

Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Information `
–EventID 1 -Category “” –Message “Broker listed in registry is $strRegisryBroker.”

# Determine if registry broker = remote broker
if ($strInternalBroker -eq $strRegisryBroker) {
# This is the internal broker, quit script
Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Information –EventID 1 -Category “” `
–Message “Internal broker. Script exiting.”
Exit
} else {
# This is not the internal broker
Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Information –EventID 1 -Category “” `
–Message “This is not the internal broker. Continuing…”
}

# Set boolean to false. If user is in remote access group, will be set to true.
$bolInGroup = $false
Get-ADPrincipalGroupMembership $arrUser[1] | Select Name | % {if ($_.Name -eq “$strRemoteAccessADGroup”) {$bolInGroup = $true}}
If ($bolInGroup -eq $false) {
# User is not in remote access group
Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Warning –EventID 1 -Category “” `
–Message “User is not in remote access group. Restarting agent to disconnect user.”

# Display warning message
$strScriptBlock = {
Add-Type -AssemblyName System.Windows.Forms
$objForm = New-Object system.Windows.Forms.Form
$objForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedToolWindow
$objForm.AutoSize = $true
$objForm.AutoSizeMode = “GrowAndShrink”
$objForm.ControlBox = $false
$objForm.ShowInTaskbar = $false
$objForm.StartPosition = “CenterScreen”
$objForm.BackColor = “Red”
$objForm.Text = “Remote Access Warning”
$guiFont = New-Object System.Drawing.Font(“Times New Roman”,22,[System.Drawing.FontStyle]::Italic)
$objForm.Font = $guiFont
$guiLabel = New-Object System.Windows.Forms.Label
$guiLabel.Text = “This user account has not been granted remote access.`r`nPlease contact the IT department. Disconnecting session…”
$guiLabel.AutoSize = $true
$objForm.SizeGripStyle = “Hide”
$objForm.Controls.Add($guiLabel)
$objForm.TopMost = $true
$objForm.ShowDialog()
}

$objAlert = Start-Job -Name Alert -ScriptBlock $strScriptBlock

# Block user from doing anything
$objUser32 = “[DllImport(`”user32.dll`”)]public static extern bool BlockInput(bool fBlockIt);”
$netBlock = Add-Type -MemberDefinition $objUser32 -Name DisableInput -Namespace DisableInput -PassThru
$netUnblock = Add-Type -MemberDefinition $objUser32 -Name EnableInput -Namespace EnableInput -PassThru
$netBlock::BlockInput($true) | out-null

C:WindowsSystem32SC.exe STOP WSNM | out-null # Stop the service
Start-Sleep -s 10 # Wait to ensure disconnect
C:WindowsSystem32SC.exe START WSNM | out-null # Start service back up
$netUnblock::BlockInput($false) | out-null # Unblock input

Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Information –EventID 1 -Category “” `
–Message “Agent restart complete. Script exiting.”

# Kill alert dialog box. For some reason this takes Powershell about 2 minutes to do. Yay
Get-Job | Stop-Job

Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Information –EventID 1 -Category “” `
–Message “Script complete. Exiting.”
} else {
Write-EventLog –LogName Application –Source “viewConnectScript” –EntryType Information –EventID 1 -Category “” `
–Message “User is in remote access group. Script exiting.”
}
Exit

(Script file can be downloaded here.)

That’s it.  You’re done.  You should now have a functioning Active Directory group that permits remote access, while denying it for everyone else.

This post originally appeared on stegsolutions.com.  Need help with your virtualization project?  Let us help.  Contact zac@stegsolutions.com for more information.