ThorneLabs

Manage Apache VirtualHosts and mod_rewrite Rules with Ansible


I needed a way to manage Apache VirtualHosts and mod_rewrite rules. There are a handful of Ansible Roles available at Ansible Galaxy to manage Apache VirtualHosts, but none of them also manage mod_rewrite rules.

After figuring out how to include additional content in an Ansible Template file, I was able to quickly put together an Ansible Playbook and Ansible Template to manage my Apache VirtualHosts and their particular mod_rewrite rules.

Ansible Directory

My Ansible environment exists within directory ~/Development/ansible-lab/ on my workstation. Any files or directories created in this post will be created within this directory.

Ansible Inventory File

In my particular case, all of my websites run on two web servers with a load balancer in front of them, so I created file ~/Development/ansible-lab/hosts with the following contents:

[web-servers]
webserver1.example.com
webserver2.example.com

Ansible Files

The mod_rewrite rules are simple text files stored in directory ~/Development/ansible-lab/files/apache/mod_rewrite/. The files are named vhost1.example.com.conf, vhost2.example.com.conf (after the actual FQDN of the website) and each contains the following contents (these are just examples, the actual text files contain many more mod_rewrite rules):

vhost1.example.com.conf

RewriteEngine On

RewriteRule /documentation/2013/01/03/example-post1.html http://vhost1.example.com/2013/01/03/example-post1.html [R=permanent,L]
RewriteRule /documentation/2013/03/05/example-post2.html http://vhost1.example.com/2013/03/05/example-post2.html [R=permanent,L]

vhost2.example.com.conf

RewriteEngine On

RewriteRule /documentation/2014/02/10/example-post3.html http://vhost2.example.com/2014/02/10/example-post3.html [R=permanent,L]
RewriteRule /documentation/2014/05/20/example-post4.html http://vhost2.example.com/2014/05/20/example-post4.html [R=permanent,L]

vhost3.example.com.conf

In this example, the vhost3.example.com Apache VirtualHost never had any mod_rewrite rules, so file vhost3.example.com.conf will not exist in the ~/Development/ansible-lab/files/apache/mod_rewrite/ directory.

Ansible Template

I created Ansible Template ~/Development/ansible-lab/templates/apache_vhost.conf.j2 with the following contents:

NameVirtualHost *:80

<VirtualHost *:80>
    ServerName {{ item.servername }}
    DocumentRoot /var/www/html/{{ item.documentrootdir }}
    ServerAdmin {{ item.serveradmin }}
    ErrorLog logs/{{ item.errorlog }}-error_log
    CustomLog logs/{{ item.customlog }}-access_log common

    <Directory /var/www/html/{{ item.documentrootdir }}>
        Options None
    </Directory>

{% include "files/apache/mod_rewrite/" + item.documentrootdir + ".conf" ignore missing %}

</VirtualHost>

When Ansible is generating the Apache VirtualHost file for vhost1.example.com.conf, the include line (which is Jinja2 syntax) would check if there is a file named vhost1.example.com.conf in the ~/Development/ansible-lab/files/apache/mod_rewrite/ directory. If the file exists, its contents would be included in the generated Apache VirtualHost file, or, if the file does not exist, the Apache VirtualHost file would be generated without any additional content (the ignore missing parameters on the include line enables this).

Take note, the Ansible Playbook was originally stored in directory ~/Development/ansible-lab/playbooks/. But, for all of this to work, it had to be moved to ~/Development/ansible-lab/. There appears to be a path problem with Ansible Templates if the Ansible Playbook is in a directory other than the root Ansible directory. Supposedly this was fixed, but I have not investigated the issue further.

Ansible Playbook

The Ansible Playbook is the last piece to put together. Below are the contents of that playbook.

In this particular example, the Apache VirtualHost parameters are stored within the Ansible Playbook. As the number of Apache VirtualHosts increases, it would be better to move them out of the Ansible Playbook into their own variable file and reference that file within the Ansible Playbook as shown in the Example Playbook section in geerlingguy’s Ansible Apache Role.

Take note that the following Ansible Playbook is written to manage Apache on RHEL/CentOS. Some of the file paths and service names will need to be changed to make it work on Debian/Ubuntu.

---
- hosts: web-servers

  vars:
    apache_vhosts_enabled:
      - servername: "vhost1.example.com"
        serveradmin: "admin@example.com"
        documentrootdir: "vhost1.example.com"
        errorlog: "vhost1.example.com"
        customlog: "vhost1.example.com"
      - servername: "vhost2.example.com"
        serveradmin: "admin@example.com"
        documentrootdir: "vhost2.example.com"
        errorlog: "vhost2.example.com"
        customlog: "vhost2.example.com"
    apache_vhosts_disabled:
      - servername: "vhost3.example.com"
        serveradmin: "admin@example.com"
        documentrootdir: "vhost3.example.com"
        errorlog: "vhost3.example.com"
        customlog: "vhost3.example.com"

  tasks:
  - name: Install Apache
    yum:
      pkg={{ item }}
      state=present
    with_items:
      - httpd

  - name: Ensure httpd is running
    service:
      name=httpd
      state=started

  - name: Ensure httpd is enabled
    service:
      name=httpd
      enabled=yes

  - name: Create sites.d directory
    file:
      path=/etc/httpd/conf.d/sites.d
      state=directory
      owner=root
      group=root
      mode=0755

  - name: Add sites.d Include to httpd.conf
    lineinfile:
      dest=/etc/httpd/conf/httpd.conf
      state=present
      line='Include conf.d/sites.d/*.conf'
    notify:
      - restart httpd

  - name: Create a VirtualHost file for each enabled VirtualHost
    template:
      src=templates/apache_vhost.conf.j2
      dest=/etc/httpd/conf.d/sites.d/{{ item.documentrootdir }}.conf
      owner=root
      group=root
      mode=0644
    with_items: apache_vhosts_enabled
    notify:
      - restart httpd

  - name: Create web directories for each enabled VirtualHost
    file:
      path=/var/www/html/{{ item.documentrootdir }}
      state=directory
      owner=apache
      group=apache
      mode=0755
    with_items: apache_vhosts_enabled

  - name: Remove VirtualHost file for each disabled VirtualHost
    file:
      path=/etc/httpd/conf.d/sites.d/{{ item.documentrootdir }}.conf
      state=absent
    with_items: apache_vhosts_disabled
    notify:
      - restart httpd

  - name: Remove web directories for each disabled VirtualHost
    file:
      path=/var/www/html/{{ item.documentrootdir }}
      state=absent
    with_items: apache_vhosts_disabled

  handlers:
  - name: restart httpd
    service:
      name=httpd
      state=restarted

As Apache VirtualHosts are enabled or disabled or more mod_rewrite rules are needed, modify the Ansible Playbook and, if necessary, the mod_rewrite text files and re-run the playbook to apply all of the changes.

References