Automate Server Configuration with Ansible Playbooks

Updated by Linode Contributed by Joshua Lyman

Contribute on GitHub

Report an Issue | View File | Edit File

Playbooks define a set of tasks to be executed by Ansible on a group of managed nodes. While you can use Ansible to execute one-off tasks via the command line, Playbooks can be reused, shared across teams, version controlled, and support complex deployment and rollout requirements. You can use features such as, handlers, variables, templates, error handling, and control logic within your Playbooks to intelligently automate your IT processes across a fleet of hosts.

Scope of this Guide

This guide provides an introduction to Ansible Playbook concepts, like tasks, plays, variables, and Jinja templating. In this guide’s examples, you will create Playbooks to automate the following:

  • Creating a limited user account on a Linode
  • Common server setup tasks, like setting a hostname, timezone, and updating system software
  • Installing a LAMP stack

Before You Begin

Playbook Basics

Ansible Playbooks are written using YAML syntax, a declarative language, to describe the tasks or actions to execute on a group of managed nodes. Playbook tasks are run in order from top to bottom. You should design your Playbooks to be idempotent, which means a Playbook can be run once or several times with the same expected result. For example, a Playbook might declare a task to set up a server configuration file using a template and inject declared variable values in the file. In this scenario, Ansible should be able to compare the template configuration file to the actual file on the server and create or update it only if necessary.

Anatomy of a Playbook

The example below displays the skeleton of a Playbook. At its most basic, a Playbook will define a group of target hosts, variables to use within the Playbook, a remote user to execute the tasks as, and a set of named tasks to execute using various Ansible modules. This grouping within a Playbook is referred to as a play and a single Playbook can contain several plays.

Common Ansible Modules
Module Usage
command Executes a command on a remote node.
script Transfers a local script to a managed node and then runs the script on the remote node.
shell Executes a command through a shell (/bin/sh) on a remote node.
template Uses a local file template to create a file on a remote node.
apt Manages apt packages on Debian or Ubuntu systems.
git Deploy software or files from git checkouts.
service Manage services on your remote node’s system. Supports BSD init, OpenRC, SysV, Solaris SMF, systemd, upstart init systems.
Playbook Skeleton
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
---
- hosts: [target hosts]
  vars:
    var1: [value 1]
    var2: [value 2]
  remote_user: [yourname]
  tasks:
    - name: [task 1]
      module:
    - name: [task 2]
      moduel:

The second example Playbook targets all hosts in the marketing_servers group and ensures Apache is started. The task is completed as the webadmin user.

Service Check Playbook
1
2
3
4
5
6
7
8
---
- hosts: [marketing_servers]
  remote_user: webadmin
  tasks:
    - name: Ensure the Apache daemon has started
      service: name=httpd state=started
      become: yes
      become_method: sudo

Web Server Setup with Ansible Playbooks

In this example, you will create three different Playbooks to configure your Linode as a web server running a LAMP stack. You will also configure the Linode to add a limited user account. The Playbooks will provide basic configurations that you can expand on, if needed.

Caution
The Playbooks created in this section are for learning purpose and will not result in a fully hardened or secure server. To further secure your Linode, you can use Ansible’s firewalld module.

Add a Limited User Account

In this section you will create a Playbook to add a limited user account to your Linode.

Create a Password Hash

When creating a limited user account you are required to create a host login password for the new user. Since you should never include plaintext passwords in your Playbooks, in this section you will use the Python passlib library to create a password hash that you can securely include in your Playbook.

Note
Ansible Vault can also be used to encrypt sensitive data. This guide will not make use of Ansible Vault, however, you can consult the How to use the Linode Ansible Module to Deploy Linodes guide to view an example that makes use of this feature.
  1. On your Ansible control node, create a password hash on your control node for Ansible to use in a later step. An easy method is to use Python’s PassLib library, which can be installed with the following commands:

  2. Install pip, the package installer for Python, on your control node if you do not already have it installed:

    sudo apt install python-pip
    
  3. Install the passlib library:

    sudo pip install passlib
    
  4. Create a password hash using passlib. Replace myPlainTextPassword with the password you’d like to use to access your Linode.

    sudo python -c "from passlib.hash import sha512_crypt; print (sha512_crypt.encrypt('myPlainTextPassword'))"
    

    A similar output will appear displaying a hash of your password:

      
    $6$rounds=656000$dwgOSA/I9yQVHIjJ$rSk8VmlZSlzig7tEwIN/tkT1rqyLQp/S/cD08dlbYctPjdC9ioSp1ykFtSKgLmAnzWVM9T3dTinrz5IeH41/K1
        
    

  5. Copy and paste the hash somewhere that you can easily access for a later step.

Disable Host Key Checking

Ansible uses the sshpass helper program for SSH authentication. This program is included by default on Ansible 2.8. sshpass requires host key checking to be disabled on your Ansible control node.

  1. Disable host key checking. Open the /etc/ansible/ansible.cfg configuration file in a text editor of your choice, uncomment the following line, and save your changes.

    /etc/ansible/ansible.cfg
    1
    
    #host_key_checking = False

Create the Inventory File

In order to target your Linode in a Playbook, you will need to add it to your Ansible control node’s inventory file.

  1. Edit your inventory file to create the webserver group and to add your Linode to the group. Open the /etc/ansible/hosts file in your preferred text editor and add the following information. Replace 192.0.2.0 with your Linode’s IP address.

    /etc/ansible/hosts
    1
    2
    
    [webserver]
    192.0.2.0

Create the Limited User Account Playbook

You are now ready to create the Limited User Account Playbook. This Playbook will create a new user on your Linode, add your Ansible control node’s SSH public key to the Linode, and add the new user to the Linode’s sudoers file.

  1. In your home directory, create a file named limited_user_account.yml and add the contents of the example. Replace the following values in the file:

    • yourusername with the user name you would like to create on the Linode
    • $6$rounds=656000$W.dSl with the password hash you create in the Create a Password Hash section of the guide.

      limited_user_account.yml
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      ---
      - hosts: webserver
        remote_user: root
        vars:
          NORMAL_USER_NAME: 'yourusername'
        tasks:
          - name: "Create a secondary, non-root user"
            user: name={{ NORMAL_USER_NAME }}
                  password='$6$rounds=656000$W.dSl'
                  shell=/bin/bash
          - name: Add remote authorized key to allow future passwordless logins
            authorized_key: user={{ NORMAL_USER_NAME }} key="{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
          - name: Add normal user to sudoers
            lineinfile: dest=/etc/sudoers
                        regexp="{{ NORMAL_USER_NAME }} ALL"
                        line="{{ NORMAL_USER_NAME }} ALL=(ALL) ALL"
                        state=present
      
          
    • The first two lines of the file tells Ansible to target the webserver group of hosts in the inventory file and to execute the remote host tasks as the root user.

    • The vars section creates the NORMAL_USER_NAME that can be reused throughout the Playbook. Ansible also allows you to create and use variables in separate files, instead of directly in your Playbook. For a deeper dive into the many ways you can use variables with Ansible, see Ansible’s official documentation on Using Variables.

    • The tasks block of the Playbook, declares three tasks. The first creates the new user and a user password. The second, adds the Ansible control node’s public SSH key to the Linode. The third task adds the new user to the sudoers file.

    • Each task makes use of Jinja templating, (i.e. {{ NORMAL_USER_NAME }}), to access the referenced variable values. Jinja templating is a powerful feature of Ansible that gives you access to control logic, filters, lookups, and functions within your Playbooks. To learn more, consult Ansible’s official documentation.

  2. Run the limited_user_account.yml Playbook. The --ask-pass option tells Ansible to log into the Linode using password authentication, instead of SSH, since your public SSH key is not yet stored there. The -u root option directs Ansible to log in as the root user. By default, Ansible will use your current local system’s username of one is not provided.

    ansible-playbook --ask-pass -u root limited_user_account.yml
    

    You should see output from Ansible that reports that the three tasks all completed successfully with a status of “changed.” We can now work with new playbooks using our limited user account and keys.

Configure the Base System

This next Playbook will take care of some common server setup tasks, such as setting the timezone, updating the hosts file, and updating packages.

  1. Create a file in your home directory named common_server_setup.yml and add the contents of the example. Replace the following values in the file:

    • yourusername with the username you created in the Create the Limited User Account Playbook section of the guide
    • web01 with the hostname you would like to set for your Linode.
    • If you have a domain name you would like to set up, replace www.example.com with it.

      common_server_setup.yml
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      
      ---
      - hosts: webserver
        remote_user: yourusername
        become: yes
        become_method: sudo
        vars:
          LOCAL_HOSTNAME: 'web01'
          LOCAL_FQDN_NAME: 'www.example.com'
        tasks:
          - name: Set the timezone for the server to be UTC
            command: ln -sf /usr/share/zoneinfo/UTC /etc/localtime
          - name: Set up a unique hostname
            hostname: name={{ LOCAL_HOSTNAME }}
          - name: Add the server's domain to the hosts file
            lineinfile: dest=/etc/hosts
                        regexp='.*{{ item }}$'
                        line="{{ hostvars[item].ansible_default_ipv4.address }} {{ LOCAL_FQDN_NAME }} {{ LOCAL_HOSTNAME }}"
                        state=present
            when: hostvars[item].ansible_default_ipv4.address is defined
            with_items: "{{ groups['linode'] }}"
          - name: Update packages
            apt: update_cache=yes upgrade=dist
                
      • The first task in this Playbook uses the command module to set the Linode’s timezone to UTC time.
      • The second task uses the hostname module to set your system’s hostname.
      • The third task updates the Linode’s host file using the lineinfile module. This task uses hostvars to retrieve the host’s IP address and use it to update the hosts file. hostvars is a reserved special variable that you can use to access various information about your hosts.
      • The fourth task updates your system’s packages using the apt module.
  2. Run the common_server_setup.yml Playbook. The --ask-become-pass tells Ansible to ask you for the limited user account’s password in order to become the sudo user and execute the Playbook via the limited user account.

    Note
    By default, Ansible will use your current local system’s username to authenticate to your Linode. If your local username is not the same as your Linode’s limited user account name, you will need to pass the -u option along with the limited user account name to appropriately authenticate. Ensure you replace limitedUserAccountName with the limited user account name you created in the Create the Limited User Account Playbook section of the guide.
    ansible-playbook common_server_setup.yml --ask-become-pass -u limitedUserAccountName
    
    • When the Playbook begins to execute, you will be prompted to enter a BECOME password:. This is the password you created in the Create a Password Hash section of the guide.
    • As the Playbook executes, you will again see the tasks display as “changed.”
    • Updating packages may take a few minutes.

Install the Stack

You are now ready to create the setup_webserver.yml Playbook that will get your Linode set up with Apache, PHP, and a test MySQL database.

  1. Follow the steps in the Create a Password Hash section of the guide to create a new password hash to use in this Playbook.

  2. Create a file in your home directory named setup_webserver.yml and add the contents of the example. Replace the following values in the file:

    • yourusername with the username you created in the Create the Limited User Account Playbook section of the guide
    • In the Create a new user for connections task, replace the value of password with your desired password.

      Note
      In order to avoid using plain text passwords in your Playbooks, you can use Ansible-Vault and variables to encrypt sensitive data. You can consult the How to use the Linode Ansible Module to Deploy Linodes guide to view an example that makes use of this feature.
      setup_webserver.yml
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      
      ---
      - hosts: webserver
        remote_user: yourusername
        become: yes
        become_method: sudo
        tasks:
          - name: "Install Apache, MySQL, and PHP"
            apt:
              pkg:
                - apache2
                - mysql-server
                - python-mysqldb
                - php
                - php-pear
                - php-mysql
              state: present
      
          - name: "Turn on Apache and MySQL and set them to run on boot"
            service: name={{ item }} state=started enabled=yes
            with_items:
              - apache2
              - mysql
      
          - name: Create a test database
            mysql_db: name= testDb
                      state= present
      
          - name: Create a new user for connections
            mysql_user: name=webapp
                        password='$6$rounds=656000$W.dSl'
                        priv=*.*:ALL state=present
      
            
    • The first task handles installing Apache, MySQL, and PHP.

    • The next task ensures that Apache and MySQL remaining running after a system reboot. This task makes use of a loop to populate the value of the service name.

    • Next, the Playbook creates a MySQL database with the name of testDB

    • Finally, a new MySQL user named webapp is created.

  3. Run the playbook from your control machine with the following command:

    ansible-playbook setup_webserver.yml --ask-become-pass
    
  4. When this playbook finishes executing, visit your Linode’s IP address or FQDN to see the default Ubuntu Apache index page.

  5. Log in to the Linode you just configured via SSH and check to see that the testDb has indeed been created:

     sudo mysql -u webapp -p
     show databases;
    
  6. If desired, you can even create a sample PHP page and place it in /var/www/html to test that PHP is active on the server.

Next Steps

Now that you are familiar with Playbooks, you can continue to learn more about Ansible. Ansible’s GitHub provides several example Playbooks that you can reference to learn different implementation options and patterns. Below are a few topics you can explore to learn how to build Playbooks of more complexity:

Join our Community

Find answers, ask questions, and help others.

This guide is published under a CC BY-ND 4.0 license.