skip to Main Content

I’m trying to use Ansible’s lineinfile module to add a new parameter to an existing log_format directive in an nginx configuration file. However, I’m encountering difficulties in getting the module to work as expected.

Here’s my nginx configuration file excerpt:

http {
  
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
                      
    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

I want it to change it to:

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"'
                      'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';
  
    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

And here’s the Ansible task I’m using:

- name: Add string between groups of regex in nginx.conf
  ansible.builtin.lineinfile:
    path: nginx.conf
    backrefs: yes
    regexp: '(log_format[^;]*?)(;)'
    line: '1 my_string 2'

Despite trying different regex patterns and configurations, the file remains unchanged, and Ansible simply returns ok without making any changes. I’ve validated the regex using online tools like regex101, and it appears to match the desired portion of the file accurately.

PS: I have tried other regex too, but none of them seem to work. Eg:

(log_formats*S*;)
(log_formats*S*;)

2

Answers


  1. In such a case it is recommended to simplify the Use Case as much as possible. You may consider

    and so on …

    In example, if the line order does not matter, a minimal playbook

    ---
    - hosts: localhost
      become: false
      gather_facts: false
    
      vars:
    
        my_string: >-
          'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'
    
      tasks:
    
        - debug:
            var: my_string
    
        - lineinfile:
            path: nginx.conf
            firstmatch: true
            insertbefore: ""';"
            line: "ttt{{ my_string }}"
            state: present
          diff: true
    

    will result into an output of

    TASK [debug] ********************************************************************************************************************
    ok: [localhost] =>
      my_string: '''rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'''
    
    TASK [lineinfile] ***************************************************************************************************************
    --- before: nginx.conf (content)
    +++ after: nginx.conf (content)
    @@ -2,6 +2,7 @@
    
         log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                           '$status $body_bytes_sent "$http_referer" '
    +                       'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'
                           '"$http_user_agent" "$http_x_forwarded_for"';
    
         access_log  /var/log/nginx/access.log  main;
    
    changed: [localhost]
    

    and a file content of

    http {
    
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                            'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'
                          '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log  /var/log/nginx/access.log  main;
    
        sendfile            on;
        tcp_nopush          on;
        tcp_nodelay         on;
        keepalive_timeout   65;
        types_hash_max_size 4096;
    
    Login or Signup to reply.
  2. The problem is that you try to match a string spanning multiple lines.
    When the
    logformat entry is on a single line, it would work. Lineinfile is not suitable for tackling this task (as the name suggests).

    From the ansible documentation:

    This module ensures a particular line is in a file, or replace an existing line using a back-referenced regular expression.
    This is primarily useful when you want to change a single line in a file only.
    See the ansible.builtin.replace module if you want to change multiple, similar lines or check ansible.builtin.blockinfile if you want to insert/update/remove a block of lines in a file. For other cases, see the ansible.builtin.copy or ansible.builtin.template modules.

    Because of the one line character of the module, your regexp simply does not match.
    If you want to use lineInFile for this task, you should change your insert strategy by inserting your string somewhere on the first line, if this is an acceptable and working solution.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search