Using Ansible to Generate a Worpdress wp-config.php file

Published by John on May 18, 2018 Under Website Hosting

Recently, I have begun to experiment with using Ansible to automate some of the tasks related to my website hosting and building tasks.

I have only just begun to dig in, but I am really digging the process and can see how using it will help save a lot of time in the future. There is, of course, an initial time cost, especially as I am learning, but over time, I think that cost will be worth it. When you compare how long it would take(for me anyway) to create a bash script that does everything a 30-40 line Ansible playbook does, it is hands down a better system. And, even if I were to get the shell script working, it wouldn’t handle errors or templating as well as Ansible does. So, going forward, I can really see myself using this and automating some of my development and server management tasks.

As one of my first playbooks, I setup a Ansible task to create a new hosting account, configure HTTPD, as well as other hosting related services, download a new copy of wordpress, and configure the wp-config.php file.

While I’m not going to share the tasks for configuring my hosting servers, as they are specific to my server setup, I will share a basic playbook for generating a WordPress configuration file, securely setting the various hash and passwords securely using randomly generated strings. I’m sure there is a better way to do this, but this seems to work quite well for my purposes.

Pre-requisites

This assummes some basic knowledge of Ansible, specifically how to setup a working playbook, utilizing an ansible.cfg and hosts file. If you haven’t gotten this far yet, you should probably start with a hello world playbook. I have been working through some of the chapters in the O’Reily Ansible Up & Running book and think it is a good starting place for learning Ansible.

Your hosts file might look something like the below, with of course the XXX being replaced with your remote server’s IP Address and Port

webserver ansible_host=XXX.XXX.XX.XXX ansible_port=XXXX

Your ansible.cfg might look something like the following, where ‘remote_user_name’ is the shell username you use to connect to your remote server:

[defaults]
inventory = hosts
remote_user = remote_user_name

The Playbook

The playbook used for generating the wp-config.php file is below:

- name: Create WordPress WP-CONFIG.PHP File
  hosts: webserver
  become: True
  vars: 
      user_name: "shell_user_name"
      db_name: "wordpress_db_name"
      db_user: "wordpress_db_user"
      db_password: "{{ lookup('password', 'tmp/pass1.txt length=45 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      AUTH_KEY: "{{ lookup('password', 'tmp/pass2.txt length=60 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      SECURE_AUTH_KEY: "{{ lookup('password', 'tmp/pass3.txt length=60 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      LOGGED_IN_KEY: "{{ lookup('password', 'tmp/pass4.txt length=60 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      NONCE_KEY: "{{ lookup('password', 'tmp/pass5.txt length=60 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      AUTH_SALT: "{{ lookup('password', 'tmp/pass6.txt length=60 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      SECURE_AUTH_SALT: "{{ lookup('password', 'tmp/pass7.txt length=60 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      LOGGED_IN_SALT: "{{ lookup('password', 'tmp/pass8.txt length=60 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      NONCE_SALT: "{{ lookup('password', 'tmp/pass9.txt length=60 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"
      
  tasks:

    - name: Create wp-config.php file
      template: 
        src: templates/wp-config.php.j2
        dest: "/home/{{ user_name }}/public_html/wp-config.php"
        owner: "{{ user_name }}"
        group: "{{ user_name }}"
  
    - name: Delete Temp Pass Files
      become: False
      connection: local
      file:
        path: "{{ item }}"
        state: absent
      loop:
        - "tmp/pass1.txt"
        - "tmp/pass2.txt"
        - "tmp/pass3.txt"
        - "tmp/pass4.txt"
        - "tmp/pass5.txt"
        - "tmp/pass6.txt"
        - "tmp/pass7.txt"
        - "tmp/pass8.txt"
        - "tmp/pass9.txt"
   

vars

There are some vars that you will need to setup, specifically user_name, db_name, and db_user.

This assumes your web root is located at /home/{{ user_name }}/public_html/, but if that is not the case, you may need to adjust the Create wp-config.php file task to use the correct directly of your worpdress installation.

Passwords are generated randomly using ansible’s lookup feature. I had initially used /dev/null, but the passwords were the same for each variable, so I used temporary files, which are deleted afterwards.

If you wanted to save a copy of the db_password, you could change that line to something like the below and it would not be deleted:

db_password: "{{ lookup('password', 'credentials/{{ db_user }}.txt length=45 chars=ascii_letters,digits,!,,,,@,#,$,%,^,&,*,(,),_,-,:,.,?,:.;,|,},{,],[') }}"

Rather than using ansible’s ascii_letters,digits,punctuation character set, I pass my own string of special characters. This is because otherwise, single quotes, double quotes, or escape characters(\) might be used, which can throw off PHP files.

Creating the Template File

You will notice the above references a template file called templates/wp-config.php.j2 in the Create wp-config.php file task. This is just a copy of the default wordpress file, with the variables referenced in the vars section added, as so:


[...]

/** The name of the database for WordPress */
define('DB_NAME', '{{ db_name }}');

/** MySQL database username */
define('DB_USER', '{{ db_user }}');

/** MySQL database password */
define('DB_PASSWORD', '{{ db_password }}');

[...]

define('AUTH_KEY',         '{{ AUTH_KEY }}');
define('SECURE_AUTH_KEY',  '{{ SECURE_AUTH_KEY }}');
define('LOGGED_IN_KEY',    '{{ LOGGED_IN_KEY }}');
define('NONCE_KEY',        '{{ NONCE_KEY }}');
define('AUTH_SALT',        '{{ AUTH_SALT }}');
define('SECURE_AUTH_SALT', '{{ SECURE_AUTH_SALT }}');
define('LOGGED_IN_SALT',   '{{ LOGGED_IN_SALT }}');
define('NONCE_SALT',       '{{ NONCE_SALT }}');

Running the Playbook

After you have the playbook and template created, you can run it as follows. I’m using become to switch to root while preforming the tasks on the web server, as otherwise I would need to be logging in as the web-server user.

So, calling the playbook looks something like this for me:

ansible-playbook create_wpconfig.yml -K --become-method=su

Whats Next

I think there may be a better way to generate a password, rather than using lookup. My goal was to avoid using shell commands, like < /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;, but it is kind of clunky. The one advantage of using the lookup is if you wanted to save the db credentials in a file for later reference. Of course, if you were to do that, you would want to save it as a secret, so it is not stored in plain text.

My next workbook is going to be to create the wordpress database and user, so when I do that, I may update this to save the password.


No Comments |

Add a Comment