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/               # ""
<!--NeedCopy-->

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/
<!--NeedCopy-->

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'
<!--NeedCopy-->

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: ""
<!--NeedCopy-->

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"
<!--NeedCopy-->

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
<!--NeedCopy-->

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: ""
<!--NeedCopy-->

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: ""
<!--NeedCopy-->

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
<!--NeedCopy-->

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"
<!--NeedCopy-->