Ansible WordPress Installation on Ubuntu

Learn how to install WordPress using Ansible on an Apache Server on PHP 7 with MySQL. As a DevOps Engineer, one of the main goals of our role is to automate. There are many reasons why we automate, but mine is to reduce human error and save time. In this guide, we’ll use many modules of Ansible which will speed up our job.

Note: Ansible has updated the references to the modules.

What will I learn?

  • How to create a 100% working WordPress installation using the latest PHP version PHP7.
  • How to enable the use of .htaccess file.
  • How to create database, user and assigns the appropriate privileges.
  • How to install the latest version of WordPress.

By the end of this guide, you’ll have mastered the art of WordPress automation using Ansible.

Prerequisites

  • Access to a Ubuntu or Debian Linux Machine.
  • Working SSH Connection to the server.
  • Ansible working on the controller machine.

Let’s automate.

We will be creating four roles.

  1. Apache (Installing Apache 2.4, enabling the use of .htaccess)
  2. PHP (Installing PHP7 with the required extensions)
  3. MySQL (Installing MySQL 5.7)
  4. WordPress (Installing the latest WordPress version and configuring the wp-config.php

Step 1 – Installing Apache

The very first thing which we need to do is to install Apache so we could serve our website. This is extremely easy using the apt-get module. With the apt-get module, you can install any program.

In the first task, we’ll install Apache. In the second task, we’ll enable mod rewrite module. The mod rewrite module enables the use of .htaccess file.

- name: install apache
  yum: 
    name: apache2
    state: present
 
 -name: Enable mod rewrite
  apache2_module:
    state: present
    name: rewrite
 
- name: enable the use of .htaccess files in any projects
  copy:
     src: enable_htaccess.conf
     dest: /etc/apache2/sites-available/enable_htaccess.conf
 
- name: enable the .htaccess vhost
  file:
    src: /etc/apache2/sites-available/enable_htaccess.conf
    dest: /etc/apache2/sites-enabled/enable_htaccess.conf
    state: link

In order to enable .htaccess file usage, will create “enable_htaccess.conf” in our files directory. The task above will copy the file into the Apache sites-available directory and enable it.

<Directory /var/www/>
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
</Directory>

We use the state present to ensure we are only installing Apache if it has not been installed already. It is a good practice to mention the state while installing services.

Step 2 – Installing PHP

Now, that we have Apache working. It is time to install PHP. WordPress requires certain PHP extensions. We will be using the loop option of Ansible to install the required extensions. This makes it simpler and easier to customize.

name: install PHP
apt: name={{item}} state=present
with_items:
    - php
    - php-mcrypt
    - php-mysql
    - php-common
    - php-mysql
    - php-tidy
    - php-xml
    - php-xmlrpc
    - php-mbstring
    - php-memcached
    - php-curl
    - php-zip

Step 3 – Installing MySQL

The latest version of WordPress requires MySQL 5.7. We will use the package module to install MySQL

---
 
  - name: Install MySQL Python
    apt:
      name: python-mysqldb
      state: present
 
  - name: install MySQL Server
    apt:
      name: mysql-server
      state: present
 
  - name: install MySQL Client
    apt:
      name: mysql-client
      state: present
 
  - name: install MySQL Common
    apt:
      name: mysql-common
      state: present
 
  - name: ensure MySQL always starts
    service:
      name: mysql
      state: started
      enabled: yes

Step 4 – Installing WordPress

Once, we have Apache, PHP and MySQL installed. Our final step is to download and install WordPress.

Since we are going to automate the entire process we have to create our wp-config variables.

Defining the variables:

Variables can be stored in many different places but we are going to save them in the defaults directory.

A good Ansible role is one which can easily be reused.

In the main.yml of defaults directory, place the below code.

---
# defaults file for wordpress
wp_install_path : '/var/www'
wp_directory_name: 'infinitypp'
 
wp_db_name: 'name'
wp_db_username: 'nm'
wp_db_password: "22"
wp_db_host: 'hos'
wp_table_prefix: 'wpzcx_'
wp_debug_mode: 'FALSE'

Writing the task.

In the wordpress/tasks/main.yml we’ll write our WordPress tasks. There are a few things which we need to check before proceeding. The most important of all is to ensure we’re not replacing a current working WordPress project. This is why we’ll only install WordPress if we ca’t find an index.php file in the install path.

- name: Concantenate the install and directory into a single variable
  set_fact:
    path: "{{wp_install_path}}/{{wp_directory_name}}"
 
- name: Only install WordPress when there is no index.php in the path
  stat:
    path: "{{path}}/index.php"
  register: stat_result
 
- name: Ensure that installation directory exists
  file: path={{path}} state=directory
  when: stat_result.stat.exists == False
 
- name: Download Latest Version to /tmp
  get_url: url=https://wordpress.org/latest.tar.gz force=no dest=/tmp/wordpress.tar.gz
  when: stat_result.stat.exists == False
 
- name: Extract archive
  unarchive: src=/tmp/wordpress.tar.gz dest=/tmp copy=no
  when: stat_result.stat.exists == False
 
 
- name: Move extracted directory to {{path}}
  shell: cp -r -n /tmp/wordpress/* {{path}}
  when: stat_result.stat.exists == False
 
 
- name: Remove wordpress.tar.gz
  file: path=/tmp/wordpress.tar.gz state=absent
  when: stat_result.stat.exists == False
 
- name: Fetch random salts for WordPress config
  local_action: command curl https://api.wordpress.org/secret-key/1.1/salt/
  register: "wp_salt"
  become: no
  become_method: sudo
 
 
- name: Copy WordPress config file
  template: src=wp-config.php dest={{path}}
 
- name: Change ownership of installation directory
  file: path={{path}} owner=www-data group=www-data  state=directory recurse=yes setype=httpd_sys_content_t
  when: stat_result.stat.exists == False
 
- name: Change ownership of wp-content directory
  file: path={{path}}/wp-content/ owner=www-data group=www-data mode=755 state=directory recurse=yes
  when: stat_result.stat.exists == False
 
 
- name: Create a our WordPress database
  mysql_db:
    config_file: "/etc/mysql/my.cnf"
    name: "{{wp_db_name}}"
    state: present
 
- name: Create a new database user and password
  mysql_user:
    config_file: "/etc/mysql/my.cnf"
    name: "{{wp_db_username}}"
    password: "{{wp_db_password}}"
    priv: '{{wp_db_name}}.*:ALL'
    state: present

The last two task involves interaction with the database. It is a good practice to use vault to securely use our important variables.

Create wp-config.php in wordpress/templates directory

The wp-config.php variables will be replaced by the ones we declared in the main.yml (defaults) directory.

define('DB_NAME', '{{ wp_db_name }}');
define('DB_USER', '{{ wp_db_username }}');
define('DB_PASSWORD', '{{ wp_db_password }}');
define('DB_HOST', '{{ wp_db_host }}');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
{{ wp_salt.stdout }}
$table_prefix  = '{{ wp_table_prefix }}';
define('WPLANG', '');
define('WP_DEBUG', {{ wp_debug_mode }});
if ( !defined('ABSPATH') )
    define('ABSPATH', dirname(__FILE__) . '/');
require_once(ABSPATH . 'wp-settings.php');

Define our website, virtual host

Every website has a virtual host config. In-order to automate this process we’ll create vhost.conf.js in the wordpress/templates directory
This file will contain the virtual host config for our WordPress Website.

<VirtualHost>
       DocumentRoot {{ path }}
       ServerName {{ website_address }}
</VirtualHost>

If you visit the website URL mentioned in the variables configuration file, you will be prompted by the WordPress Insulation page. One of the good aspects of writing an Ansible role is its usability. If we want to deploy a WordPress website for another project, it is a matter of updating our variables

If you are looking into configuring cron jobs in Ansible, why not read our Ansible cron post