Ansible 101: Facts & Templates
Tags: ansible, devops, automation, docker
Published: 2024-01-29 12:36:16

In Ansible, variables and facts, along with templates, are foundational tools for creating flexible automation workflows. Variables allow you to manage and change your configurations dynamically. Facts are a special subset of variables that Ansible gathers from the remote systems, providing context-specific information. Templates enable the generation of variable-driven configuration files, making your playbooks adaptable to varied environments and scenarios.

These tools make playbooks reusable and adaptable, allowing you to avoid hard-coding values and enabling customization for different environments.

### Variables & Facts

Variables allow for parameters to be modified without altering the playbook's core logic.


![Diagram showing how variables, facts and templates work together](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xfjr9qfh0f9aj28tg43.png)



  

* **Types of Variables:**
    
    * **Boolean:** True or False values.
        
    * **List:** An ordered collection of items.
        
    * **Dictionary:** Key-value pairs for complex data structures.
        
    * **Registered Variables:** Captures the output of tasks to use later in your playbook.
        
    * **Facts:** Auto-collected variables that provide details about the remote systems you are managing.
        

  
**NB:** Avoid conflicts in variable names by using bracket notation.

```yaml
- name: Print the distribution of the target
  hosts: all
  vars:
   curr_time: "{{ now() }}"
  tasks:
   - name: Distro Check
     ansible.builtin.debug:
      msg: "The target system is {{ ansible_facts['distribution'] }}. Timestamp: {{ curr_time }}"
      
```

### Templates & Files

Templates in Ansible use the Jinja2 templating language to dynamically create files using variable interpolation, loops, and conditionals.

```yaml
- name: Write distro name
  hosts: all
  tasks:
   - name: Write distro name
     ansible.builtin.template:
      src: distro.j2
      dest: /root/distro.txt
      mode: '644'

# src: location of jinja2 template file
# dest: location it will be copied to
# permissions that will be granted to the file
```

## Practice

We're going to use the OS Family to determine whether to install NGINX of Lighttpd then we'll deploy a custom homepage to the remote host containing NGINX all without hardcoding hostnames.

1. Clone the repo
    

```bash
git clone https://github.com/perplexedyawdie/ansible-learn.git
```

2\. Spin up the environment using docker-compose

```bash
docker compose up -d --build
```

3\. SSH into the Ansible server

```bash
ssh -o StrictHostKeyChecking=no -o NoHostAuthenticationForLocalhost=yes root@localhost -p 2200# password: test123
```

4\. Change directory to `ansible_learn`

```bash
cd ansible_learn
```

### Variables & facts

5\. Create a playbook called `server_setup.yaml`. Here, we'll setup NGINX & Lighttpd then output the name of the distro for each remote host

```yaml
- name: Install NGINX on Debian & Lighttpd on RedHat
  hosts: all
  vars:
   dev1: "Debian"
   dev2: "RedHat"
  tasks:
   - name: Install NGINX for Debian-based systems   
     ansible.builtin.apt:
      name: nginx
      state: present
     when: ansible_facts['os_family'] == dev1       

   - name: Install Lighttpd for RedHat-based systems 
     ansible.builtin.yum:
      name: lighttpd
      state: present
     when: ansible_facts['os_family'] == dev2       

   - name: Display the distribution
     ansible.builtin.debug:
      msg: "The server is running {{ ansible_facts['distribution'] }}"
```

6\. Run `ansible-lint`

```bash
ansible-lint server_setup.yaml
```

7\. Run the playbook

```bash
ansible-playbook --key-file /root/.ssh/id_rsa_ansible -u root -i inventory.yaml server_setup.yaml
```

8\. Confirm that setup was successful

```bash
ssh -i /root/.ssh/id_rsa_ansible root@server3 nginx -V

ssh -i /root/.ssh/id_rsa_ansible root@server2 lighttpd -v
ssh -i /root/.ssh/id_rsa_ansible root@server1 lighttpd -v
```

### Templates & Files

9\. Create a Jinja2 template file called `index.html.j2`

It will get auto populated with the OS Family & Distribution.

```Html
<html>
<head>
  <title>Welcome to {{ ansible_facts['os_family'] }}</title>
</head>
<body>
  <h1>Server running on {{ ansible_facts['distribution'] }}</h1>
</body>
</html>
```

10\. Create a playbook called `custom_homepage.yaml`

We're deploying the custom homepage created above to NGINX then restarting the server.

```yaml
- name: Deploy Custom Homepage and restart
  hosts: all
  vars:
   dev1: "Debian"
   dev2: "RedHat"
  tasks:
   - name: Create Homepage with Jinja2 Template for NGINX
     ansible.builtin.template:
      src: index.html.j2
      dest: /var/www/html/index.html
      mode: '644'
     when: ansible_facts['os_family'] == dev1
     notify: restart nginx

  handlers:
   - name: Restart NGINX
     listen: "restart nginx"
     ansible.builtin.service:
      name: nginx
      state: restarted
     when: ansible_facts['os_family'] == dev1
```

11\. Run the linter

```bash
ansible-lint custom_homepage.yaml

```

12\. Run the playbook

```bash

ansible-playbook --key-file /root/.ssh/id_rsa_ansible -u root -i inventory.yaml custom_homepage.yaml
```

13\. Confirm deployment by visiting [`http://localhost:2203`](http://localhost:2203) in your browser.

## Recap

Awesome effort! 🙌 We've learned how to use variables & facts in a playbook along with how to create dynamic files using templates. Next we'll look at modularization and error handling. Til then, take care!