Getting Started with Ansible for Linux Bootstrapping

3 minute read

What is Ansible

Ansible is an Infrastructure as Code (IaC) tool used to provision and manage resources. The benefit of IaC becomes evident when you realize it allows you to write a single source of truth in the form of configuration files that you can reuse. Configuration files you can put under version control and allow for collaboration across teams.

Back in the old days, system administrators usually kept a collection of bash scripts to repeat typical system configuration changes when deploying new nodes. This wasn’t easy to maintain, and bash can be difficult to read and comes with weird syntax quirks.

Ansible configuration files are largely expressed in YAML format, which is a common syntax when dealing with configuration for modern admin tools and frameworks.


I’m going to start with the concept of a Playbook within Ansible. When you’re working with Ansible, you want to start with a Playbook, which will contain plays that will execute in order from top to bottom the goals of your specific Playbook. These plays include tasks that also run in order from top to bottom.

To better explain these core concepts, I’m going to discuss concepts while moving along a playbook I am developing with the goal of bootstrapping a Linux virtual machine.

# Initial server setup
- hosts: testhost # Host which is defined in hosts.yaml file
  vars: # Used to define variables.
    tmzone: America/Chicago # Timezone to be used in bootstrap role
    sudo_timeout: 20 # Used in bootstrap role 
    # variable using a Literal Block Scalar 
    vimrc: | 
      set mouse-=a
    - bootstrap
    - {role: jnv.unattended-upgrades, ansible_become: yes}

After the variables are defined, we get to the meat of the Playbook. We mentioned that Playbooks include what are considered plays that perform tasks. For our particular Playbook, we’re utilizing Roles, which package up tasks along with contained variables, files, and handlers.

This makes tasks and consequently plays, portable and function almost as modules. The first role is called bootstrap, and it’s the core of the project. It follows a basic Role directory structure and is the code that does the heavy lifting. This code is in my ansible-bootstrap GitHub that I will be hacking away at in the next few weeks, or months…

The next role is a little different because it’s a 3rd part package that handles the specific function of configuring unattended-upgrades on your remote host. There are different ways to go about installing this external role, but one method is making use of Ansible Galaxy. Another important note is that the role is in brackets and I redefine a role that is already defined as a child to the roles dictionary. This is because I like to run my playbook as an unprivileged user and become root only when it’s needed. In this case, all of the tasks within the unattended-upgrades role require root permissions. It’s encapsulated between the brackets to use ansible_become only for that specific role.

Hosts file

The next file host.ymlis an important configuration that declares the systems we intend to run our playbook against.

    ansible_python_interpreter: /usr/bin/python3
    ansible_become_method: sudo 
          user: user 
          cfg_static_network: false

This is a fairly straightforward file that defines a few variables relating to host connections, setting python v3 on the hosts and specifying the use of sudo for escalating privileges.

SSH Config

testhost is not only the name of our remote host in Ansible, it’s also inconspicuously the name of the host in our ~/.ssh/config file for ssh_config client configuration..

So for example, this section is in my config file:

Host testhost 
	User user 
	Port 22 
	IdentityFile ~/.ssh/myHost.pem

Note that Host attribute is the same exact name as the YAML-dictionary name directly under the hosts YAML dictionary. This is the simplest method to maintain connection details for our remote hosts, just use the built-in functionality of OpenSSH since we’re already using its client for remote connections.

Next, we just need to run the playbook, assuming we have installed both roles bootstrap & jnv.unattended-upgrades.

[~/ansible-bootstrap]$ ansible-playbook debian.yml

If the connection is established, you should see Ansible going through our plays and making changes on your host(s).

Conclusion, and more…

This is a nice and short dive into some of the basic functionality. The GitHub will have the bulk of the code, and over the course of time, I’ll go over specific segments.

Of course, for future reading: