Ansible

Infrastructure as a code? What?

2025-07-25
linuxDevops

Ansible

I decided to take a look at some common and most popular devops tools out there to see what kind of issue do they solve (besides Docker, because i'm currently familiar with that).

After some research, i found that Ansible is a pretty good starting point in this journey.

Ansible is an open-source automation engine used for IT tasks such as configuration management, application deployment. It simplifies infrastructure management by allowing admins to describe their desired state and execute them on one or multiple machines. This concept is actually called infrastructure as code. The idea is, besides your actual software that you want to deploy, you should also write your desired infrastructure state as a code. So it can automatically and (quite) easily created again from ground up. In general, when you are taking some kind of devops responsibility, you basically try to automate every process like this as you can.

Ansible is actually fairly easy to work with. I found it pretty intuitive and easy to pick up and use.

First let's see what are some essential concepts we need to know in order to use Ansible:

Inventory

An Ansible inventory is a collection of managed hosts that Ansible targets for automation and configuration management tasks. It defines the "who" of your automation. Listing the servers, network devices, or other systems that Ansible will interact with are defined here.

Example:

[homelab]
10.0.0.30 ansible_port=2222
10.0.0.31 ansible_port=2222
10.0.0.32 ansible_port=2222

[cloud]
188.65.100.54 ansible_port=22

Here i define remote machines that i want to use as a target of my ansible tasks. As you can see, we can separate them by grouping. We can these aliases later.

Also i define the ansible_port variable for them. ansible_port is actually a special variable that ansible uses by default for initiating the ssh connection to remote.

Playbook

A playbook is a collection of tasks that you use to confugure remote servers. And it's utilizes YAML file format for defining a playbook logic.

Example:

---
- name: Intro to Ansible Playbooks
  hosts: homelab # here you can specify remote machines by their alias, or pointing to all of them using "all" keyword
  become: yes
  become_method: sudo
  remote_user: ubuntu
  tasks:
    - name: Copy file hosts with permissions
      ansible.builtin.copy:
        src: ./hosts
        dest: /tmp/hosts_backup
        mode: '0644'

    - name: Add the user 'bob'
      ansible.builtin.user:
        name: bob

    - name: Upgrade all apt packages
      apt:
        force_apt_get: yes
        upgrade: dist
        become: yes

We can break up the playbook into different sections. Let's start with the header. In the header you set things like the name of the playbook, the hosts to specify targets, the remote_user and become and become_method for configuring the privilege escelation method.

These header fields are mostly boilerplate so let's get familiar with them really fast:

  1. name: Simply, the intent of the playbook! write something meaningful here so debugging it can be easier later.
  2. hosts: You will select target hosts from inventory here. It is also possible to use all as a hosts for applying to all of them.
  3. become: some operations on the host require special privileges, so ansible allows you to become to another user with privileges.
  4. become_method: Which privilege escalation method should be used. It works in conjunction with become. Essentially, it determines how you become another user with elevated privileges, such as sudo, su, or others.
  5. remote_user: specifies the username that Ansible uses to connect to the remote target machines via SSH. This is the user account under which Ansible will log in and execute commands on the remote system.

The next section tasks is where you list and define all of your tasks that you want to be performed. think of it as an instruction manual that is exectued one by one.

- name: Copy file hosts with permissions
      ansible.builtin.copy:
        src: ./hosts
        dest: /tmp/hosts_backup
        mode: '0644'
  1. name: The intent of the tasks. useful for debugging and readability in general.
  2. module: Ansible tasks are usually performed by leveraging ansible modules. For most of the tasks that you want to perform, there is exist a module for that; whether a built in module or community module. here we want to copy a file from our local machine to remote machine. And there is a built in module exist for this usecase named copy (docs).
  3. parameters: you provide the params and the module simply does its job. I think the example above is pretty much self explanatory. reading the documentation would be helpful to see which parameter are required and which are optional.

Execute

After installing ansible, run the playbook by using the command below :

ansible-playbook -i inventory.ini my-playbook.yaml

Here, -i stands for inventory. And as a first parameter, you simply pass the playbook that you wrote.

By default, Ansible prints a human-readable summary of play and task execution, including task names, host names, and status indicators (e.g., changed, ok, failed, skipped). For example the status ok means there is nothing to do usually because the desired state is currently exists! (Idempotence)

To be continued...

In the next post, we will write some powerful and useful playbooks aimed at hardening our hosts.