Updated on 25.5.2023

How to automate Cloud Server provisioning using Ansible

Ansible is an agentless automation tool that makes provisioning Cloud Servers remotely quick and easy. Designed for multi-tier deployments, Ansible takes care of configuration management, application deployment, intra-service orchestration, and many other IT needs.

Managing one system at a time is also a thing of the past. Using Ansible, you can model your cloud infrastructure by simply describing how your systems relate to one another. It requires no agents nor additional custom security infrastructure by employing the trusted SSH protocol by default.

In this tutorial, we’ll explain the principal workings of Ansible and how to get started configuring your cloud infrastructure. Ansible is available for many operating systems capable of running Python. Therefore, the steps in the guide should work for just about any Linux distribution as well as other operating systems with minor adaptations.

How Ansible works

The goal of using Ansible is often to install and configure software required by your deployment. Whether this is done on a single server or a global cluster, Ansible works by connecting to your nodes and pushing out small programs, called “Ansible modules”.

Ansible modules are written to model the desired state of system resources. Ansible maintains a large library of common modules which can take care of the majority of provisioning popular software. However, users are more than welcome to write their own modules as needed and your own library of modules can reside on any machine. There are no servers, daemons, or requirements for databases.

Once connected to the target system, Ansible executes the modules to bring the system to the desired state. Afterwards, it runs a cleanup to remove the used modules leaving you with a clean installation.

Furthermore, Ansible makes getting started really simple by requiring nothing more than your favourite terminal program and a text editor. Additional tools such as version control systems can also be useful for keeping track of changes to your configurations.

What are Playbooks

Building on its modularity, Ansible allows you to define tasks and processes in easy-to-read configuration files call Playbooks. In the simplest terms, Ansible Playbooks describe your automation jobs using YAML. The simple markdown language makes the configurations easy to write in plain English without complex syntax or special features. The point of the simplicity of the language is to allow you to come back to old code even years later and be able to instantly understand it.

Playbooks provide you with fine-grain control for orchestrating your cloud infrastructure as needed. Each module can target any number of servers giving you total control of how many machines to tackle at the time. This is where Ansible starts to get even more interesting.

If Ansible modules are the tools in your workshop, playbooks are your instruction manuals, and your inventory of hosts are your raw material.

Below is a simple example of how Playbooks can define different ways to organise target systems for operations according to your inventory.

---
- hosts: webservers
serial: 5 # update 5 machines at a time
roles:
- common
- webapp

- hosts: content_servers
roles:
- common
- content

Check out docs.ansible.com for complete documentation on all that’s possible.

Installing Ansible

From the user’s point of view, Ansible is a simple command-line tool. As such, it can be installed on just about any machine. The easiest is to just run it on your own preferred computer be it a laptop or a home server.

Ansible is available from a number of popular package managers for specific operating systems. However, for the most universal install method, we are going to use Pip with Python.

If pip is not already available on your system, run the commands below to install it. Note that you do need Python installed first. You can likely find it on your operating system’s native package manager.

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
sudo python get-pip.py

Once the pip is installed, you can install Ansible.

sudo python -m pip install ansible

That’s all you need to run Ansible, no databases or daemons are required. Ansible can manage an entire fleet of Cloud Servers from a single point of control.

Creating your Playbook

Now that you have Ansible installed, you can continue with creating your first playbook. Below is an example of a simple LAMP stack installation on an Ubuntu 20.04 Cloud Server. When executed, it runs the following operations:

  1. Update and upgrade all packages on the system.
  2. Install the latest versions of the required software: Apache2, MariaDB. PHP and PHP-MySQL
  3. Enable and start the Apache2 web server
  4. Enable and start the MariaDB database service
  5. Fetch a test PHP index page from a remote resource
  6. Confirm that the website is accessible

Create a new file called lamp-stack.yml and add the below configuration to it.

---
- name: Ubuntu 20.04 LAMP stack
  user: root
  hosts: all
  become: yes
  tasks:
    - name: Update repository list and cache
      apt: update_cache=yes cache_valid_time=3600
      
    - name: Upgrade all packages on the Cloud Server
      apt: upgrade=yes
      
    - name: Install the latest versions of each component
      apt:
        name:
          - apache2
          - mariadb-server
          - php
          - php-mysql
        state: latest

    - name: Check that apache2 is enabled and running
      service:
        name: apache2
        enabled: true
        state: started

    - name: Check that mariadb is enabled and running
      service:
        name: mariadb
        enabled: true
        state: started

    - name: Copy the php test page from remote
      get_url:
        url: "https://www.middlewareinventory.com/index.php"
        dest: /var/www/html/index.php
        mode: 0644

    - name: Confirm that the web server is working
      uri:
        url: http://{{ansible_hostname}}/index.php
        status_code: 200

Playbooks can also contain multiple plays and target any number of Cloud Servers. You could easily split the database to its own server in addition to this web server. This way you can quickly provision both with a single command.

Running your Playbook

With a playbook configured, you are then almost ready to start provisioning. However, you still need to tell Ansible how to find your Cloud Servers. This is done using a list or lists known as the inventory.

The default location Ansible searches for inventory list is /etc/ansible/hosts. However, this is only convenient if you have a single inventory list.

If you are working with multiple lists, you can specify the inventory file at the command line using the -i {/path/to/file} option.

For example, create an inventory file called hosts.yml and add the following contents.

webserver:
  hosts:
    {domain or IP address}
  vars:
    ansible_user: "root"
    ansible_ssh_private_key_file: "/path/to/ssh-key"

Replace the {domain or IP address} with the public IP of your Cloud Server. You can also set host-specific variables in the inventory file like the username and SSH key in the example above.

Once your inventory is defined, you can use the group names to select the hosts or groups you want Ansible to run against.

For example, test out the connection by using the ping module by targeting your webserver.

ansible webserver -i hosts.yml -m ping
ansible.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

If the connection worked correctly, you should see an output similar to the above example.

You are then ready to run the playbook on your Cloud Server. Use the command below to install the LAMP on your web server.

ansible-playbook -i hosts.yml lamp-stack.yml

When run, you should see an output like the example below.

PLAY [Installing LAMP stack on Ubuntu 20.04] *********************************

TASK [Gathering Facts] *******************************************************
ok: [ansible.ezample.xyz]

TASK [Update repository list and cache] **************************************
changed: [ansible.ezample.xyz]

TASK [Upgrade all packages on the Cloud Server] ******************************
changed: [ansible.ezample.xyz]

TASK [Install the latest versions of each component] *************************
changed: [ansible.ezample.xyz]

TASK [Check that apache2 is enabled and running] *****************************
ok: [ansible.ezample.xyz]

TASK [Check that mariadb is enabled and running] *****************************
ok: [ansible.ezample.xyz]

TASK [Copy the php test page from remote] ************************************
changed: [ansible.ezample.xyz]

TASK [Confirm that the web server is working] ********************************
ok: [ansible.ezample.xyz]

PLAY RECAP *******************************************************************
: ok=8 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

If you didn’t include connection credentials in your inventory file, you can specify them at the command line.

ansible-playbook -i hosts.yml -u root --private-key '/path/to/ssh-private-key' lamp-stack.yml

It’s also possible to run commands targeting specific servers without an inventory file by defining the domain or IP address on the command line.

ansible-playbook -i '{target-public-ip},' -u root --private-key '/path/to/ssh-private-key' lamp-stack.yml

This can be useful when integrating Ansible with other cloud infrastructure management tools.

Integrating Ansible with Terraform

Ansible is a great tool for provisioning your Cloud Servers once deployed. However, you first need to get the servers up and running. For this purpose, look no further than Terraform.

Terraform is a popular open-source infrastructure-as-code software tool that allows you to define infrastructure as code. It uses simple, human-readable language to safely and predictably manage cloud infrastructure by codifying APIs into declarative configuration files. Once the infrastructure has been deployed, Terraform can call on Ansible to provision the new Cloud Servers as required.

Check out our guide on how to get started with Terraform if you haven’t used it before.

How to get started with Terraform

Running Ansible together with Terraform enables you to provision Cloud Servers directly after deployment. It allows you to make resources usable predictably, repeatably and much faster than configuring anything manually. It also enables easier maintenance and troubleshooting, thanks to the identical deployment steps which can eliminate human error.

For example, you can use the code below in your Terraform configuration to have Ansible provision the new deployment.

# SSH connection and authentication
connection {
  host        = self.network_interface[0].ip_address
  type        = "ssh"
  user        = "root"
  private_key = file("/path/to/ssh-private-key")
}

# Wait until the Cloud Server is availble
provisioner "remote-exec" {
  inline = ["echo 'Cloud Server ready!'"]
}

# Provision the Cloud Server using Ansible
provisioner "local-exec" {
  command = <<-EOT
     ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook 
     -u root 
     -i '${self.network_interface[0].ip_address},' 
     --private-key '/path/to/ssh-private-key' 
     your-playbook.yml
  EOT
}

The above example includes three sections:

  • Connection details to use the public IP address of the Cloud Server, username, and the private SSH key counterpart of a public key the Cloud Server was deployed with to authenticate the connection.
  • A remote execution provisioner is used to ensure the Cloud Server is reachable over SSH before continuing with the provisioning.
  • The local execution provisioner then calls Ansible to run the requested playbook.

With the required configurations in place, you can deploy and provision a new Cloud Server from start to finish with a single command.

terraform apply

Then kick back and let the automation take care of your entire deployment.

upcloud_server.server: Creating...
upcloud_server.server: Still creating... [10s elapsed]
...
upcloud_server.server (remote-exec): Cloud Server ready!
upcloud_server.server: Provisioning with 'local-exec'...
upcloud_server.server (local-exec): Executing: ["/bin/sh" "-c" "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook n-u root n-i '94.237.49.179,' n--private-key '~/.ssh/terraform_rsa' nlamp-stack.ymln"]
upcloud_server.server (local-exec): PLAY [Installing LAMP stack on Ubuntu 20.04] ***********************************
upcloud_server.server (local-exec): TASK [Gathering Facts] *********************************************************
upcloud_server.server (local-exec): ok: [94.237.49.179]
upcloud_server.server (local-exec): TASK [Update repository list and cache] ****************************************
upcloud_server.server: Still creating... [1m0s elapsed]
upcloud_server.server (local-exec): changed: [94.237.49.179]
upcloud_server.server (local-exec): TASK [Upgrade all packages on the Cloud Server] ********************************
upcloud_server.server (local-exec): changed: [94.237.49.179]
upcloud_server.server (local-exec): TASK [Install the latest versions of each component] ***************************
...
upcloud_server.server: Still creating... [3m20s elapsed]
upcloud_server.server: Still creating... [3m30s elapsed]
upcloud_server.server: Still creating... [3m40s elapsed]
upcloud_server.server (local-exec): changed: [94.237.49.179]
upcloud_server.server (local-exec): TASK [Check that apache2 is enabled and running] *******************************
upcloud_server.server (local-exec): ok: [94.237.49.179]
upcloud_server.server (local-exec): TASK [Check that mariadb is enabled and running] *******************************
upcloud_server.server (local-exec): ok: [94.237.49.179]
upcloud_server.server (local-exec): TASK [Copy the php test page from remote] **************************************
upcloud_server.server (local-exec): changed: [94.237.49.179]
upcloud_server.server (local-exec): TASK [Confirm that the web server is working] **********************************
upcloud_server.server (local-exec): ok: [94.237.49.179]
upcloud_server.server (local-exec): PLAY RECAP *********************************************************************
upcloud_server.server (local-exec): 94.237.49.179              : ok=8    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

upcloud_server.server: Creation complete after 3m50s [id=00cfc56b-06bb-4269-8a3b-dca2a34cd916]

Once done, you should see an output like the above showing the provisioning was completed successfully.

Summary

Congratulations! You should now have all the components in place to automate your Cloud Server deployment and provisioning. Ansible together with Terraform makes light work of managing even the largest cloud infrastructure saving you time and money.

Now that you’ve gotten started, you might want to check out some other playbook examples over at Ansible’s GitHub repository. Also, if you’d like to learn more about Terraform, head over to our tutorial below on how to automate Terraform deployments.

How to automate Terraform deployments

Janne Ruostemaa

Editor-in-Chief

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top