Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the acf domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php on line 6131

Deprecated: Creation of dynamic property ACF::$fields is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/fields.php on line 138

Deprecated: Creation of dynamic property acf_loop::$loops is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/loop.php on line 28

Deprecated: Creation of dynamic property ACF::$loop is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/loop.php on line 269

Deprecated: Creation of dynamic property ACF::$revisions is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/revisions.php on line 397

Deprecated: Creation of dynamic property acf_validation::$errors is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/validation.php on line 28

Deprecated: Creation of dynamic property ACF::$validation is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/validation.php on line 214

Deprecated: Creation of dynamic property acf_form_customizer::$preview_values is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/forms/form-customizer.php on line 28

Deprecated: Creation of dynamic property acf_form_customizer::$preview_fields is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/forms/form-customizer.php on line 29

Deprecated: Creation of dynamic property acf_form_customizer::$preview_errors is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/forms/form-customizer.php on line 30

Deprecated: Creation of dynamic property ACF::$form_front is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/forms/form-front.php on line 598

Deprecated: Creation of dynamic property acf_form_widget::$preview_values is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/forms/form-widget.php on line 34

Deprecated: Creation of dynamic property acf_form_widget::$preview_reference is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/forms/form-widget.php on line 35

Deprecated: Creation of dynamic property acf_form_widget::$preview_errors is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/forms/form-widget.php on line 36

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the all-in-one-wp-migration domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php on line 6131

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/wp_plugin/wp_plugin.php on line 23

Deprecated: str_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/wp-super-cache/wp-cache-phase2.php on line 54

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/wp-super-cache/wp-cache-phase2.php on line 1539

Deprecated: strtolower(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/wp-super-cache/wp-cache-phase2.php on line 828

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the rocket domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php on line 6131

Deprecated: Creation of dynamic property acf_field_oembed::$width is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/fields/class-acf-field-oembed.php on line 31

Deprecated: Creation of dynamic property acf_field_oembed::$height is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/fields/class-acf-field-oembed.php on line 32

Deprecated: Creation of dynamic property acf_field_google_map::$default_values is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/fields/class-acf-field-google-map.php on line 33

Deprecated: Creation of dynamic property acf_field__group::$have_rows is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/includes/fields/class-acf-field-group.php on line 31

Deprecated: Creation of dynamic property acf_field_clone::$cloning is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/pro/fields/class-acf-field-clone.php on line 34

Deprecated: Creation of dynamic property acf_field_clone::$have_rows is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-pro/pro/fields/class-acf-field-clone.php on line 35

Deprecated: Creation of dynamic property jh_acf_field_table::$settings is deprecated in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-content/plugins/advanced-custom-fields-table-field/class-jh-acf-field-table.php on line 23

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/functions.php:6131) in /var/www/vhosts/studiogo.tech/httpdocs/upcloudold/wp-includes/rest-api/class-wp-rest-server.php on line 1902
{"id":24957,"date":"2021-07-26T14:25:57","date_gmt":"2021-07-26T11:25:57","guid":{"rendered":"https:\/\/upcloud.com\/community\/tutorials\/automate-cloud-server-provisioning-ansible"},"modified":"2021-07-26T14:25:57","modified_gmt":"2021-07-26T11:25:57","slug":"automate-cloud-server-provisioning-ansible","status":"publish","type":"tutorial","link":"https:\/\/studiogo.tech\/upcloudold\/tutorial\/automate-cloud-server-provisioning-ansible\/","title":{"rendered":"How to automate Cloud Server provisioning using Ansible"},"content":{"rendered":"\n

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

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

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

How Ansible works<\/h2>\n\n\n\n

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 \u201cAnsible modules\u201d.<\/p>\n\n\n\n

Ansible modules are written to model the desired state of system resources. Ansible maintains a large 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

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

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

What are Playbooks<\/h2>\n\n\n\n

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

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

\n

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

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

---\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<\/pre>\n\n\n\n

Check out docs.ansible.com<\/a> for complete documentation on all that\u2019s possible.<\/p>\n\n\n\n

Installing Ansible<\/h2>\n\n\n\n

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

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

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

curl https:\/\/bootstrap.pypa.io\/get-pip.py -o get-pip.py\nsudo python get-pip.py<\/pre>\n\n\n\n

Once the pip is installed, you can install Ansible.<\/p>\n\n\n\n

sudo python -m pip install ansible<\/pre>\n\n\n\n

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

Creating your Playbook<\/h2>\n\n\n\n

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

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

    Create a new file called lamp-stack.yml<\/tt> and add the below configuration to it.<\/p>\n\n\n\n

    ---\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<\/pre>\n\n\n\n

    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

    Running your Playbook<\/h2>\n\n\n\n

    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

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

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

    For example, create an inventory file called hosts.yml<\/tt> and add the following contents.<\/p>\n\n\n\n

    webserver:\n  hosts:\n    {domain or IP address}<\/span>\n  vars:\n    ansible_user: \"root\"\n    ansible_ssh_private_key_file: \"\/path\/to\/ssh-key<\/span>\"<\/pre>\n\n\n\n

    Replace the {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

    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

    For example, test out the connection by using the ping module by targeting your webserver.<\/p>\n\n\n\n

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

    If the connection worked correctly, you should see an output similar to the above example.<\/p>\n\n\n\n

    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

    ansible-playbook -i hosts.yml lamp-stack.yml<\/pre>\n\n\n\n

    When run, you should see an output like the example below.<\/p>\n\n\n\n

    PLAY [Installing LAMP stack on Ubuntu 20.04] *********************************\n\nTASK [Gathering Facts] *******************************************************\nok: [ansible.ezample.xyz]<\/span>\n\nTASK [Update repository list and cache] **************************************\nchanged: [ansible.ezample.xyz]<\/span>\n\nTASK [Upgrade all packages on the Cloud Server] ******************************\nchanged: [ansible.ezample.xyz]<\/span>\n\nTASK [Install the latest versions of each component] *************************\nchanged: [ansible.ezample.xyz]<\/span>\n\nTASK [Check that apache2 is enabled and running] *****************************\nok: [ansible.ezample.xyz]<\/span>\n\nTASK [Check that mariadb is enabled and running] *****************************\nok: [ansible.ezample.xyz]<\/span>\n\nTASK [Copy the php test page from remote] ************************************\nchanged: [ansible.ezample.xyz]<\/span>\n\nTASK [Confirm that the web server is working] ********************************\nok: [ansible.ezample.xyz]<\/span>\n\nPLAY RECAP *******************************************************************\n: ok=8<\/span> changed=4<\/span> unreachable=0 failed=0 skipped=0 rescued=0 ignored=0<\/pre>\n\n\n\n

    If you didn\u2019t include connection credentials in your inventory file, you can specify them at the command line.<\/p>\n\n\n\n

    ansible-playbook -i hosts.yml -u root --private-key '\/path\/to\/ssh-private-key' lamp-stack.yml<\/pre>\n\n\n\n

    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

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

    This can be useful when integrating Ansible with other cloud infrastructure management tools.<\/p>\n\n\n\n

    Integrating Ansible with Terraform<\/h2>\n\n\n\n

    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

    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

    Check out our guide on how to get started with Terraform if you haven\u2019t used it before.<\/p>\n\n\n\n

    \n

    How to get started with Terraform<\/a><\/p>\n<\/blockquote>\n\n\n\n

    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

    For example, you can use the code below in your Terraform configuration to have Ansible provision the new deployment.<\/p>\n\n\n\n

    # 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 = <<-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}<\/pre>\n\n\n\n

    The above example includes three sections:<\/p>\n\n\n\n