{"id":2050,"date":"2021-07-26T14:25:57","date_gmt":"2021-07-26T11:25:57","guid":{"rendered":"https:\/\/upcloud.com\/global\/us\/resources\/tutorials\/automate-cloud-server-provisioning-ansible\/"},"modified":"2026-04-23T13:25:48","modified_gmt":"2026-04-23T12:25:48","slug":"automate-cloud-server-provisioning-ansible","status":"publish","type":"tutorial","link":"https:\/\/upcloud.com\/global\/resources\/tutorials\/automate-cloud-server-provisioning-ansible\/","title":{"rendered":"How to automate Cloud Server provisioning using Ansible"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this tutorial, we\u2019ll 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Ansible works<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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,&nbsp;Ansible works by connecting to your nodes and pushing out small programs, called \u201cAnsible modules\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ansible modules are written to model the desired state of system resources. Ansible maintains a large <a href=\"https:\/\/docs.ansible.com\/ansible\/2.9\/modules\/list_of_all_modules.html\" target=\"_blank\" rel=\"noopener\">library of common modules<\/a> 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What are Playbooks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">If Ansible modules are the tools in your workshop, playbooks are your instruction manuals, and your inventory of hosts are your raw material.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Below is a simple example of how Playbooks can define different ways to organise target systems for operations according to your inventory.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">---\n- hosts: webservers\nserial: 5 # update 5 machines at a time\nroles:\n- common\n- webapp\n\n- hosts: content_servers\nroles:\n- common\n- content<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check out <a rel=\"noopener\" href=\"http:\/\/docs.ansible.com\" target=\"_blank\">docs.ansible.com<\/a> for complete documentation on all that\u2019s possible.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installing Ansible<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">From the user\u2019s 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ansible is available from a number of popular package managers for <a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/installation_guide\/intro_installation.html#installing-ansible-on-specific-operating-systems\" target=\"_blank\" rel=\"noopener\">specific operating systems<\/a>. However, for the most universal install method, we are going to use Pip with Python.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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\u2019s native package manager.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">curl https:\/\/bootstrap.pypa.io\/get-pip.py -o get-pip.py\nsudo python get-pip.py<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once the pip is installed, you can install Ansible.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo python -m pip install ansible<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That\u2019s 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating your Playbook<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Update and upgrade all packages on the system.<\/li>\n\n\n\n<li>Install the latest versions of the required software: Apache2, MariaDB. PHP and PHP-MySQL<\/li>\n\n\n\n<li>Enable and start the Apache2 web server<\/li>\n\n\n\n<li>Enable and start the MariaDB database service<\/li>\n\n\n\n<li>Fetch a test PHP index page from a remote resource<\/li>\n\n\n\n<li>Confirm that the website is accessible<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Create a new file called <tt>lamp-stack.yml<\/tt> and add the below configuration to it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">---\n- name: Ubuntu 20.04 LAMP stack\n  user: root\n  hosts: all\n  become: yes\n  tasks:\n    - name: Update repository list and cache\n      apt: update_cache=yes cache_valid_time=3600\n      \n    - name: Upgrade all packages on the Cloud Server\n      apt: upgrade=yes\n      \n    - name: Install the latest versions of each component\n      apt:\n        name:\n          - apache2\n          - mariadb-server\n          - php\n          - php-mysql\n        state: latest\n\n    - name: Check that apache2 is enabled and running\n      service:\n        name: apache2\n        enabled: true\n        state: started\n\n    - name: Check that mariadb is enabled and running\n      service:\n        name: mariadb\n        enabled: true\n        state: started\n\n    - name: Copy the php test page from remote\n      get_url:\n        url: \"https:\/\/www.middlewareinventory.com\/index.php\"\n        dest: \/var\/www\/html\/index.php\n        mode: 0644\n\n    - name: Confirm that the web server is working\n      uri:\n        url: http:\/\/{{ansible_hostname}}\/index.php\n        status_code: 200<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Running your Playbook<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The default location Ansible searches for inventory list is <tt>\/etc\/ansible\/hosts<\/tt>. However, this is only convenient if you have a single inventory list.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you are working with multiple lists, you can specify the inventory file at the command line using the <tt>-i {\/path\/to\/file}<\/tt> option.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, create an inventory file called <tt>hosts.yml<\/tt> and add the following contents.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">webserver:\n  hosts:\n    <span style=\"color: #ff0000;\">{domain or IP address}<\/span>\n  vars:\n    ansible_user: \"root\"\n    ansible_ssh_private_key_file: \"<span style=\"color: #ff0000;\">\/path\/to\/ssh-key<\/span>\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Replace the <span style=\"color: #ff0000;\"><tt>{domain or IP address}<\/tt><\/span> 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once your inventory is defined, you can use the group names to select the hosts or groups you want Ansible to run against.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, test out the connection by using the ping module by targeting your webserver.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ansible webserver -i hosts.yml -m ping<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\"><span style=\"color: #26a269;\">ansible.example.com | SUCCESS =&gt; {\n    \"ansible_facts\": {\n        \"discovered_interpreter_python\": \"\/usr\/bin\/python3\"\n    },\n    \"changed\": false,\n    \"ping\": \"pong\"\n}<\/span><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the connection worked correctly, you should see an output similar to the above example.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You are then ready to run the playbook on your Cloud Server. Use the command below to install the LAMP on your web server.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ansible-playbook -i hosts.yml lamp-stack.yml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When run, you should see an output like the example below.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">PLAY [Installing LAMP stack on Ubuntu 20.04] *********************************\n\nTASK [Gathering Facts] *******************************************************\n<span style=\"color: #26a269;\">ok: [ansible.ezample.xyz]<\/span>\n\nTASK [Update repository list and cache] **************************************\n<span style=\"color: #a2734c;\">changed: [ansible.ezample.xyz]<\/span>\n\nTASK [Upgrade all packages on the Cloud Server] ******************************\n<span style=\"color: #a2734c;\">changed: [ansible.ezample.xyz]<\/span>\n\nTASK [Install the latest versions of each component] *************************\n<span style=\"color: #a2734c;\">changed: [ansible.ezample.xyz]<\/span>\n\nTASK [Check that apache2 is enabled and running] *****************************\n<span style=\"color: #26a269;\">ok: [ansible.ezample.xyz]<\/span>\n\nTASK [Check that mariadb is enabled and running] *****************************\n<span style=\"color: #26a269;\">ok: [ansible.ezample.xyz]<\/span>\n\nTASK [Copy the php test page from remote] ************************************\n<span style=\"color: #a2734c;\">changed: [ansible.ezample.xyz]<\/span>\n\nTASK [Confirm that the web server is working] ********************************\n<span style=\"color: #26a269;\">ok: [ansible.ezample.xyz]<\/span>\n\nPLAY RECAP *******************************************************************\n: <span style=\"color: #26a269;\">ok=8<\/span> <span style=\"color: #a2734c;\">changed=4<\/span> unreachable=0 failed=0 skipped=0 rescued=0 ignored=0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you didn\u2019t include connection credentials in your inventory file, you can specify them at the command line.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ansible-playbook -i hosts.yml -u root --private-key '\/path\/to\/ssh-private-key' lamp-stack.yml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It\u2019s also possible to run commands targeting specific servers without an inventory file by defining the domain or IP address on the command line.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ansible-playbook -i '{target-public-ip},' -u root --private-key '\/path\/to\/ssh-private-key' lamp-stack.yml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This can be useful when integrating Ansible with other cloud infrastructure management tools.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Integrating Ansible with Terraform<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Check out our guide on how to get started with Terraform if you haven\u2019t used it before.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/get-started-terraform\/\">How to get started with Terraform<\/a><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, you can use the code below in your Terraform configuration to have Ansible provision the new deployment.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\"># SSH connection and authentication\nconnection {\n  host        = self.network_interface[0].ip_address\n  type        = \"ssh\"\n  user        = \"root\"\n  private_key = file(\"\/path\/to\/ssh-private-key\")\n}\n\n# Wait until the Cloud Server is availble\nprovisioner \"remote-exec\" {\n  inline = [\"echo 'Cloud Server ready!'\"]\n}\n\n# Provision the Cloud Server using Ansible\nprovisioner \"local-exec\" {\n  command = &lt;&lt;-EOT\n     ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook \n     -u root \n     -i '${self.network_interface[0].ip_address},' \n     --private-key '\/path\/to\/ssh-private-key' \n     your-playbook.yml\n  EOT\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The above example includes three sections:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>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.<\/li>\n\n\n\n<li>A remote execution provisioner is used to ensure the Cloud Server is reachable over SSH before continuing with the provisioning.<\/li>\n\n\n\n<li>The local execution provisioner then calls Ansible to run the requested playbook.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">With the required configurations in place, you can deploy and provision a new Cloud Server from start to finish with a single command.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">terraform apply<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then kick back and let the automation take care of your entire deployment.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">upcloud_server.server: Creating...\nupcloud_server.server: Still creating... [10s elapsed]\n...\nupcloud_server.server (remote-exec): Cloud Server ready!\nupcloud_server.server: Provisioning with 'local-exec'...\nupcloud_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\"]\nupcloud_server.server (local-exec): PLAY [Installing LAMP stack on Ubuntu 20.04] ***********************************\nupcloud_server.server (local-exec): TASK [Gathering Facts] *********************************************************\nupcloud_server.server (local-exec): ok: [94.237.49.179]\nupcloud_server.server (local-exec): TASK [Update repository list and cache] ****************************************\nupcloud_server.server: Still creating... [1m0s elapsed]\nupcloud_server.server (local-exec): changed: [94.237.49.179]\nupcloud_server.server (local-exec): TASK [Upgrade all packages on the Cloud Server] ********************************\nupcloud_server.server (local-exec): changed: [94.237.49.179]\nupcloud_server.server (local-exec): TASK [Install the latest versions of each component] ***************************\n...\nupcloud_server.server: Still creating... [3m20s elapsed]\nupcloud_server.server: Still creating... [3m30s elapsed]\nupcloud_server.server: Still creating... [3m40s elapsed]\nupcloud_server.server (local-exec): changed: [94.237.49.179]\nupcloud_server.server (local-exec): TASK [Check that apache2 is enabled and running] *******************************\nupcloud_server.server (local-exec): ok: [94.237.49.179]\nupcloud_server.server (local-exec): TASK [Check that mariadb is enabled and running] *******************************\nupcloud_server.server (local-exec): ok: [94.237.49.179]\nupcloud_server.server (local-exec): TASK [Copy the php test page from remote] **************************************\nupcloud_server.server (local-exec): changed: [94.237.49.179]\nupcloud_server.server (local-exec): TASK [Confirm that the web server is working] **********************************\nupcloud_server.server (local-exec): ok: [94.237.49.179]\nupcloud_server.server (local-exec): PLAY RECAP *********************************************************************\nupcloud_server.server (local-exec): 94.237.49.179              : ok=8    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0\n\nupcloud_server.server: Creation complete after 3m50s [id=00cfc56b-06bb-4269-8a3b-dca2a34cd916]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once done, you should see an output like the above showing the provisioning was completed successfully.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now that you\u2019ve gotten started, you might want to check out some other <a href=\"https:\/\/github.com\/ansible\/ansible-examples\" target=\"_blank\" rel=\"noopener\">playbook examples<\/a> over at Ansible\u2019s GitHub repository. Also, if you\u2019d like to learn more about Terraform, head over to our tutorial below on how to automate Terraform deployments.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/upcloud.com\/global\/resources\/tutorials\/automate-terraform-deployments\/\">How to automate Terraform deployments<\/a><\/p>\n<\/blockquote>\n","protected":false},"author":3,"featured_media":21238,"comment_status":"open","ping_status":"closed","template":"","community-category":[256,223],"class_list":["post-2050","tutorial","type-tutorial","status-publish","has-post-thumbnail","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/2050","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial"}],"about":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/types\/tutorial"}],"author":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/comments?post=2050"}],"version-history":[{"count":2,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/2050\/revisions"}],"predecessor-version":[{"id":6409,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/2050\/revisions\/6409"}],"wp:attachment":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/media?parent=2050"}],"wp:term":[{"taxonomy":"community-category","embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/community-category?post=2050"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}