I’m currently working on a lab project which enables cloud consumers to request a full VMware Horizon environment straight from my vRealize Automation catalog. This will provision Connection Servers, a Composer server, UAGs – all load-balanced using VMware NSX. Exciting, but not without its challenges. The first being… Horizon wouldn’t install.
Please note: the credit for most of this work goes to Chip Zoller. He did the bulk of it – I merely plagiarized his work and sanded down the edges.
When vRA installs software components on a provisioned workload, it uses the Software Agent, which is installed in the parent template. When you perform the installation, you’re asked to choose which account to run the service as – localSystem or a domain user. Prior to version 7.3, you could also choose a local account, but this option was later removed.
For the majority of software installations, localSystem works fine. However, the VMware Horizon Connection Server is slightly different in that if you try to install as localSystem the process will fail – it needs to be a domain account.
The solution
To resolve this we need to find a way to run the vRA Software Agent as a domain account. To do this it will also require the “Logon as a Service” right.
Please note: whilst I’m aware you can choose this option when installing the agent, our templates do not belong to a domain before provisioning – so this needs to happen after the machine has joined the domain.
With the parent template powered on, install the Guest User (gugent) and Software Agents as normal, selecting localSystem for the account.
Next, create a local group (I called mine “Gugent”) and leave it empty.
Use the Local Security Policy editor to assign the “Logon as a Service” right to the newly-created group. Finally, either reload the security policy or reboot the machine before converting it into a template or taking a snapshot.
Of course there’s a script…
There’s a number of ways to achieve the next step, and I chose to create another software component to do it. The idea is for the component to run a script which takes the username, password and domain name of the account which will run the software agent service. It will then add the user to the group, change the logon credentials for the service, kill the process (it won’t shut down gracefully, trust me – I tried), before finally starting the service.
On the converged blueprint designer, this software component will be placed before the one that installs Horizon, enabling the installation to complete successfully.
Use the following script for the software component:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Format the username | |
if ($userName -ne $null) { | |
$user = $userName + "@" + $userDomain | |
} else { | |
$user = $userName | |
}; | |
# Add the user to the Gugent group | |
$group = "Gugent" | |
Add-LocalGroupMember -Group $group -Member $user | |
# Set vRA Software Agent Bootstrap to logon using local account | |
$serviceName = 'vRASoftwareAgentBootstrap' | |
$computerName = 'localhost' | |
$filter = 'Name=' + "'" + $serviceName + "'" + '' | |
$service = Get-WMIObject -ComputerName $computerName -namespace "root\cimv2" -class Win32_Service -Filter $filter | |
Write-Output "Changing service $serviceName to run as account $userName" | |
$result = $service.Change($null,$null,$null,$null,$null,$null,$user,$userPass,$null,$null,$null) | |
if ($result.ReturnValue -eq '0') { | |
Write-Output "Service $serviceName logon account changed" | |
} else { | |
Write-Output "Error: $serviceName logon account not changed" | |
}; | |
# After hours of working on this, I know the service won't stop gracefully. Kill it with fire… | |
Write-Output "Stopping service $serviceName" | |
$servicePID = (Get-WMIObject -class Win32_Service | Where-Object Name -eq $serviceName).processID | |
Stop-Process $servicePID -Force | |
# Start the service | |
Write-Output "Starting service $serviceName" | |
try | |
{ | |
Start-Service -Name $serviceName -ErrorAction Stop | |
Write-Output "Service $serviceName started" | |
} | |
catch | |
{ | |
Write-Output "Error: $serviceName failed to start" | |
} |
Once you have added this to your blueprint, be sure to add any further software components on after this one, for example.
Pingback: Newsletter: January 18, 2020 – Notes from MWhite