32

Is it possible to check if a string exists in a file using Ansible?

I want to check is a user has access to a server. This can be done on the server using cat /etc/passwd | grep username, but I want Ansible to stop if the user is not there.

I have tried to use the lineinfile but can't seem to get it to return.

code

 - name: find
   lineinfile: dest=/etc/passwd
               regexp=[user]
               state=present
               line="user"

The code above adds user to the file if he is not there. All i want to do is check. I don't want to modify the file in any way, is this possible

Thanks.

isherwood
  • 58,414
  • 16
  • 114
  • 157
Frostie_the_snowman
  • 629
  • 3
  • 9
  • 17
  • 4
    Possible duplicate of [Only check whether a line present in a file (ansible)](http://stackoverflow.com/questions/30786263/only-check-whether-a-line-present-in-a-file-ansible) – psiyumm Jul 19 '16 at 14:59
  • It doesn't solve the general case but if you just need to check whether a user exists, have you considered the [getent module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/getent_module.html)? – Calum Halpin Jun 02 '23 at 19:51

6 Answers6

63

It's a tricky one. the lineinfile module is specifically intended for modifying the content of a file, but you can use it for a validation check as well.

- name: find
  lineinfile: 
    dest: /etc/passwd
    line: "user"
  check_mode: yes
  register: presence
  failed_when: presence.changed

check_mode ensures it never updates the file. register saves the variable as noted. failed_when allows you to set the failure condition i.e. by adding the user because it was not found in the file.

There are multiple iterations of this that you can use based on what you want the behavior to be. lineinfile docs particular related to state and regexp should allow you to determine whether or not presence or absence is failure etc, or you can do the not presence.changed etc.


Olga
  • 982
  • 1
  • 11
  • 17
Jon Staples
  • 888
  • 1
  • 8
  • 4
  • I like the answer, thank you. Not quite what I was looking for, but better. Am thinking of asking for an option where backrefs can be set to yes and if the line isn't found (with the specified regex) then still add the text. At this time it doesn't seem to. I understand, through the example they give in the doc for lineinfile, the use case for backrefs and not adding the line if it's not there. But I wouldn't need to register a variable, run two separate actions to make sure the line is consistent, and ensure the file is in the state I want it to be in. I wonder the appetite or drawbacks. – Gregg Dec 03 '19 at 02:37
  • When setting `state: absent` and using `regexp` in above code you get the _found_ property in the registered result showing how often your regex has been found. _changed_ property might be confusing, as it is true also when the line shall be added. – skipperTux Jul 25 '20 at 07:49
  • Was very happy when finding this one! But I have a proposal on extending it. Cause by extending 'failed_when: presence.changed' with ' or presence.exception is defined' additional errors can be caught. One example is that the file didn't got changed cause the access file permissions of the testing user were not granted. – j0j0j0 Jul 25 '20 at 10:49
26

I'd probably register and evaluate a variable.

The following simple playbook works for me:

- hosts: localhost
  tasks:

  - name: read the passwd file
    shell: cat /etc/passwd
    register: user_accts

  - name: a task that only happens if the user exists
    when: user_accts.stdout.find('hillsy') != -1
    debug: msg="user hillsy exists"
hillsy
  • 671
  • 6
  • 12
12

If you want to fail if there is no user:

tasks:
  - shell: grep username /etc/passwd
    changed_when: false

By default shell module will fail if command exit code is non zero.
So it will give you ok if username is there and fails otherwise.
I use changed_when: false to prevent changed state when grepping.

Konstantin Suvorov
  • 65,183
  • 9
  • 162
  • 193
  • This also works but kills the rest of the tasks, thanks – Frostie_the_snowman Jul 19 '16 at 16:30
  • You probably want `ignore_errors: yes` as per https://docs.ansible.com/ansible/playbooks_error_handling.html#ignoring-failed-commands . – Xiong Chiamiov Jul 19 '16 at 20:51
  • 3
    Original question states _"but I want Ansible to stop if the user is not there"_ – my answer does just that: execution is stopped if user is not there. – Konstantin Suvorov Jul 19 '16 at 20:58
  • What I meant by my question _"but I want Ansible to stop if the user is not there"_ is i wanted the task to stop not the playbook. after passing in the `ignore_errors: yes` you answer also works perfect. – Frostie_the_snowman Jul 20 '16 at 09:05
  • 1
    @Frostie_the_snowman glad, that my answer helped, but I honestly don't understand your task then. It's generally a bad idea to write playbooks with `ignore_errors: yes`. If you need to conditionally do some other tasks based on the fact of user existence, you should register a variable, as other answer suggests. – Konstantin Suvorov Jul 20 '16 at 09:19
1

Other answers show how to search using a normal substring. I needed to search using a regex.

Option 1 using shell (simpler):

- ansible.builtin.shell: grep '.* bar baz' /some/file
  changed_when: false

Option 2 using ansible itself (for the purists, and Windows users):

- ansible.builtin.lineinfile:
    path: /some/file
    regex: ".* bar baz"
    state: absent            # here's the trick
  changed_when: false
  check_mode: true
  register: result
  failed_when: result.found != 1
lonix
  • 14,255
  • 23
  • 85
  • 176
  • 1
    This solution (the one using the "lineinfile" module) worked best for me and seems to be more elegant. Thank you! – Tony Jul 13 '23 at 16:20
0

I am using the following approach, using only a grep -q and a registered variable.

Upside is that it's simple, downside is that you have a FAILED line in your output. YMMV.

- name: Check whether foobar is defined in /bar/baz
  command:
    cmd: 'grep -q foobar /bar/baz'
  register: foobar_in_barbaz
  changed_when: false
  ignore_errors: true


- when: not foobar_in_barbaz.failed
  name: Do something when foobar is in /bar/baz
    ....


- when: foobar_in_barbaz.failed
  pause:
    seconds: 1
    content: |
      You do not seem to have a foobar line in /bar/baz
      If you add it, then magic stuff will happen!
vjt
  • 486
  • 4
  • 9
0

To see if a string exists in a file, you can use the ansible.builtin.find module. For example:

- name: "Verify that string exists in file"
  find:
    name: "/path/to/folder/containing/file"
    patterns: "filename"
    file_type: file
    use_regex: false
    read_whole_file: true
    contains: "String you are looking for"
  register: find_result
  failed_when: find_result.matched == 0

According to the documentation, the contains input parameter is a regular expression, so you could use it to check for more than just a substring.

Thomas M
  • 140
  • 1
  • 7