Filling in the blanks: Linux VDI with VMware Horizon

So, we all know that Windows is popular on the desktop right? But it’s also expensive and power users are increasingly coming to expect Linux as an option. Now, VMware has offered support for Linux desktops on Horizon VDI for some time. This comes in the form of a Horizon View agent that can be installed on Red Hat and Debian derived distributions and by using vSphere Customization Specifications for these distributions which support changing the hostname and network config of cloned VMs.

BUT this means that you will need to setup each desktop one by one and manually add them to the VDI server instead of letting them be cloned automatically like with Windows.

Now, I should say that I don’t run VDI full-time but I look after a few environments in our labs so I thought I’d give it a shot.

However, and this was a big ‘however’ the instructions were practically non-existent. For instance on the often thorny and complex task of joining Linux systems to Windows AD domains the VMware guide had the following to say:

Right…. So how are we supposed to do this? Because it looks like the only real suggestion is to embed the password to an Active Directory account into every single Linux image we make.

Maybe I can use OpenLDAP as they suggest instead? Sounds neater doesn’t it.

Actually no. Using OpenLDAP for passthrough with nss-ldap is a legacy route which presents swathes of arcane options to the user and is riddled with the type of opaque documentation that made Linux famous in the Good Old Days. Plus, once you’ve created this eggshell of interwoven config files there is every chance that the next time you upgrade your system it will fall to pieces.

So, we have two bad options neither of which is really explained properly and nowhere to go? Wrong. We have Google, determination and a good knowledge of scripting and automation. Let’s fix this!

First stop is to find a reliable means of authenticating to AD without hand-crafting 99 different config files. Enter the System Services Security Daemon or SSSD from the good folks at Red Hat, a multi-purpose tool for managing exactly this problem, built with modern use cases like Active Directory in mind and with simple and straightforward configuration and documentation. This is progress already.

Second stop is to build an actual desktop image. Download a recent CentOS 7 ISO, install a VM with the defaults including the Gnome Desktop Environment and snapshot that VM. Leave the hostname set to default but add a generic local admin account (as well as root). We have the first layer of our image.

Next stop. Automation. Now I’ve compared and contrasted these previously. We need a system which can detect new clients and move these clients closer to a desired state. Sounds like a job for Puppet! If you haven’t already done so I suggest you setup a simple Puppet server with the DNS set to puppet.yourdomain.com. Clients will automatically look for a puppet hostname on their local domain by default.

If you haven’t already setup a Puppet server here’s a brief walk-through:

  1. Install Ubuntu (16.04 is recommended as the most recently Long Term Support release)
  2. Set a static IP and a DNS entry using puppet.<yourdomain>
  3. Install puppet open source edition using the excellent docs at Puppet.com
  4. Add the SSSD module “puppet module install walkamongus-realmd
  5. Edit /etc/puppetlabs/puppet/puppet.conf and under [master] add the line “autosign = /etc/puppetlabs/puppet/autosign.conf”
  6. Create the file /etc/puppetlabs/puppet/autosign.conf with the content “*.<yourdomain>”
  7. Now restart puppet “service puppetserver restart”

We have just setup Puppet to automatically setup certificates with any clients that contact it from our local domain. Currently there are no instructions so it won’t actually send any commands to the clients. So let’s fix that.

Back on the Puppet server make a new text file /etc/puppetlabs/code/environments/production/manifests/vdi-linux.pp:

node /^vdi[A-Za-z\d_-]+\.yoursubdommain\.yourdomain\.com$/ {
include ntp
 class { '::realmd':
domain => $::domain,
domain_join_user => 'account_used_to_join_domain',
domain_join_password => 'password_used_to_join_domain',
manage_sssd_config => true,
sssd_config => {
'sssd' => {
'domains' => $::domain,
'config_file_version' => '2',
'services' => 'nss,pam',
},
"domain/${::domain}" => {
'ad_domain' => $::domain,
'krb5_realm' => upcase($::domain),
'realmd_tags' => 'manages-system joined-with-adcli',
'cache_credentials' => 'True',
'id_provider' => 'ad',
'access_provider' => 'ad',
'krb5_store_password_if_offline' => 'True',
'default_shell' => '/bin/bash',
'ldap_id_mapping' => 'True',
'fallback_homedir' => '/home/%u',
'case_sensitive' => 'False',
},
},
}
 include horizon_view_agent
}

At the start of the file your can see a regular expression, which is used to select which machines will run this configuration. Here I have chosen only machines whose names start with “vdi” and are in the domain yoursubdomain.yourdomain.com. Edit that as needed.

Now create some new folders:

mkdir -p /etc/puppetlabs/code/environments/production/modules/horizon_view_agent/manifests
mkdir -p /etc/puppetlabs/code/environments/production/modules/horizon_view_agent/files

Into the files directory, copy your Horizon View Agent tar.gz file onto your home directory on the Puppet server using WinSCP, then move it using the root account into the new location

/etc/puppetlabs/code/environments/production/modules/horizon_view_agent/files

Once you have done this create a new file /etc/puppetlabs/code/environments/production/modules/horizon_view_agent/manifests/init.pp

class horizon_view_agent {
  exec {'join_domain_test':
command => '/usr/sbin/realm list | grep int.vcevlab.com',
before => File['/tmp//VMware-horizonagent-linux-x86_64-7.1.0-5141896.tar.gz']
}
  file { '/tmp//VMware-horizonagent-linux-x86_64-7.1.0-5141896.tar.gz':
source => 'puppet:///modules/horizon_view_agent/VMware-horizonagent-linux-x86_64-7.1.0-5141896.tar.gz',
before => Exec['unpack_file'],
require => Exec['join_domain_test']
}
  exec {'unpack_file':
onlyif => '/usr/bin/test -f /tmp/VMware-horizonagent-linux-x86_64-7.1.0-5141896.tar.gz',
cwd => '/tmp',
command => '/usr/bin/tar -zxf /tmp/VMware-horizonagent-linux-x86_64-7.1.0-5141896.tar.gz',
before => Exec['install_agent'],
require => File['/tmp//VMware-horizonagent-linux-x86_64-7.1.0-5141896.tar.gz']
}
  exec {'install_agent':
onlyif => '/usr/bin/test -f /tmp/VMware-horizonagent-linux-x86_64-7.1.0-5141896/install_viewagent.sh',
cwd => '/tmp/VMware-horizonagent-linux-x86_64-7.1.0-5141896',
command => '/tmp/VMware-horizonagent-linux-x86_64-7.1.0-5141896/install_viewagent.sh -A yes',
before => Exec['cleanup_files'],
require => Exec['unpack_file'],
}
  exec {'cleanup_files':
cwd => '/tmp',
command => '/usr/bin/rm -rf /tmp/VMware-horizonagent-linux-x86_64-7.1.0-5141896*'
}
}

I’ve hard-coded the file names here, so if you have used a different version of the view agent just change those names.

Once you have created the server part you will also need to install the Puppet agent on your desktop VM, again following the steps in the Puppet install guide but DO NOT START THE SERVICE, just stop after installing the package. Instead just enable the service but do not start if, so that it will run on the next boot. “systemctl enable pxp-agent.service” Now take a clone of your VM to a new template in vSphere.

Take the new template and clone a VM, using a customization spec to set DNS etc and change the hostname to one starting with “vdi” as we used at the top of the first file, if you haven’t already saved a customization spec go through the wizard and remember to save it for later in the last step.

Power on your new clone and wait for it to boot. You may need to wait a while but you should if you look in Active Directory Users and Computers you should see a new Computer object appear with this hostname. Login to your clone if it doesn’t work and run “sudo /opt/puppetlabs/bin/puppet agent -t — no-daemonize” Look at the output, chances are that you have a spelling mistake somewhere in your config.

Once you have the domain joining working you can do ahead and create a pool in Horizon View. I’d recommend sticking to full clones as there are extra complications for linked clones and storage is cheap these days, especially when you can easily setup a Linux VM on 16GB of disk.

There you have it. Linux VDI. Now your Power Users can stop bugging you!