Linux Virtual Delivery Agent

Manage your deployment using Ansible

Ansible helps to automate the process of deploying applications, configurations, and updates across your deployment. This article offers step-by-step instructions on using Ansible for managing your deployment with efficiency.

Step 1: Determine what to deploy

Before you start, identify what you need to deploy, such as applications, services, configurations, and environment variables.

Step 2: Set up your Ansible project

Create a directory structure for your Ansible project. One crucial way to organize your playbook content is Ansible’s “roles” feature. For more information, see Roles in the Ansible documentation.

The following are two example directory structures for your reference:

Example directory structure #1

production # inventory file for production servers staging # inventory file for staging environment group_vars/ group1.yml # here we assign variables to particular groups group2.yml host_vars/ hostname1.yml # here we assign variables to particular systems hostname2.yml library/ # if any custom modules, put them here (optional) module_utils/ # if any custom module_utils to support modules, put them here (optional) filter_plugins/ # if any custom filter plugins, put them here (optional) site.yml # master playbook webservers.yml # playbook for webserver tier dbservers.yml # playbook for dbserver tier roles/ common/ # this hierarchy represents a "role" tasks/ # main.yml # <-- tasks file can include smaller files if warranted handlers/ # main.yml # <-- handlers file templates/ # <-- files for use with the template resource ntp.conf.j2 # <------- templates end in .j2 files/ # bar.txt # <-- files for use with the copy resource foo.sh # <-- script files for use with the script resource vars/ # main.yml # <-- variables associated with this role defaults/ # main.yml # <-- default lower priority variables for this role meta/ # main.yml # <-- role dependencies library/ # roles can also include custom modules module_utils/ # roles can also include custom module_utils lookup_plugins/ # or other types of plugins, like lookup in this case webtier/ # same kind of structure as "common" was above, done for the webtier role monitoring/ # "" fooapp/ # ""

Example directory structure #2

inventories/ production/ hosts # inventory file for production servers group_vars/ group1.yml # here we assign variables to particular groups group2.yml host_vars/ hostname1.yml # here we assign variables to particular systems hostname2.yml staging/ hosts # inventory file for staging environment group_vars/ group1.yml # here we assign variables to particular groups group2.yml host_vars/ stagehost1.yml # here we assign variables to particular systems stagehost2.yml library/ module_utils/ filter_plugins/ site.yml webservers.yml dbservers.yml roles/ common/ webtier/ monitoring/ fooapp/

Step 3: Configure your inventory

Define your inventory file (inventory.ini). An inventory file typically lists the hosts that you want to manage by using Ansible, along with necessary details such as host names, IP addresses, and group memberships. For example:

# Hostname and ip address [UBUNTU2004] <ip address> [UBUNTU2204] <ip address> [RHEL8] <ip address> [RHEL9] <ip address> [DEBIAN11] <ip address> [DEBIAN12] <ip address> [SUSE15] <ip address> [all:children] UBUNTU2004 UBUNTU2204 RHEL8 RHEL9 DEBIAN11 DEBIAN12 SUSE15 [all:vars] ansible_user=<ansible execute user e.g root> ansible_password=<> ansible_ssh_common_args='-o StrictHostKeyChecking=no'

Step 4: Create Ansible playbooks

Create playbooks (.yml files) to automate deployment tasks. The section provides example playbooks for automating different deployment tasks.

Example playbook for patching Linux distributions

To patch various Linux distributions using Ansible, you can create a YAML playbook file named patch-for-different-distribution.yml and populate the file with tasks similar to the following. The hosts directive defines the target hosts from the inventory (inventory.ini in this context) that are targeted to execute the playbook tasks.

- name: Upgrade and Reboot RHEL & Debian family Linux distros hosts: <host1,host2,host3> # replace with your actual hosts in the inventory file. vars: reboot_connect_timeout: 5 reboot_post_reboot_delay: 15 reboot_timeout: 600 tasks: # Upgrade RHEL family OS packages - name: Upgrade RHEL Family OS packages ansible.builtin.yum: name: '*' state: latest when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "7" # Upgrade RHEL family OS packages - name: Upgrade RHEL Family OS packages ansible.builtin.yum: name: '*' state: latest when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "8" # Upgrade RHEL family OS packages - name: Upgrade RHEL Family OS packages ansible.builtin.yum: name: '*' state: latest when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "9" # Ubuntu Family upgrade - name: Update repositories cache apt: update_cache: yes when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "20" - name: Update all packages to their latest version apt: name: "*" state: latest when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "22" # Debian Family upgrade - name: Upgrade the OS (apt-get dist-upgrade) apt: upgrade: dist when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "11" - name: Upgrade the OS (apt-get dist-upgrade) apt: upgrade: dist when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "12" # Reboot after upgrade - name: Reboot host reboot: connect_timeout: "" post_reboot_delay: "" reboot_timeout: ""

Example playbook for installing .Net environments

The following example playbook installs different versions of .Net environments on the specific Linux distributions.

- name: Install dotnet runtime environment on Linux distros hosts: <host1,host2,host3> # replace with your actual hosts in the inventory file. tasks: # Install dotnet runtime environment on RHEL7 - name: Enable the rhel-7-server-dotnet-rpms repository command: subscription-manager repos --enable=rhel-7-server-dotnet-rpms when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "7" - name: Install dotnet runtime environment on RHEL7 ansible.builtin.yum: name: rh-dotnet60-aspnetcore-runtime-6.0 state: present when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "7" - name: Remove /usr/bin/dotnet if it exists file: path: /usr/bin/dotnet state: absent - name: Create a symbolic link file: src: /opt/rh/rh-dotnet60/root/usr/lib64/dotnet/dotnet dest: /usr/bin/dotnet state: link # RHEL8 linux vda install dotnet runtime environment - name: Install dotnet-runtime-8.0 ansible.builtin.dnf: name: dotnet-runtime-8.0 state: present when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "8" - name: Install aspnetcore-runtime-8.0 ansible.builtin.dnf: name: aspnetcore-runtime-8.0 state: present when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "8" # RHEL9 linux vda install dotnet runtime environment - name: Install dotnet-runtime-8.0 ansible.builtin.dnf: name: dotnet-runtime-8.0 state: present when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "9" - name: Install aspnetcore-runtime-8.0 ansible.builtin.dnf: name: aspnetcore-runtime-8.0 state: present when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "9" # Ubuntu20.04 linux vda install dotnet runtime environment - name: Register Microsoft key and feed shell: | wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb dpkg -i packages-microsoft-prod.deb rm packages-microsoft-prod.deb when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "20" - name: Install dotnet-runtime-8.0 ansible.builtin.apt: name: dotnet-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "20" - name: Install aspnetcore-runtime-8.0 ansible.builtin.apt: name: aspnetcore-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "20" # Ubuntu22.04 linux vda install dotnet runtime environment - name: Install dotnet-runtime-8.0 ansible.builtin.apt: name: dotnet-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "22" - name: Install aspnetcore-runtime-8.0 ansible.builtin.apt: name: aspnetcore-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "22" # Debian11 linux vda install dotnet runtime environment - name: Register Microsoft key and feed shell: | wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb dpkg -i packages-microsoft-prod.deb rm packages-microsoft-prod.deb when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "11" - name: Install dotnet-runtime-8.0 ansible.builtin.apt: name: dotnet-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "11" - name: Install aspnetcore-runtime-8.0 ansible.builtin.apt: name: aspnetcore-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "11" # Debian12 linux vda install dotnet runtime environment - name: Register Microsoft key and feed shell: | wget https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb dpkg -i packages-microsoft-prod.deb rm packages-microsoft-prod.deb when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "12" - name: Install dotnet-runtime-8.0 ansible.builtin.apt: name: dotnet-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "12" - name: Install aspnetcore-runtime-8.0 ansible.builtin.apt: name: aspnetcore-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "12" # Sles15 linux vda install dotnet runtime environment - name: Register Microsoft key and feed shell: | sudo rpm -Uvh https://packages.microsoft.com/config/sles/15/packages-microsoft-prod.rpm sudo ln -s /etc/yum.repos.d/microsoft-prod.repo /etc/zypp/repos.d/microsoft-prod.repo when: - ansible_facts['distribution'] == "SLES" - ansible_facts['distribution_major_version'] == "15" - name: Install dotnet-runtime-8.0 community.general.zypper: name: dotnet-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "SLES" - ansible_facts['distribution_major_version'] == "15" - name: Install aspnetcore-runtime-8.0 community.general.zypper: name: aspnetcore-runtime-8.0 state: present update_cache: yes when: - ansible_facts['distribution'] == "SLES" - ansible_facts['distribution_major_version'] == "15" # Amazon2 linux vda install dotnet runtime environment - name: Install dotnet-runtime-8.0 ansible.builtin.yum: name: dotnet-runtime-8.0 state: present when: - ansible_facts['distribution'] == "Amazon" - ansible_facts['distribution_major_version'] == "2" - name: Install aspnetcore-runtime-8.0 ansible.builtin.yum: name: aspnetcore-runtime-8.0 state: present when: - ansible_facts['distribution'] == "Amazon" - ansible_facts['distribution_major_version'] == "2"

Example playbooks for upgrading the Linux VDA

To automate the Linux VDA upgrades using Ansible, you can create two separate playbooks. One playbook, such as get_the_build.yml, is dedicated to downloading and transferring the Linux VDA package to the target machines (hosts). The other playbook, for example linux_upgrade.yml, contains tasks designed to upgrade the Linux VDA on the target machines using the previously downloaded package.

Example playbook get_the_build.yml

- hosts: localhost name: Get the latest release build to local vars: build_url: <linux vda download link> # replace with your actual value. local_tmp: "/tmp/" # replace with your actual value. remote_tmp: "/tmp/" # replace with your actual value. linuxvda_file_name : "linux vda rpm/deb file name" # replace with your actual value. tasks: - name: Download the file get_url: url: "" dest: "" tags: - get - hosts: <host1,host2,host3> # replace with your actual hosts in the inventory file. name: Copy a file to remote location tasks: - name: Copy vda to the remote machine ansible.builtin.copy: src: "" dest: "" remote_src: no tags: - copy

Example playbook linux_upgrade.yml

- name: Upgrade Linux VDA and Reboot RHEL & Debian Linux distros hosts: <host1,host2,host3> # replace with your actual hosts in the inventory file. vars: remote_tmp: "/path/to/remote/tmp" # replace with your actual path rhel7_file_name: "rhel7_file.rpm" # replace with your actual file name rhel8_file_name: "rhel8_file.rpm" # replace with your actual file name rhel9_file_name: "rhel9_file.rpm" # replace with your actual file name ubuntu2004_file_name: "ubuntu2004_file.deb" # replace with your actual file name ubuntu2204_file_name: "ubuntu2204_file.deb" # replace with your actual file name debian11_file_name: "debian11_file.deb" # replace with your actual file name debian12_file_name: "debian12_file.deb" # replace with your actual file name suse15_file_name: "suse15_file.deb" # replace with your actual file name amazon2_file_name: "amazon2_file.rpm" # replace with your actual file name tasks: # Upgrade RHEL linux vda packages - name: Upgrade RHEL7 linux vda packages ansible.builtin.yum: name: "" state: present when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "7" # Upgrade RHEL linux vda packages - name: Upgrade RHEL8 linux vda packages ansible.builtin.yum: name: "" state: present when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "8" # Upgrade RHEL linux vda packages - name: Upgrade RHEL9 linux vda packages ansible.builtin.yum: name: "" state: present when: - ansible_facts['distribution'] == "RedHat" - ansible_facts['distribution_major_version'] == "9" # Ubuntu20.04 linux vda upgrade - name: Ubuntu20.04 linux vda upgrade ansible.builtin.apt: deb: "" when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "20" - name: Ubuntu22.04 linux vda upgrade ansible.builtin.apt: deb: "" when: - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "22" # Debian Linux VDA upgrade - name: Debian11 Linux VDA upgrade ansible.builtin.apt: deb: "" when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "11" - name: Debian12 Linux VDA upgrade ansible.builtin.apt: deb: "" when: - ansible_facts['distribution'] == "Debian" - ansible_facts['distribution_major_version'] == "12" # Sles15 Linux VDA upgrade - name: Sles15 Linux VDA upgrade community.general.zypper: name: "" state: present when: - ansible_facts['distribution'] == "SLES" - ansible_facts['distribution_major_version'] == "15" # Amazon2 Linux VDA upgrade - name: Amazon2 Linux VDA upgrade ansible.builtin.yum: name: "" when: - ansible_facts['distribution'] == "Amazon" - ansible_facts['distribution_major_version'] == "2" # Reboot after upgrade - name: Reboot host reboot: connect_timeout: "" post_reboot_delay: "" reboot_timeout: ""

Example playbook for mounting a Network File System (NFS) server as the home directory

The following example playbook mounts an NFS server as the home directory on the target hosts:

- hosts: <host1,host2,host3> # replace with your actual hosts in the inventory file. vars: nfs_server = <nfsserver ip address> # replace with your actual values mount_points = /home/<domain realm>/user1,/home/<domain realm>user2 # replace with your actual values nfs_shares = user1,user2 # replace with your actual values owners = user1,user2 # replace with your actual values groups = group1,group2 # replace with your actual values tasks: - name: Enable NFS as home directory ansible.builtin.command: cmd: "/opt/Citrix/VDA/bin/ctxreg create -k 'HKLM\\System\\CurrentControlSet\\Control\\Citrix' -t 'REG_DWORD' -v 'CheckUserHomeMountPoint' -d '0x00000001' --force" register: result failed_when: result.rc != 0 check_mode: no - name: Mount NFS shares ansible.builtin.mount: path: "" src: ":" fstype: nfs opts: rw,nolock state: mounted loop: "" - name: Set owner, group and mode for NFS client paths ansible.builtin.file: path: "" owner: "" group: "" mode: "" loop: ""

Example playbooks for remote command execution

Example playbook for modifying registry settings

- hosts: <host1,host2,host3> # replace with your actual hosts in the inventory file. vars: registry_key: "your_registry_key" # E.g. registry_key = HKLM\System\CurrentControlSet\Control\Terminal Server\Wds\icawd registry_type: "your_registry_type" # E.g. registry_type = REG_DWORD registry_value: "your_registry_value" # E.g. registry_value = AdaptiveScalingEnabled registry_data: "your_registry_data" # E.g. registry_data = 0x00000000 tasks: - name: Execute AdaptiveScaling redirection script ansible.builtin.command: cmd: "/opt/Citrix/VDA/bin/ctxreg create -k \"\" -t \"\" -v \"\" -d \"\" --force" register: result failed_when: result.rc != 0 check_mode: no

Example playbook for locking the RHEL minor version

- hosts: <host1,host2,host3> # replace with your actual hosts in the inventory file. vars: rhel_minor_version: "9.3" # replace with your actual minor version such as 9.3, 8.8 tasks: - name: Lock system to a specific minor version ansible.builtin.command: cmd: "subscription-manager release --set=" register: result failed_when: "'Error' in result.stderr"