VMware Horizon View with Ansible and Powershell – Part 2

logo for ansible vmware

VMware Horizon View with Ansible and Powershell – Part 2

A little while ago I wrote part 1 of VMware Horizon View with Ansible and Powershell. The blog was about setting up a simple and scalable environment of FullClone VDI pools. In this post I will add some extra tasks and complexity to Horizon View with Ansible and Powershell. I will create a flow that first creates securitygroups in AD and then add those as entitlements to corresponding Horizon View pools.

Updates in Part 1

Poolname was hardcoded in the example and is now changed to a variable $hvtemplatepool in Powershell. The ansible taskfile must have a variable named hv_template_poolname.

Goal for part 2

Add entitlements to the pools created in Part1 and do this in an automated fashion. Entitlement is done through securitygroups in AD. Then these securitygroups should correspond with the poolnames we are creating. Therefore a securitygroup must exist before adding it as entitlement. Also make sure the entitlement does not already exist before adding it. In addition the script should not fail if no entitlement is added.

This means:

  • sg_env_poolAA as entitlement for pool env_poolAA
  • sg_env_poolAB as entitlement for pool env_poolAB
  • sg_env_poolAC as entitlement for pool env_poolAC
How do we achieve this
  • first add entitlement variable to Powershell and Ansible taskfile
  • secondly add code for adding entitlements in create_hv_desktoppool.ps1
  • then create role and playbook for adding securitygroups to AD
  • finally run two consecutive playbooks to achieve this
Prerequisites
  • equal to Part 1
  • ldap module for ansible should be installed
    • because I am creating the securitygroups through ldap

Create the ldap-securitygroup-create role and playbook

We need securitygroups for the the entitlements to work and for creating such groups I am using the ldap_entry module. This is probably a little more complex than using a regular Windows module, but I like the versatility of it. We need a couple of things:

  • dN of the securitygroup
  • attribute: grouptype local or global
  • attribute: sAMAccountName
  • ldap connection settings
roles/role_ldap_securitygroup_create/tasks/main.yml

The taskfile

---
- name: add security group object entries to AD
  ldap_entry:
    server_uri: "{{ your_ldap_server_uri }}"
    bind_dn: "{{ your_ldap_bind }}"
    bind_pw: "{{ your_ldap_service_password }}"
    dn: "CN=sg_{{ item }},{{ your_ldap_dn_tree }}" # together must be full dN for item
    attributes:
      groupType: "{{ grouptype }}"
      sAMAccountName: "sg_{{ item }}"
    objectClass:
      - group
    state: present
  • Line 7: dN , make sure the combinates is the exact dN in your AD
  • Line 9: groupType, the number that defines local or global group
  • Line 11: sAMAccountName, this is needed for the group to display correctly

ldap-secgroup-create.yml

The playbook needed to execute the role

---
# file: wg-ldap-secgroup-create
- name: create securiry group in Active Directory through LDAP
  max_fail_percentage: 0
  hosts: all
  connection: local
  gather_facts: false

  vars_files:
    - "vars/pools.yml"

  # Load specifics for environment
    - name: create empty list environment_list
      set_fact:
        environment_list: []

    - name: set scope to all environment
      set_fact:
        environment_list: "{{ environment_list | default([]) }} + \
        [ '{{ item.key }}' ]"
      with_dict: "{{ environment }}"
      when: item.value.active

    - debug:
        var: environment_list

  roles:
    - role: role_ldap_securitygroup_create
      vars:
        grouptype: 0x80000004  # Local / 0x80000002 for global
Result

The result should be three groups, according to the example used those would be sg_poolAA, sg_poolAB and sg_poolAC

Edit the horizon_view_create role files and playbook

To add the newly created securitygroups as entitlement to the corresponding Horizon View pools we need to edit some files and then add some code.

  • Add code to the powershell module create_hv_desktoppool.ps1
  • Edit the taskfile main.yml
  • Edit the playbook horizon-pool-create.yml
roles/role_horizon_view_create/library/create_hv_desktoppool.ps1

Add some code to check for entitlements and add if not already entitled. The code first checks if the entitlement already exists. If it does not exist, then it will try to create the entitlement with the given sg_group. When it cannot find the specified securitygroup the script will fail.

#!powershell

# Copyright: Davy van de Laar
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# todo:
#   - use credential for vcenter / nsx connection
#   - created functions
#   - add functionality for
#       - checking
#       - absent / present state
#       - additional options
#       - more logic in error logging

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

$ErrorActionPreference = "Stop"

# 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
$hvtemplatepool = Get-AnsibleParam -obj $params -name "hv_template_poolname" -type "str" -failifempty $true
$addentitlement = Get-AnsibleParam -obj $params -name "hv_add_entitlement" -type "list" - element "str"-failifempty $false
$poollist = Get-AnsibleParam -obj $params -name "hv_pool_list" -type "list" -element "str" -failifempty $true

$result = @{
    changed = $false
    exists = @()
    created = @()
    entitled = @()
    entitle_exists =@()
    entitle_error = @()
}

Import-Module VMware.VimAutomation.HorizonView
Get-Module -ListAvailable 'VMware.Hv.Helper' | Import-Module

# make connection with connection server
try{
    $hvserver = Connect-HVServer $hvserver_input -User $hvusername"@"$hvuserdomain -Password $hvpassword
    $Global:services = $hvserver.ExtensionData
    $hvserver
} catch {Fail-Json -obj $result "check credentials en pre-requisites"}


foreach ($pool in $poollist) {
    $poolcheck = Get-HVPool -Poolname $pool.name
    if ( $poolcheck -like '*No Pool Found with given search parameters*' ){
        try {
            Get-HVPool -Poolname $hvtemplatepool | New-HVPool -Poolname $pool.name -NamingPattern $pool.vdi
            Start-Sleep -Seconds 5
            Set-HVPool -PoolName $pool.name -Key automatedDesktopData.vmNamingSettings.patternNamingSettings.maxNumberOfMachines -Value $pool.poolsize
            $result.created += $pool.name + " wordt gemaakt"
            $result.changed = $true
        } catch {Fail-Json -obj $result "onbekende fout bij pool creatie"}
    }
    elseif ( $poolcheck.Base.Name -eq $pool.name){
        $result.exists += $poolcheck.Base.Name + " bestaat al"
    }
}

if ( $null -ne $addentitlement.entitlement ){
    foreach ($entitlementpool in $addentitlement){
        $entitlecheck = (Get-HVEntitlement -ResourceName $entitlementpool.name -ResourceType Desktop).Base.Name
        if ($entitlecheck -eq $entitlementpool.entitlement){
            $result.entitle_exists += "de toewijzing " + $entitlementpool.entitlement + " bestaat al"
        }
        else {
            try{
                $entitleduser = $entitlementpool.entitlement
                $newentitlement = New-HVEntitlement -User $entitleduser'@'$hvuserdomain -ResourceName $entitlementpool.name -Type 'group'
                if ($newentitlement -like '*Unable to find specific user or group with given search parameters*'){
                    $result.entitle_error += "de groep voor entitlement " + $entitlementpool.name + " doe not exist"
                    $result.failed = $true
                }
                else {
                    $newentitlement
                    $result.entitled += $entitlementpool.entitlement + " toegevoegd aan pool " + $entitlementpool.name 
                    $result.changed = $true
                }
            } catch { Fail-Json -obj $result "er gaat iets mis bij het aanmaken van entitlement" + $entitlementpool.name }
        }
    }
}

Exit-Json -obj $result
  • Line 27: added variable for entitlement. It's a non-required list variable
  • Line 66: if the $addentitlement variable is not empty, it will try to add entitlements. If it is empty it does nothing
  • Line 67-71: check if entitlement exists in Horizon View
  • Line 72-79: Check if securitygroup exists. The Horizon HV.Helper tool returns a value on error and that is why I check on the value instead of an error. (Line76). Module fails if securitygroup can't be found
  • Line 80-84: If the securitygroup exists, the entitlement can be added.
roles/role_horizon_view_create/tasks/main.yml

Only small change needed here.

---
- name: create desktop pool
  create_hv_desktoppool:
    hv_server: "{{ your_connection_server }}"
    hv_password: "{{ your_admin_password }}"
    hv_username: "{{ your_admin_user }}"
    hv_domain: "{{ your_horizon_domain }}"
    hv_pool_list: "{{ environment_list }}"
    hv_template_poolname: "{{ your_horizon_template_poolname }}"
    hv_add_entitlement: "{{ environment_list }}"
  when: environment_list|list
  register: results
  delegate_to: "{{ win_powershell_jumphost }}"
  vars:
    ansible_connection: winrm
    ansible_winrm_transport: credssp
    ansible_winrm_server_cert_validation: ignore
    ansible_user: "{{ credssp_ansible_user }}"
    ansible_password: "{{ credssp_ansible_password }}"

- name: debug results for logging
  debug:
    var: results

- name: show message if nothing to do
  debug:
    msg: 'lege lijst van environment'
  when: not environment_list|list
  • Line 10: added <strong>hv_add_entitlement</strong>
horizon-pool-create.yml

To run the playbook it needs a minor change. The entitlement should be added to the query.

---
# file: playbook-horizon-view-environment.yml
- name: Provision horizon desktoppools
  max_fail_percentage: 0
  hosts: all
  connection: local
  gather_facts: false

  vars_files: "vars/pools.yml"

  pre_tasks:
  - name: create empty list environment_list
    set_fact:
      environment_list: []

  - name: set scope to all environment
    set_fact:
      environment_list: "{{ environment_list | default([]) + \
      [ { 'name': 'env_' + item.key, \
      'vdi': 'vdi' + item.key + '{n:fixed=2}', \
      'poolsize': item.value.vdi_pool_custom_size , \
      'entitlement': 'sg_' + item.key } ] }}"
    with_dict: "{{ environment }}"
    when: item.value.active

  - debug:
      var: environment_list
    
  roles:
    - role_horizon_view_create
  • Line 22: added entitlement to the query so it can be passed on to the taskfile
Result

The result should show added entitlements to the corresponding Horizon View pool.

Run the scripts in a flow

It is possible to run the playbooks in a flow. For instance if you need to run the scripts in a specific order. This might be necessary because of dependencies for example. In this case there is a dependency that the securitygroup must exist when adding an entitlement. This is really easy to do with a simple yaml file. For now I call the file site.yml, but that may be anything of your own choice.

---
- import-playbook: ldap-securitygroup-create.yml
- import-playbook: ldap-horizon-pool-create.yml
Run a flow
$ ansible-playbook -i hosts site.yml --limit vcenter_cc

This should result in the creation of three securitygroups who are then added to corresponding Horizon View pools as entitlement

Final Result

The picture below shows the final result. First the proof there was no entitlement. Secondly running the ansible playbook. Then the result in the Horizon View desktop dashboard, it show entitlements now. Finally proof of the actual entitlement with corresponding securitygroup en pool.

Conclusion

To me this is a nice real world example of how to create a simple and scalable flow. In this examples I created and then combined a custom Horizon View module with the Ansible ldap_entry module. This shows how flexible Ansible can be. Setting up the environment and then making sure you have it right with all the variable takes a little time and effort. However, once done you will have an extremely scalable environment which you can create by running just one playbook.

The code can be found on my github:

https://github.com/davyvandelaar/codecrusaders/tree/master/VMware-Horizon-View-with-Ansible-and-Powershell

The previous post can be found here:

VMware Horizon View with Ansible and Powershell – Part 1

Please speak out if you have any questions or remarks.

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments