A beginner guide to Ansible

Almabud Shohan
Brain Station 23
Published in
9 min readFeb 3, 2023

Before dive into ansible we have to know what is ansible. So, let’s start.

Ansible is an open-source automation platform. It does configuration management, application deployment, along with infrastructure orchestration. Moreover, it is procedural rather than declarative. In ansible, we define what we want to do and ansible goes through each and every step for that.

Now, we are ready to jump into the feature of ansible.

Features of Ansible:

  • Ansible is an open source Configuration management, Deployment & Orchestration tool.
  • Its modules are written in Python. Wait, don’t get panic. You don’t need to know Python to use Ansible, though.
  • The good news is the process of installation & setup is pretty much simple and very easy to learn. That means no need to learn extra languages like puppet.
  • Also, it is a pull-based configuration management tool. That means there is no need to install any agents or client software on the targeted host like the push base tools eg. puppet. It will automatically copy Python code using SSH and run from there. Yes, the only requirement is Python.
  • Last but not the least, extensible/developer modules can be written in any language, but most of the available guides is assuming you are using Python.

Okay, then what next? Umm… I think before going into depth, it’s necessary to know about the architecture of Ansible.

Architechture of Ansible:

Ansible has the following 3 major parts.

  • Config: ansible.cfg file is the file where all ansible configuration is kept. Ansible searches the ansible.cfg file in the following places:
    - ANSIBLE_CONFIG (an environment variable, set a path of ansible.cfg file)
    - ansible.cfg (in the directory where the ansible command will be run)
    - .ansible.cfg (in the home directory root)
    - /etc/ansible/ansible.cfg

Note: If you can not see ansible.cfg file in the above location then don’t get panic. Just create a file mentioned above, the Ansible will find the file automatically. Here is an example of ansible.cfg

  • Inventory: This is the file where all hosts or host groups are kept. Inventory can be written in many formats but most common is INI and YML. This file contains the list of IP addresses of the host machines. The inventory file location is set in ansible.cfg file.
  • Playbook: This is the place where all the tasks or configurations are kept. This describes the entire work-follow of the system. This section is divided into the following 3 parts.
    - Modules: It is referred to as “task plugins” or “library plugins”. Modules are discrete units of code that can be used from the command line or in a playbook task. Ansible executes each module on the remote nodes as per the task description.
    - APIs: APIs are not meant for direct consumption. They are just there to support the command line tools. For example, they have the python API and these APIs can also be used for transport to the cloud services whether it’s public or private.

Note: APIs are under construction. You can see the documentation for more details.

- Plugins: These are the special kind of module that augments Ansible’s core functionality with logic and features that are accessible to all modules. Ansible collections include a number of handy plugins (eg. cache plugins, action plugins, callback plugins, etc), and you can easily write your own module. You can find plugin details here.

Ansible architechture

Note: Ansible stores all configuration details in CMDB (Configuration Management Database)

Okay, now forget about the thoracal stuff and dive into the installation process.

Installation process:

We can install Ansible in two ways with the package manager and with pip.

I will describe the method using pip as installation via package manager will be different in different os. I assume you have set up python correctly.

  • Install Ansible using pip.
pip3 install ansible

If you wanna install the ansible in a virtual environment then activate the specific environment.

  • Now, verify the Ansible installation.
ansible --version
Output

Inventory Creation:

  • As we installed it using pip we don’t have the ansible.cfg file. So we need to create an ansible.cfg file. We can create this file in 3 places as I described above. I just created an ansible.cfg file in the /etc/ansible/ansible.cfg .
  • Now, let’s copy or download a default configuration file from the GitHub repo. Here is ansible documentation for the config file. Here is the GitHub repo where you can find the example of ansible.cfg. Now in the configuration file, you can find inventory under the [defaults]. If it is started with # then remove it and give the path of your inventory file(/etc/ansible/hosts)
ansibe.cfg
  • Now create an inventory file /ect/ansible/hosts. There are several file formats but here I am using YAML format.
  • Edit the /ect/ansible/hosts and change your information using the following inventory file structure.
<group_name>:
hosts:
<host_1>:
ansible_host: <host_1_address>
# For passwordless ssh just remove the
# ansible_user and ansible_password
ansible_user: <host_1_user>
ansible_password: <host_1_password>
<host_2>:
ansible_host: <host_2_address>
ansible_user: <host_2_user>
ansible_password: <host_2_password>
all:
hosts:
<ungroup_host_1>:
ansible_host: <ungroup_host_1_address>
ansible_user: <ungroup_host_1_user>
ansible_password: <ungroup_host_1_password>
children:
<group_name>:
  • Several formats are given below:
<group_name_1>:
hosts:
<host_1_address>
<host_2_address>
all:
hosts:
<ungroup_host_1>:
ansible_host: <ungroup_host_1_address>
ansible_user: <ungroup_host_1_user>
ansible_password: <ungroup_host_1_password>
children:
<group_name_1>:
<group_name_2>:

# Or

<group_name_1>:
hosts:
<host_1_address>
<host_2_address>
all:
hosts:
<ungroup_host_1_address>
<ungroup_host_2_address>
children:
<group_name_1>:
<group_name_2>:

# Or
<group_name>:
hosts:
<host_1>:
ansible_host: <host_1_address>
<host_2>:
ansible_host: <host_2_address>
all:
hosts:
<ungroup_host_1_address>
<ungroup_host_2_address>
children:
<group_name>:
  • Here, groupe_name is a variable for grouping all hosts. Which needs to execute the same task.
  • Here ansible_host defines the host address, ansible_user defines the user of that host, and ansible_password defines the password of that host.

Note: If you gonna use ssh with the password then you need to install a package named sshpass.

  • Verify the inventory file.
ansible-inventory --list
Output
  • Now, you can ping your servers. Let’s try the above setup.
ansible <group_name> -m ping
# you can use group_name or specific server by their variable. eg. host_1.
# ansible host_1 -m ping
Output

Playbook Creation:

The playbook has the following 6 parts.

  • Playbook starts with 3 dashes at the top of the file( — -)
  • Name of the playbook
  • HOSTS:
    -
    Here we need to mention the hosts by giving a list of hosts in which we need to run the playbook or tasks.
  • VARIABLES:
    -
    Here we can mention the list of variables by gathering the facts (if needed)
  • TASKS:
    -
    Here we have to mention all the tasks that need to be executed.
    - The tasks will execute in the order they have been written
  • HANDLERS:
    -
    Handlers are different kinds of tasks.
    - The handlers will execute if and only if it is triggered in the tasks (you can say these are call-back tasks).
    - Handlers run after all task has been executed(if you want to run synchronously use — meta: flush_handlers . You can find an example here.
    - Handlers will execute only when any task report changes state.
Playbook
  • Now create a playbook for installing the apache server on an ubuntu server
---
- name: Install apache server playbook
hosts:
vmservers

tasks:
- name: stop firewalld
service:
name: firewalld
state: stopped

- name: install httpd
dnf:
name: httpd
state: latest
update_cache: yes
notify:
- start httpd

handlers:
- name: start httpd
service:
name: httpd
state: started
  • name: is the name of the task
  • service, dnf is a built-in module. You can find a lot of built-in modules/plugins.

Note: We stop the firewall service for the simplicity of this playbook and never used this in production.

  • We’ve created our first playbook. Let’s verify the playbook.
ansible-playbook <playbook_path>/<playbook_file_name>.yml --syntax-check
  • If anything goes wrong this command will raise errors.
  • Run the playbook
ansible-playbook <playbook_path>/<playbook_file_name>.yml
Output

Declare variables in the playbook:

  • Defining a variable in the playbook.
- hosts: <host_name>
vars:
app_path: {{ base_path }}/22

# Accessing the variable using jinga2 template engine syntax.
{{ app_path }}
  • Here you can see that vars: are holding all variables.
  • You can access the variable using jinga2 template engine syntax.
  • Create a list and dict variable
- hosts: <host_name>
vars:
region:
- northeast
- southeast
- midwest
foo:
field1: one
field2: two

# Use the variable like this
region: "{{ region[0] }}"

# For accessing dict variable
foo['field1']
# Or
foo.feild1

Register variable for holding the task's output:

  • You can create variables for holding the output of an Ansible task with the task keyword register. You can use registered variables in any later tasks in your playbook. For example:
- hosts: <hosts_name>
tasks:
- name: Run a shell command and register its output as a variable
ansible.builtin.shell: /usr/bin/foo
register: foo_result
ignore_errors: true

- name: Run a shell command using output of the previous task
ansible.builtin.shell: /usr/bin/bar
when: foo_result.rc == 5
  • Here when keywords are used for conditional logic. That means the last task will execute only when the condition is getting true.
  • You can output any variable in the terminal where ansible is run this way.
 - debug:
var: find_output
  • So, the above playbook debug will look like this.
- hosts: <hosts_name>
tasks:
- name: Run a shell command and register its output as a variable
ansible.builtin.shell: pwd
register: pwd_result
ignore_errors: true

- debug:
var: pwd_result

- name: Run a shell command using output of the previous task
ansible.builtin.shell: pwd
when: pwd_result.rc == 5
Output
  • Here you can see that all output is printed. But you can select a specific variable from the output eg.
 - debug:
var: pwd_result.stdout
Output
  • You can access the variable in the playbook(eg. other tasks) like this
{{ find_output["stdout"] }}
# neste variables can be loaded in this way find_outpur[][][]
  • You can use jinja template engine for filtering the output of a variable.
  • You can create variables anywhere like inventory, console, reusable files, etc. ansible will load every possible variable it finds.

Ansible Template:

Sometimes we need to copy or make the configuration file with different values for each node server(dynamically). In that case, we need an ansible template. Ansible template allows us to define text files with variables instead of static values and then replace those variables automatically at playbook runtime.

Now, the question is what does an Ansible template look like?: An Ansible template is a text file built with the Jinja2 templating language with a j2 file extension.

  • Creating a template file name: app2.conf.j2
 template_host = "{{ template_host }}"
template_uid = "{{ template_uid }}"
template_path = "{{ template_path }}"
template_fullpath = "{{ template_fullpath }}"
template_run_date = "{{ template_run_date }}"
  • For coping the app2.conf.j2 file in the node server we will use the template module of Ansible. Because the copy module is not feasible to copy the .j2 file.
---
- hosts: <hosts_name>
- name: Ansible file permission example
remote_user: ubuntu
tasks:
- name: Create the app.conf configuration file and assign permissions
template:
src: "~/ansible_template_demo/app.conf.j2"
dest: "/etc/app.conf"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: 0644 ## OR mode: u=rw, g=w,o=r
become: true

Conclusion

And that’s it for this tutorial. For, further study you can read about Ansible Role. This article is based on my personal learning and experience. If any information is wrong then just comment, I’ll update it.

Thanks for reading the content. If you like the content then, please support it with claps. Cheers.

Reference:

--

--