ansible – insert after only if the pattern exists and the new insert is not there

Here is a quick ansible tip for system administrators for the ansible lineinfile. Imagine you want to insert a line after a word (or a predefined marker in your configuration file), but you want to insert the line ONLY if the word exists!
It could be done with lineinfile module but there is a limitation. The module will insert after the first occurrence of your marker or at the end of the file. Here is what the manual says: “If specified regular expression has no matches, EOF will be used instead.” And what if you what to insert some additional line to your structured configuration file? It will corrupt your configuration file, so we need something else!
Not only this! Imagine you have already inserted the line in a previous playbook run? It will be unwanted to add the line, again and again, each time the playbook is run. So here we propose the following solution:

  1. Test for existance of the file you want to insert text.
  2. Test for the existance of the marker (aka tag) in the file.
  3. Test for the existance of the line we want to insert.
  4. Insert the line after the marker (aka tag) if all of the above three conditions are true.

Here we use three ansible modules – stat, shell, lineinfile and variables and conditional checks.
If you are a newbie in ansible you can check this article – First ansible use – install and execute a single command or multiple tasks in a playbook There you can see how to create your inventory file (and configure sudo if you remotely log in with unprivileged user) used herein the example:

Ansible YAML file

---
- hosts: all
  tasks:
        - name: Test for nginx-config
          stat:
            path: /etc/nginx/nginx.conf
          register: test_exist_nginx_config
          tags: cors-insert-include
      
        - name: Test for \#FIRST-SRV-LOCATION tag
          shell: grep '#FIRST-SRV-LOCATION' /etc/nginx/nginx.conf | tr -d "\n" | cat
          register: test_first_srv_location
          when: test_exist_nginx_config.stat.exists
          changed_when: False
          tags: cors-insert-include

        - name: Test for cors-locations.loc inserted already
          shell: grep "cors-locations.loc" /etc/nginx/nginx.conf | tr -d "\n" | cat
          register: test_cors_locations_loc
          when: test_exist_nginx_config.stat.exists
          changed_when: False
          tags: cors-insert-include
          
        - name: Insert the includes after \#FIRST-SRV-LOCATION
          lineinfile:
            path: /etc/nginx/nginx.conf
            insertafter: '#FIRST-SRV-LOCATION'
            line: '                include /etc/nginx/conf.d/cors-locations.loc;'
            state: present
          when: test_exist_nginx_config.stat.exists and test_first_srv_location.stdout != "" and test_cors_locations_loc.stdout == ""
          tags: cors-insert-include

We want to insert a new include line after our predefined tag “#FIRST-SRV-LOCATION” in the nginx webserver’s main configuration file.

Here is how to run the above ansible playbook

myuser@srv ~ $ ansible-playbook -l srv3 -i ./inventory.ini ./playbook-example.yml

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [srv3]

TASK [Test for nginx-config] ***********************************************************************************************************************************************
ok: [srv3]

TASK [Test for \#FIRST-SRV-LOCATION tag] ***********************************************************************************************************************************
ok: [srv3]

TASK [Test for cors-locations.loc inserted already] ************************************************************************************************************************
ok: [srv3]

TASK [Insert the includes after \#FIRST-SRV-LOCATION] **********************************************************************************************************************
changed: [srv3]

PLAY RECAP *****************************************************************************************************************************************************************
srv3                       : ok=5    changed=3    unreachable=0    failed=0   

In “playbook-example.yml” is the code we provided above and the file “inventory.ini” includes our servers.

If you execute it again it will output the last task (the task, only which changes something, because the first three are just tests) as “skipped” not “changed” as the first run:

myuser@srv ~ $ ansible-playbook -l srv3 -i ./inventory.ini ./playbook-example.yml

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [srv3]

TASK [Test for nginx-config] ***********************************************************************************************************************************************
ok: [srv3]

TASK [Test for \#FIRST-SRV-LOCATION tag] ***********************************************************************************************************************************
ok: [srv3]

TASK [Test for cors-locations.loc inserted already] ************************************************************************************************************************
ok: [srv3]

TASK [Insert the includes after \#FIRST-SRV-LOCATION] **********************************************************************************************************************
skipping: [srv3]

PLAY RECAP *****************************************************************************************************************************************************************
srv3                       : ok=4    changed=0    unreachable=0    failed=0   

And if you run it with missing file, in which we want to insert the line, it will report multiple “skipped”:

myuser@srv ~ $ ansible-playbook -l srv3 -i ./inventory.ini ./playbook-example.yml

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [srv3]

TASK [Test for nginx-config] ***********************************************************************************************************************************************
ok: [srv3]

TASK [Test for \#FIRST-SRV-LOCATION tag] ***********************************************************************************************************************************
skipping: [srv3]

TASK [Test for cors-locations.loc inserted already] ************************************************************************************************************************
skipping: [srv3]

TASK [Insert the includes after \#FIRST-SRV-LOCATION] **********************************************************************************************************************
skipping: [srv3]

PLAY RECAP *****************************************************************************************************************************************************************
srv3                       : ok=2    changed=0    unreachable=0    failed=0   

Leave a Reply

Your email address will not be published. Required fields are marked *