ansibleansible-module

How to search for a string in a remote file using Ansible?


Based on a questions

and considerations like

I was wondering how this can be solved instead of using the shell module?

---
- hosts: localhost
  become: false
  gather_facts: false

  vars:

    SEARCH_STRING: "test"
    SEARCH_FILE: "test.file"

  tasks:

  - name: Search for string in file
    command:
      cmd: "grep '{{ SEARCH_STRING }}' {{ SEARCH_FILE }}"
    register: result
    # Since it is a reporting task
    # which needs to deliver a result in any case
    failed_when: result.rc != 0 and result.rc != 1
    check_mode: false
    changed_when: false

Or instead of using a workaround with the lineinfile module?

---
- hosts: localhost
  become: false
  gather_facts: false

  vars:

    SEARCH_STRING: "test"
    SEARCH_FILE: "test.file"

  tasks:

  - name: Search for string
    lineinfile:
      path: "{{ SEARCH_FILE }}"
      regexp: "{{ SEARCH_STRING }}"
      line: "SEARCH_STRING FOUND"
      state: present
    register: result
    # Since it is a reporting task
    changed_when: false
    failed_when: "'replaced' not in result.msg" # as it means SEARCH_STRING NOT FOUND
    check_mode: true # to prevent changes and to do a dry-run only

  - name: Show result, if not found
    debug:
      var: result
    when: "'added' in result.msg" # as it means SEARCH_STRING NOT FOUND

Since I am looking for a more generic approach for tasks in example like

grep ${SEARCH_STRING} /var/log/messages* | cut -d ':' -f 1 | uniq

could it be a feasible case for Should you develop a module?


Solution

  • Following Developing modules and Creating a module I've found the following simple solution with

    Custom Module library/pygrep.py

    #!/usr/bin/python
    
    from __future__ import (absolute_import, division, print_function)
    __metaclass__ = type
    
    from ansible.module_utils.basic import AnsibleModule
    
    def run_module():
        module_args = dict(
            path=dict(type='str', required=True),
            search_string=dict(type='str', required=True)
        )
    
        result = dict(
            changed=False,
            found_lines='',
            found=False
        )
    
        module = AnsibleModule(
            argument_spec=module_args,
            supports_check_mode=True
        )
    
        with open(module.params['path'], 'r') as f:
            for line in f.readlines():
                if module.params['search_string'] in line:
                    result['found_lines'] =  result['found_lines'] + line
                    result['found'] = True
    
        result['changed'] = False
    
        module.exit_json(**result)
    
    def main():
        run_module()
    
    if __name__ == '__main__':
        main()
    

    Playbook pygrep.yml

    ---
    - hosts: localhost
      become: false
      gather_facts: false
    
      vars:
    
        SEARCH_FILE: "test.file"
        SEARCH_STRING: "test"
    
      tasks:
    
      - name: Grep string from file
        pygrep:
          path: "{{ SEARCH_FILE }}"
          search_string: "{{ SEARCH_STRING }}"
        register: search
    
      - name: Show search
        debug:
          var: search
        when: search.found
    

    For a simple test.file

    NOTEST
    This is a test file.
    It contains several test lines.
    321tset
    123test
    cbatset
    abctest
    testabc
    test123
    END OF TEST
    

    it will result into an output of

    TASK [Show search] ******************
    ok: [localhost] =>
      search:
        changed: false
        failed: false
        found: true
        found_lines: |-
          This is a test file.
          It contains several test lines.
          123test
          abctest
          testabc
          test123
    
    

    Some Links