Horizon View Connection Server with Ansible

Horizon View Connection Server with Ansible

How to setup Horizon View Connection Server with Ansible. In my previous Horizon and Ansible posts I assumed the connection server was already there. What if it isn’t though and you need automation on it. This question on Reddit actually triggered me. It’s true I do not mention installing and configuring the Connection Server. So let’s make an Ansible play with a custom module to do exactly does that.

Assumptions

I am going to make some assumptions here. My first assumption is that you have the installation software on some location. The second assumption, you have a server vm ready. And the third assumption, you know how roles work in Ansible. Since I basicly willl be writing a role, not a playbook.

Used software and versions

Below a list of software and versions which I used to test on.

  • Ansible 2.9.27
  • Windows Server 2019
  • Powershell 5.1
  • Powercli 12.4
  • HV Helper (https://github.com/vmware/PowerCLI-Example-Scripts/tree/master/Modules/VMware.Hv.Helper)
  • VMware-Horizon-Connection-Server-x86_64-7.12.0-15770369

Intended Audience

The script I will be sharing with you can be used as Ansible role in your automation. In this post I will make a breakdown on how to make a custom Powershell module for Ansible. So, this post is for you if you are interested in leveraging Powershell scripts to Ansible. Ofcourse this post is also for you if you are looking for a way to automate Connection server installation and configuration.

Three steps

There are three steps needed to get this installation done. Firstly I need to make sure the software is actually installed. Secondly I need to push the configuration to the Connection Server. In this case it will just be the vCenter Connection. My main focus for this post will be on this second step. This step contains the custom module I want to use. Finally I need to call for the custom module in an Ansible Playbook.

Step one – install Horizon View Connection Server software

First I need to install the connection server. In my case I have injected the software into the template. This means I can always find it in the same location in the template. Then I am going to use a win_command to have it actually installed. This would be step one.

In Ansible it would look like this:

---
# check if installation already exists, to make script idempotent
- name: check for Connection Server
  ansible.builtin.win_stat:
    path: 'C:\Program Files\VMware\VMware View\Server\bin\ws_ConnectionServer.exe'
  register: connection_info

- name: Install Connection Server
  win_command: c:\install\VMware-Horizon-Connection-Server-x86_64-7.12.0-15770369.exe /s /v" /qn FWCHOICE=1 VDM_SERVER_INSTANCE_TYPE=1 VDM_INITIAL_ADMIN_SID="S-1-5-32-544" VDM_SERVER_RECOVERY_PWD="<some_password>" VDM_SERVER_RECOVERY_PWD_REMINDER="<a_reminder>""
  become: yes
  become_method: runas
  become_user: "{{ your_administrator_account }}"
  vars:
    ansible_become_password: "{{ your_administrator_account_password }}"
  when: connection_info.stat.exists == False

So the code above will install an instance of Horizon View Connection Server 7.12. For it to work I did need to elevate the win_command. Now with an instance of Horizon View installed, the big question is. How to configure this in an automated way? This is where the custom module comes in. First some pre-requisites for using the custom module.

Prerequisites for using the custom Horizon View module in a play

The first thing I need is a working Horizon View Module in Powershell. Then I need to make sure the VMware.HV.Helper module is installed and present. Last but not least I need to set some configuration settings to make a succesful connection to the Connection Server. In this case the c:\install folder in my template also contains the three files to install HV.Helper.

For those who like the to see the code I am using to achieve this, see the following snippet. Again, this is optional and there are other ways to get this working. Either way, it needs to work though!

---
# make sure the Horizon View Module is installed
- name: install HorizonView
  win_psmodule:
    name: VMware.VimAutomation.HorizonView
    state: present
  register: install_result

# when the module is newly installed, disable certificate checking
- name: disable ssl certificate checking
  win_shell: |
    Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false
  when: install_result is changed

# create a location for the HV.Helper modules
- name: create location for HV modules
  win_file:
    path: 'C:\Program Files\WindowsPowerShell\Modules\VMware.HV.Helper\'
    state: directory

# copy the HV.Helper files to the appropiate location
- name: copy vc connection info to localhost
  win_copy:
    src: 'C:\install\{{ item }}'
    dest: 'C:\Program Files\WindowsPowerShell\Modules\VMware.HV.Helper\'
    force: no
    remote_src: yes
  loop:
    - VMware.HV.Helper.psm1
    - VMware.HV.Helper.psd1
    - VMware.HV.Helper.format.ps1xml
  register: hv_result

# after installing HV.Helpers make sure modules are ready to use from custom module
- name: one time run of commands based on hv_result
  win_shell: |
    Set-PowerCLIConfiguration -Scope User -ParticipateInCeip:$false -Confirm:$false
    Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false
    Get-Command -Module 'VMware.HV.Helper'
  when: hv_result is changed

Step two – creating the custom Horizon View Connection Server module

This module will configure the connection with your vCenter. It is not possible to do this with the regular modules, hence this custom module. To find out how to achieve this I actually needed to dig into the API of Horizon View. Once there it was an easy find, to be honest. However, it still needs to fit in a custom Ansible module.

section one and two

First of all I need to tell Powershell that I am going to create an Ansible module. Let’s call this section one. Then I need to think about the variables I want to use. Those variables need to have a translation between YAML and Powershell. Let’s call this section two and do a little breakdown.

#input parameters
$hvusername = Get-AnsibleParam -obj $params -name "hv_username" -type "str" -failifempty $true
$hvpassword = Get-AnsibleParam -obj $params -name "hv_password" -type "str" -failifempty $true

In this snippet $hvusername represents the variable for the username in Horizon View. It reads from the playbook I am using to connect with the custom module. More specifiaclly it looks for the hv_username parameter. Then the input will be read as a string. When there is no input, the script fails. This is determined by the -failifempty parameter.

section three – status change of the Horizon module

When all that is done and thought about, the next step. This is simply creating an array with one value: $changed=false. When nothing changes this status will remain. Suppose the script fails, status remains unchanged. Also, when the connection is already done, nothing changes. Making the script idempotent. Let’s call this section 3.

section four – connect to Horizon View

There is not much to tell about section four. Just making a connection with the Horizon View Connection Server. Sometimes I like to put this in a try and catch. Today i don’t.

section five – talking with the API of Horizon View Connection Server

Then section 5, this is where the magic happens! First a simple check if a vCenter already exists. If not, let’s go ahead and connect the vCenter. In my code I decided that hostname represents the vCenter. Also make sure to have a working username. For some reason it needs to contain an ‘@’ . I incorporated this in the Ansible playbook.

The whole section is in a try/catch which should help troubleshooting and error handling in case of trouble. Finally in the end of this section the connection is made and the vcenter is added to the connection server.

section six

This is just one line. However, it is important. This line prints the result. The same result as I will see in the Ansible output.

what is not in the script

The script does not deal with Composer. If you need the composer part too I can recommend https://www.retouw.nl/2020/04/12/adding-vcenter-server-to-horizon-view-using-the-apis/ . This module is in fact largely based on this script.

the script

#!powershell

# add vcenter to Horizon View Connection Server
# tested on Horizon View 7.12

# Section 1
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.Legacy

$ErrorActionPreference = "Stop"

# Section 2
# input parameters
$params = Parse-Args $args -supports_check_mode $true
$hvserver_input = Get-AnsibleParam -obj $params -name "hv_server" -type "str" -failifempty $true
$hvusername = Get-AnsibleParam -obj $params -name "hv_username" -type "str" -failifempty $true
$hvpassword = Get-AnsibleParam -obj $params -name "hv_password" -type "str" -failifempty $true
$hvuserdomain = Get-AnsibleParam -obj $params -name "hv_domain" -type "str" -failifempty $true
$vchostname = Get-AnsibleParam -obj $params -name "hostname" -type "str" -failifempty $true
$vcusername = Get-AnsibleParam -obj $params -name "username" -type "str" -failifempty $true
$vcpassword = Get-AnsibleParam -obj $params -name "password" -type "str" -failifempty $true

# Section 3
$result = @{
    changed = $false
}

# section 4
$hvserver = Connect-HVServer $hvserver_input -User $hvusername"@"$hvuserdomain -Password $hvpassword
$services=  $hvServer.ExtensionData


# section 5
$vc=(Get-HVvCenterServer).ServerSpec.ServerName
if ($vc -ne $vchostname) {
    try{
        $spec=new-object VMware.Hv.VirtualCenterSpec
        $spec.serverspec=new-object vmware.hv.serverspec
        $spec.viewComposerData=new-object VMware.Hv.virtualcenterViewComposerData

        $spec.Certificateoverride=new-object vmware.hv.CertificateThumbprint
        $spec.limits=new-object VMware.Hv.VirtualCenterConcurrentOperationLimits
        $spec.storageAcceleratorData=new-object VMware.Hv.virtualcenterStorageAcceleratorData

        # vCenter Server specs

        $spec.ServerSpec.servername="$vchostname"        # Required, fqdn for the vCenter server
        $spec.ServerSpec.port=443                                 # Required
        $spec.ServerSpec.usessl=$true                             # Required
        $spec.ServerSpec.username="$vcusername"   # Required user@domain
        $vcencPassword = New-Object VMware.Hv.SecureString
        $enc = [system.Text.Encoding]::UTF8
        $vcencPassword.Utf8String = $enc.GetBytes("$vcpassword")
        $spec.ServerSpec.password=$vcencPassword
        $spec.ServerSpec.servertype="VIRTUAL_CENTER"

        # Description & Displayname, neither is required to be set

        #$spec.description="description"              # Not Required
        #$spec.displayname="virtualcenterdisplayname" # Not Required
        $spec.CertificateOverride=($services.Certificate.Certificate_Validate($spec.serverspec)).thumbprint
        $spec.CertificateOverride.SslCertThumbprint=($services.Certificate.Certificate_Validate($spec.serverspec)).certificate
        $spec.CertificateOverride.sslCertThumbprintAlgorithm = "DER_BASE64_PEM"

        # Limits
        # Only change when you want to change the default values. It is required to set these in the spec

        $spec.limits.vcProvisioningLimit=20
        $spec.Limits.VcPowerOperationsLimit=50
        $spec.limits.ViewComposerProvisioningLimit=12
        $spec.Limits.ViewComposerMaintenanceLimit=20
        $spec.Limits.InstantCloneEngineProvisioningLimit=20

        # Storage Accelerator data

        $spec.StorageAcceleratorData.enabled=$false
        #$spec.StorageAcceleratorData.DefaultCacheSizeMB=1024   # Not Required

        # Cmposer
        # most can be left empty but they need to be set otherwise you'll get a xml error

        $spec.ViewComposerData.viewcomposertype="DISABLED"  # DISABLED for none, LOCAL_TO_VC for installed with the vcenter and STANDALONE for s standalone composer


# Disk reclamation, this is required to be set to either $false or $true
$spec.SeSparseReclamationEnabled=$false 

# This will create the connection
    $services.VirtualCenter.VirtualCenter_Create($spec)
    $result.changed = $true
    } catch {Fail-Json -obj $result "wooops"}
} 

# section 7
Exit-Json -obj $result

Step three – use the module in a playbook to configure Horizon View

I have created a new folder in the roles/<your_role_name> directory. I named this new folder library. This folder is a special folder. When I put custom code in this folder it will be rerad by Ansible. I have saved the custom module here with the name set_hv_vcserver.ps1.

Now in the playbook I can call for this module and fill out the parameters. Like the code below.

---
# call for the custom module in library
- name: register vcenter to connection server with custom module
  set_hv_vcserver:
    hv_server: "{{ fqdn_of_your_horizon_connection_server }}"
    hv_password: "{{ your_horizon_connection_server_password }}"
    hv_username: "{{ your_horizon_connection_server_username }}" # without domain
    hv_domain: "{{ domain }}" # the connection server uses user@domain
    hostname: "{{ your_vcenter_fqdn }}"
    username: "{{ your_user@domain_for_vcenter }}"
    password: "{{ your_vcenter_password }}

That should do the trick. It might seem I have used a strange solution for using the same user@domain construction. However in the past I had difficulties doing it different. Feel free though to experiment on this. It does take a little effort to make the script suitable for Ansible. However once done it absolutelu contributes to the overall Ansible experience.

Conclusion

This should conclude the post for today. Hope you enjoyed reading. Please feel free to ask me any questions regarding to this post.

Follow up

I am planning to do the 12DaysOfK8s, curious what that will bring for me. Also some more on Tanzu is to come.
2021 has been a strange year with not as much blogging as I hoped for. Hopefully 2022 will be better. After things settled down privately and having found my way at Redlogic things should be fee. Blog you soon!

guest
0 Comments
Inline Feedbacks
View all comments