Chapter 4: Managing Variables and Facts

Exercise: Managing variables and facts

Put the whole chapter together: a play whose every name and path is a variable, a web page built from facts, and a test play that logs in with a password kept in a Vault-encrypted file. Try each task yourself before opening the solution.

A developer wants a web server on serverb.lab.example.com (in the webserver group) that protects its site with HTTP basic authentication. The files directory of ~/data-review already contains:

Use these play variables:

VariableValue
firewall_pkgfirewalld
firewall_svcfirewalld
web_pkghttpd
web_svchttpd
ssl_pkgmod_ssl
httpdconf_srcfiles/httpd.conf
httpdconf_dest/etc/httpd/conf/httpd.conf
htaccess_srcfiles/.htaccess
secrets_dir/etc/httpd/secrets
secrets_srcfiles/htpasswd
secrets_dest"{{ secrets_dir }}/htpasswd"
web_root/var/www/html

Notice that secrets_dest is built from another variable.

Managing variables and facts

  • Define variables and use facts in a playbook, and use variables defined in an encrypted file.

Prerequisites: Complete control-node setup and the preceding lessons in this chapter. Run commands as student on workstation unless a step names another machine.

From the project directory, run the relevant home-lab check. For an exercise with several checkpoints, grade each state before changing or removing it:

lab grade data-review

The grader reads project files and host state. A passing report covers the listed checks; also perform the task’s independent connection, repeat-run, and reboot checks where requested.

Try an incorrect guest password in the verification play. Explain the HTTP response and restore the correct secret.

Record the published result first. The grader checks the original requirements, so a changed name or value may intentionally fail those checks.

Deliver an authenticated HTTPS page on serverb from playbook.yml, using the variables and supplied configuration files described above.

  • Install the web, firewall, and TLS packages. Apache and the firewall must run and start at boot; HTTPS must be allowed persistently.
  • Configuration files must be root-owned, mode 0644. The secrets directory must be apache-owned, mode 0500; its password file and the web-root .htaccess must be apache-owned, mode 0400.
  • The index page must identify the host's FQDN and default IPv4 address using facts.
  • A workstation play must retrieve it as guest using web_pass from encrypted vars/secret.yml (practice password redhat). The lab's self-signed certificate is expected.

files/httpd.conf is Rocky's stock Apache configuration with one change: AllowOverride AuthConfig for the document root, so that .htaccess can require a login. files/htpasswd is generated on your workstation with one user, guest, password redhat.

Start the play and its variables

In ~/data-review, create playbook.yml with a play named install and configure webserver with basic auth for the webserver group, and define all the variables from the table.

---
- name: install and configure webserver with basic auth
  hosts: webserver
  vars:
    firewall_pkg: firewalld
    firewall_svc: firewalld
    web_pkg: httpd
    web_svc: httpd
    ssl_pkg: mod_ssl
    httpdconf_src: files/httpd.conf
    httpdconf_dest: /etc/httpd/conf/httpd.conf
    htaccess_src: files/.htaccess
    secrets_dir: /etc/httpd/secrets
    secrets_src: files/htpasswd
    secrets_dest: "{{ secrets_dir }}/htpasswd"
    web_root: /var/www/html

Install the packages

Add tasks and one task that installs the latest firewall_pkg, web_pkg and ssl_pkg.

  tasks:
    - name: latest version of necessary packages installed
      ansible.builtin.dnf:
        name:
          - "{{ firewall_pkg }}"
          - "{{ web_pkg }}"
          - "{{ ssl_pkg }}"
        state: latest

Deploy the Apache configuration

Copy httpdconf_src to httpdconf_dest, owned by root:root, mode 0644.

    - name: configure web service
      ansible.builtin.copy:
        src: "{{ httpdconf_src }}"
        dest: "{{ httpdconf_dest }}"
        owner: root
        group: root
        mode: 0644

Create the secrets directory

Use ansible.builtin.file to create secrets_dir, owned by apache:apache, mode 0500.

    - name: secrets directory exists
      ansible.builtin.file:
        path: "{{ secrets_dir }}"
        state: directory
        owner: apache
        group: apache
        mode: 0500

Install the password file

Copy secrets_src to secrets_dest, owned by apache:apache, mode 0400.

    - name: htpasswd file exists
      ansible.builtin.copy:
        src: "{{ secrets_src }}"
        dest: "{{ secrets_dest }}"
        owner: apache
        group: apache
        mode: 0400

Install .htaccess in the document root

Copy htaccess_src to {{ web_root }}/.htaccess, owned by apache:apache, mode 0400.

    - name: .htaccess file installed in docroot
      ansible.builtin.copy:
        src: "{{ htaccess_src }}"
        dest: "{{ web_root }}/.htaccess"
        owner: apache
        group: apache
        mode: 0400

Build index.html from facts

Create {{ web_root }}/index.html with the content HOSTNAME (IPADDRESS) has been customized by Ansible., where HOSTNAME is the host's FQDN and IPADDRESS its default IPv4 address. Use ansible.builtin.copy with content:.

    - name: create index.html
      ansible.builtin.copy:
        content: "{{ ansible_facts['fqdn'] }} ({{ ansible_facts['default_ipv4']['address'] }}) has been customized by Ansible.\n"
        dest: "{{ web_root }}/index.html"

Start the firewall, open HTTPS, start Apache

Three tasks: enable and start firewall_svc; permanently and immediately allow the https service in firewalld; enable and start web_svc.

    - name: firewall service enabled and started
      ansible.builtin.service:
        name: "{{ firewall_svc }}"
        state: started
        enabled: true

    - name: open the port for the web server
      ansible.posix.firewalld:
        service: https
        state: enabled
        immediate: true
        permanent: true

    - name: web service enabled and started
      ansible.builtin.service:
        name: "{{ web_svc }}"
        state: started
        enabled: true

Add a test play that uses a secret

Add a second play, test web server with basic auth, on workstation, without privilege escalation. Give it a play variable web_user: guest, and load vars/secret.yml with vars_files (you create that file next). Its tasks:

  1. request https://serverb.lab.example.com with ansible.builtin.uri, using basic authentication as web_user / web_pass, expecting status 200, and register the result as auth_test. serverb's certificate is not trusted, so turn off certificate validation;
  2. print auth_test.content.
- name: test web server with basic auth
  hosts: workstation
  become: false
  vars:
    web_user: guest
  vars_files:
    - vars/secret.yml
  tasks:
    - name: connect to web server with basic auth
      ansible.builtin.uri:
        url: https://serverb.lab.example.com
        validate_certs: false
        force_basic_auth: true
        user: "{{ web_user }}"
        password: "{{ web_pass }}"
        return_content: true
        status_code: 200
      register: auth_test

    - ansible.builtin.debug:
        var: auth_test.content

Create the encrypted secret

Create vars/secret.yml with Ansible Vault, password redhat, containing web_pass: redhat.

[student@workstation data-review]$ mkdir vars
[student@workstation data-review]$ ansible-vault create vars/secret.yml
New Vault password: redhat
Confirm New Vault password: redhat
web_pass: redhat

Check the syntax, then run

[student@workstation data-review]$ ansible-navigator run -m stdout \
> --playbook-artifact-enable false \
> playbook.yml --syntax-check --vault-id @prompt
Vault password (default): redhat

playbook: /home/student/data-review/playbook.yml
[student@workstation data-review]$ ansible-navigator run -m stdout \
> --playbook-artifact-enable false \
> playbook.yml --vault-id @prompt
Vault password (default): redhat
...output omitted...
TASK [ansible.builtin.debug] ***************************************************
ok: [workstation] => {
    "auth_test.content": "serverb.lab.example.com (172.25.250.11) has been customized by Ansible.\n"
}

PLAY RECAP *********************************************************************
serverb.lab.example.com    : ok=10   changed=8    unreachable=0    failed=0  ...
workstation                : ok=3    changed=0    unreachable=0    failed=0  ...

The content returned over HTTPS, after authenticating with the vaulted password, is the page you built from serverb's facts.

Grade and finish

Check your work with lab grade data-review, then put the exercise away with lab finish data-review.