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:
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.