regexautomationansibleyaml

Regex to extract WordPress wp-config database information and automating it with ansible to update a shared hosting environment


Dears,

I need your support with this below regex I've been at it for the last 5 hours and could not figure it out

define\(\s*[\'"]DB_HOST[\'"]\s*,\s*[\'"]([^\'"]+)[\'"]\)

The task I have is simple I need a regex to extract WordPress database information and I will be using it in an Ansible playbook

/\*\* The host of the database for WordPress \*/
define( 'DB_HOST', 'database_host_here' );

the above is the format

I tried more than regex with the help of ChatGPT and other GenAI tools but every output fails due to a regex escape issue

this is my current playbook

---
- name: Update wp-config.php with new template
  hosts: localhost

  vars_prompt:
    - name: username
      prompt: "Enter the username"
      private: false

  vars:
    wp_config_filename: "wp-config.php"
    enable_debug: false
    multi_site: false
    search_paths:
      - "/home/{{ username }}/public_html/public"

  tasks:
    - name: Find wp-config.php files
      ansible.builtin.find:
        paths: "{{ search_paths }}"
        patterns: "{{ wp_config_filename }}"
      register: wp_config_files

    - name: Fail if no wp-config.php files found
      ansible.builtin.fail:
        msg: "No wp-config.php files found in the provided paths."
      when: wp_config_files.matched == 0

    - name: Debug wp-config.php files
      ansible.builtin.debug:
        var: wp_config_files

    - name: Read existing wp-config.php for database credentials
      ansible.builtin.slurp:
        src: "{{ item.path }}"
      loop: "{{ wp_config_files.files }}"
      loop_control:
        loop_var: item
      register: wp_config_content
      when: wp_config_files.matched > 0

    - name: Decode base64 encoded wp-config.php content
      ansible.builtin.set_fact:
        wp_config_decoded: "{{ wp_config_content.results | map(attribute='content') | map('b64decode') | list }}"

    - name: Debug decoded wp-config.php content
      ansible.builtin.debug:
        var: wp_config_decoded

    - name: Set database credentials from existing wp-config.php
      ansible.builtin.set_fact:
        db_host: "{{ item | regex_search('define\\(\\s*[\'\"]DB_HOST[\'\"]\\s*,\\s*[\'\"]([^\'\"]+)[\'\"]') | default('localhost') }}"
        db_name: "{{ item | regex_search('define\\(\\s*[\'\"]DB_NAME[\'\"]\\s*,\\s*[\'\"]([^\'\"]+)[\'\"]') | default('wordpress') }}"
        db_user: "{{ item | regex_search('define\\(\\s*[\'\"]DB_USER[\'\"]\\s*,\\s*[\'\"]([^\'\"]+)[\'\"]') | default('root') }}"
        db_password: "{{ item | regex_search('define\\(\\s*[\'\"]DB_PASSWORD[\'\"]\\s*,\\s*[\'\"]([^\'\"]+)[\'\"]') | default('password') }}"
      loop: "{{ wp_config_decoded }}"
      loop_control:
        loop_var: item

    - name: Debug extracted database credentials
      ansible.builtin.debug:
        msg: >
          DB Name: {{ db_name }},
          DB User: {{ db_user }},
          DB Password: {{ db_password }},
          DB Host: {{ db_host }}

    - name: Create a new wp-config.php from template
      ansible.builtin.template:
        src: "wp-config.php.j2"
        dest: "{{ item.path }}"
        mode: "0644"
      loop: "{{ wp_config_files.files }}"
      when: wp_config_files.matched > 0

I have a shared hosting environment with more than one WordPress website and I want to make all the settings are the same so I want to automate the detection of the database information, update the template, and place it in the correct path.

define\(\s*[\'"]DB_HOST[\'"]\s*,\s*[\'"]([^\'"]+)[\'"]\)

It fails with ansible

UPDATE

Thanks to @WiktorStribiżew I was able to figure out the solution to my issue

The new and updated regex value with regex_search for ansible

regex_search('define\\s*\\(\\s*[\\x22\\x27]DB_HOST[\\x22\\x27]\\s*,\\s*[\\x22\\x27]([^\\x22\\x27]+)[\\x22\\x27]\\s*\\)', '\\1') | first

UPDATE 2:

Based on @U880D's answer, if someone faces the same case or wants to implement the same task.

Full Playbook:

---
- name: Update wp-config.php with new template
  hosts: all
  become: true

  tasks:
    - name: Find all user directories under /home
      ansible.builtin.find:
        paths: "{{ search_path }}"
        file_type: directory
      register: user_dirs

    - name: Find all wp-config.php files in user directories
      ansible.builtin.find:
        paths: "{{ item.path }}/public_html"
        patterns: "wp-config.php"
      register: wp_config_files
      loop: "{{ user_dirs.files }}"

    - name: Fail if no wp-config.php files found
      ansible.builtin.fail:
        msg: "No wp-config.php files found in the provided paths."
      when: wp_config_files.matched == 0

    - name: Debug wp-config.php files
      ansible.builtin.debug:
        var: wp_config_files

    - name: Read existing wp-config.php for database credentials
      ansible.builtin.slurp:
        src: "{{ item.path }}"
      loop: "{{ wp_config_files.files }}"
      register: wp_config_content
      when: wp_config_files.matched > 0

    - name: Debug wp-config.php content
      ansible.builtin.debug:
        var: wp_config_content.results

    - name: Set database credentials from existing wp-config.php
      ansible.builtin.set_fact:
        db_host: >-
          {{ item.content | b64decode | regex_search('DB_HOST(.+)', '\\1') | regex_search('([A-Za-z])+') }}
        db_name: >-
          {{ item.content | b64decode | regex_search('DB_NAME(.+)', '\\1') | regex_search('([A-Za-z])+') }}
        db_user: >-
          {{ item.content | b64decode | regex_search('DB_USER(.+)', '\\1') | regex_search('([A-Za-z])+') }}
        db_password: >-
          {{ item.content | b64decode | regex_search('DB_PASSWORD(.+)', '\\1') | regex_search('([A-Za-z])+') }}
      loop: "{{ wp_config_content.results }}"
      loop_control:
        loop_var: item
      when: item.content is defined

    - name: Debug extracted database credentials
      ansible.builtin.debug:
        msg: >-
          DB Name: {{ db_name }},
          DB User: {{ db_user }},
          DB Password: {{ db_password }},
          DB Host: {{ db_host }}

    - name: Create a new wp-config.php from template
      ansible.builtin.template:
        src: "wp-config.php.j2"
        dest: "{{ item.path }}"
        mode: "0644"
      loop: "{{ wp_config_files.files }}"
      when: wp_config_files.matched > 0

Solution

  • There is a workaround for adding double and single quotes to your regex pattern if you have trouble understanding how to escape them inside your string literals. This works with most -if not all - NFA regex engines.

    You can match " with \x22 and ' with \x27 escape entity.

    So, your pattern will look like

    regex_search('define\\(\\s*[\\x22\\x27]DB_HOST[\\x22\\x27]\\s*,\\s*[\\x22\\x27]([^\\x22\\x27]+)[\\x22\\x27]\\s*\\)')
    

    Note that if you need to get the group value from the result, you need to specify it as the second argument to regex_search:

    regex_search('define\\(\\s*[\\x22\\x27]DB_HOST[\\x22\\x27]\\s*,\\s*[\\x22\\x27]([^\\x22\\x27]+)[\\x22\\x27]\\s*\\)', '\\1')
    

    Since you obtain a list as a result of your code execution, and you only need the first item in that list, all you need is to add the | first after your regex search command.