Ansible: Insert line if not exists - ansible

I'm trying insert a line in a property file using ansible.
I want to add some property if it does not exist, but not replace it if such property already exists in the file.
I add to my ansible role
- name: add couchbase host to properties
lineinfile: dest=/database.properties regexp="^couchbase.host" line="couchbase.host=127.0.0.1"
But this replaces the property value back to 127.0.0.1 if it exists already in the file.
What I'm doing wrong?

The lineinfile module ensures the line as defined in line is present in the file and the line is identified by your regexp. So no matter what value your setting already has, it will be overridden by your new line.
If you don't want to override the line you first need to test the content and then apply that condition to the lineinfile module. There is no module for testing the content of a file so you probably need to run grep with a shell command and check the .stdout for content. Something like this (untested):
- name: Test for line
shell: grep -c "^couchbase.host" /database.properties || true
register: test_grep
And then apply the condition to your lineinfile task:
- name: add couchbase host to properties
lineinfile:
dest: /database.properties
line: couchbase.host=127.0.0.1
when: test_grep.stdout == "0"
The regexp then can be removed since you already made sure the line doesn't exist so it never would match.
But maybe you're doing things back to front. Where does that line in the file come from? When you manage your system with Ansible there should be no other mechanisms in place which interfere with the same config files. Maybe you can work around this by adding a default value to your role?

This is possible by simply using lineinfile and check_mode:
- name: Check if couchbase.host is already defined
lineinfile:
state: absent
path: "/database.properties"
regexp: "^couchbase.host="
check_mode: true
changed_when: false # This just makes things look prettier in the logs
register: check
- name: Define couchbase.host if undefined
lineinfile:
state: present
path: "/database.properties"
line: "couchbase.host=127.0.0.1"
when: check.found == 0

This is the only way I was able to get this to work.
- name: checking for host
shell: cat /database.properties | grep couchbase.host | wc -l
register: test_grep
- debug: msg="{{test_grep.stdout}}"
- name: adding license server
lineinfile: dest=/database.properties line="couchbase.host=127.0.0.1"
when: test_grep.stdout == "0"

By a long way of "Trials and errors" I come to this:
- name: check existence of line in the target file
command: grep -Fxq "ip addr add {{ item }}/32 dev lo label lo:{{ app | default('app') }}" /etc/rc.local
changed_when: false
failed_when: false
register: ip_test
with_items:
- "{{ list_of_ips }}"
- name: add autostart command
lineinfile: dest=/etc/rc.local
line="ip addr add {{ item.item }}/32 dev lo label lo:{{ app | default('app') }}"
insertbefore="exit 0"
state=present
when: item.rc == 1
with_items:
- "{{ ip_test.results }}"

Looks like it does not work if you use backrefs.
Following sample does not add a line
- name: add hosts file entry
lineinfile:
path: "/etc/hosts"
regexp: "foohost"
line: "10.10.10.10 foohost"
state: present
backrefs: yes
After removing backrefs I got my line added to the file
- name: add hosts file entry
lineinfile:
path: "/etc/hosts"
regexp: "foohost"
line: "10.10.10.10 foohost"
state: present

Same idea as presented here : https://stackoverflow.com/a/40890850/7231194
Steps are:
Try to replace the line.
If replace mod change it, restore
If replace mod doesn't change, add the line
Example
# Vars
- name: Set parameters
set_fact:
ipAddress : "127.0.0.1"
lineSearched : "couchbase.host={{ ipAddress }}"
lineModified : "couchbase.host={{ ipAddress }} hello"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
# If the line not is here, I add it
- name: Add line
lineinfile:
state : present
dest : /dir/file
line : '{{ lineSearched }}'
regexp : ''
insertafter: EOF
when: checkIfLineIsHere.changed == false
# If the line is here, I still want this line in the file, Then restore it
- name: Restore the searched line.
lineinfile:
state : present
dest : /dir/file
line : '{{ lineSearched }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed

Ok, here is mine naive solution... probably not a cross-platform and native Ansible (I've just started to use this tool and still learn it), but definitely shorter:
- name: Update /path/to/some/file
shell: grep -q 'regex' /path/to/some/file && echo exists || echo 'text-to-append' >> /path/to/some/file
register: result
changed_when: result.stdout.find('exists') == -1

We have tried the below and it worked well. our scenario need to entry "compress" in syslog file if it does not exist.
- name: Checking compress entry present if not add entry
lineinfile:
path: /etc/logrotate.d/syslog
regexp: " compress"
state: present
insertafter: " missingok"
line: " compress"

- name: add couchbase.host to properties, works like add or replace
lineinfile:
path: /database.properties
regexp: '^couchbase.host=(?!(127.0.0.1))\b'
line: 'couchbase.host=127.0.0.1'
This code will replace any line ^couchbase.host=* except couchbase.host=127.0.0.1 or will add new line if it does not exist

- name: add couchbase.host to properties, works like add or replace
lineinfile:
state: present
dest: /database.properties
regexp: '^couchbase.host'
line: 'couchbase.host=127.0.0.1'

Related

How to compare the contain of my file with ansible? [duplicate]

In ansible, I need to check whether a particular line present in a file or not. Basically, I need to convert the following command to an ansible task. My goal is to only check.
grep -Fxq "127.0.0.1" /tmp/my.conf
Use check_mode, register and failed_when in concert. This fails the task if the lineinfile module would make any changes to the file being checked. Check_mode ensures nothing will change even if it otherwise would.
- name: "Ensure /tmp/my.conf contains '127.0.0.1'"
lineinfile:
name: /tmp/my.conf
line: "127.0.0.1"
state: present
check_mode: yes
register: conf
failed_when: (conf is changed) or (conf is failed)
- name: Check whether /tmp/my.conf contains "127.0.0.1"
command: grep -Fxq "127.0.0.1" /tmp/my.conf
register: checkmyconf
check_mode: no
ignore_errors: yes
changed_when: no
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: checkmyconf.rc == 0
Update 2017-08-28: Older Ansible versions need to use always_run: yes instead of check_mode: no.
User robo's regexp & absent method is quite clean, so I've fleshed it out here for easy use and added improvements from comments by #assylias and #Olivier:
- name: Ensure /tmp/my.conf contains 127.0.0.1
lineinfile:
path: /tmp/my.conf
regexp: '^127\.0\.0\.1.*whatever'
state: absent
check_mode: yes
changed_when: false
register: out
- debug:
msg: "Yes, line exists."
when: out.found
- debug:
msg: "Line does NOT exist."
when: not out.found
With the accepted solution, even though you ignore errors, you will still get ugly red error output on the first task if there is no match:
TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
failed: [localhost] => {"changed": false, "cmd": "grep -Fxq "127.0.0.1" /tmp/my.conf", "delta": "0:00:00.018709", "end": "2015-09-27 17:46:18.252024", "rc": 1, "start": "2015-09-27 17:46:18.233315", "stdout_lines": [], "warnings": []}
...ignoring
If you want less verbose output, you can use awk instead of grep. awk won't return an error on a non-match, which means the first check task below won't error regardless of a match or non-match:
- name: Check whether /tmp/my.conf contains "127.0.0.1"
command: awk /^127.0.0.1$/ /tmp/my.conf
register: checkmyconf
changed_when: False
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: checkmyconf.stdout | match("127.0.0.1")
Notice that my second task uses the match filter as awk returns the matched string if it finds a match.
The alternative above will produce the following output regardless of whether the check task has a match or not:
TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
ok: [localhost]
IMHO this is a better approach as you won't ignore other errors in your first task (e.g. if the specified file did not exist).
Use ansible lineinfile command, but this command will update the file with the line if it does not exists.
- lineinfile: dest=/tmp/my.conf line='127.0.0.1' state=present
Another way is to use the "replace module" then "lineinfile module".
The algo is closed to the one used when you want to change the values of two variables.
First, use "replace module" to detect if the line you are looking for is here and change it with the something else. (Like same line + something at the end).
Then if "replace" is true, It means your line is here then replace the new line with a particularity, with the new line looking.
Else the line you are looking for is not here.
Example:
# Vars
- name: Set parameters
set_fact:
newline : "hello, i love ansible"
lineSearched : "hello"
lineModified : "hello you"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
- name: Line is here, change it
lineinfile:
state : present
dest : /dir/file
line : '{{ newline }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
If the file contains "hello", it will become "hello you" then "hello, i love ansible" at the end.
If the file content doesn't contain "hello", the file is not modified.
With the same idea, you can do something if the lineSearched is here:
# Vars
- name: Set parameters
set_fact:
newline : "hello, i love ansible"
lineSearched : "hello"
lineModified : "hello you"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
# If the line is here, I want to add something.
- name: If line is here, do something
lineinfile:
state : present
dest : /dir/file
line : '{{ newline }}'
regexp : ''
insertafter: EOF
when: checkIfLineIsHere.changed
# But I still want this line in the file, Then restore it
- name: Restore the searched line.
lineinfile:
state : present
dest : /dir/file
line : '{{ lineSearched }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
If the file contains "hello", the line will still contain "hello" and "hello, i love ansible" at the end.
If the file content doesn't contain "hello", the file is not modified.
You can use the file plugin for this scenario.
To set a fact you can use in other tasks ... this works.
- name: Check whether /tmp/my.conf contains "127.0.0.1"
set_fact:
myconf: "{{ lookup('file', '/tmp/my.conf') }}"
ignore_errors: yes
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: "'127.0.0.1' in myconf"
To check the file content as a condition of a task ... this should work.
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: "'127.0.0.1' in lookup('file', '/tmp/my.conf')"
Another solution, also useful for other purposes is to loop over the contents of the file, line by line
- name: get the file
slurp:
src: /etc/locale.gen
register: slurped_file
- name: initialize the matches list
set_fact:
MATCHES: []
- name: collect matches in a list
set_fact:
MATCHES: "{{ MATCHES + [line2match] }}"
loop: "{{ file_lines }}"
loop_control:
loop_var: line2match
vars:
- decode_content: "{{ slurped_file.content | b64decode }}"
- file_lines: "{{ decode_content.split('\n') }}"
when: '"BE" in line2match'
- name: report matches if any
debug:
msg: "Found {{ MATCHES | length }} matches\n{{ MATCHES }}"
when: 'listlen | int > 0'
vars:
listlen: "{{ MATCHES | length }}"
This mechanism can be use to get a specific value from a matched line if you use it for example with the jinja regex_replace filter

Ansible: Modify cmdline.txt on Raspberry Pi

I am modifying /boot/cmdline.txt to add container features to a Raspberry Pi, so I need to add cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory into the file, within the same line.
I am trying to do it with the lineinfile module without much success:
- hosts: mypi
become: yes
tasks:
- name: Enable container features
lineinfile:
path: /boot/cmdline.txt
regex: " cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory"
line: " cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory"
insertafter: EOF
state: present
I have been trying modifying the insertafter to BOF, using insertbefore too, using a regex to match the last word... But it ends up adding a carriage return. I have been unable to find some way to not add a new line.
As Vladimir pointed out, Jack's answer unfortunately is not sufficient for empty files and also fails if the desired argument already exists at the beginning of the line.
The following suggested solution should address those issues. In particular, it is supposed to
support empty files,
support existing arguments at any position within the string,
be robust even with multi-line files (just in case...),
be idempotent, and
optionally update existing keys with the desired value.
# cmdline.yml
- name: read cmdline.txt
become: true
slurp: "src={{ cmdline_txt_path }}"
register: result_cmdline
- name: generate regular expression for existing arguments
set_fact:
regex_existing: '{{ "\b" + key|string + "=" + ("[\w]*" if update else value|string) + "\b" }}'
key_value_pair: '{{ key|string + "=" + value|string }}'
- name: generate regular expression for new arguments
set_fact:
regex_add_missing: '{{ "^((?!(?:.|\n)*" + regex_existing + ")((?:.|\n)*))$" }}'
- name: update cmdline.txt
become: true
copy:
content: '{{ result_cmdline.content
| b64decode
| regex_replace(regex_existing, key_value_pair)
| regex_replace(regex_add_missing, key_value_pair + " \1")
}}'
dest: "{{ cmdline_txt_path }}"
Usage:
- set_fact:
cmdline_txt_path: /boot/cmdline.txt
- include_tasks: cmdline.yml
vars:
key: cgroup_enable
value: memory
update: false
# will add the argument if the key-value-pair doesn't exist
- include_tasks: cmdline.yml
vars:
key: cgroup_enable
value: cpu
update: false
- include_tasks: cmdline.yml
vars:
key: cgroup_memory
value: 1
update: true
# will replace the value of the first matching key, if found;
# will add it if it's not found
However, I might have missed some edge cases - please let me know if you find any issues.
Since you only have the one line in the file, you can do that with either replace or lineinfile. Here is the replace version:
- name: Enable container features
replace:
path: cmdline.txt
regexp: '^([\w](?!.*\b{{ item }}\b).*)$'
replace: '\1 {{ item }}'
with_items:
- "cgroup_enable=cpuset"
- "cgroup_memory=1"
- "cgroup_enable=memory"
Stole the answer from here
I followed all of the different strategies laid out above, but in the end, I wanted something simple, as this is my first playbook, and I need to understand it now, and when I pick it up again later,.
My cmdline.txt contained multiple lines:
cat /boot/cmdline.txt -E
console=serial0,115200 console=tty1 rootfstype=ext4 fsck.repair=yes rootwait cgroup_enable=memory cgroup_enable=cpuset cgroup_memory=1$
dtoverlay=pi3-disable-bt$
dtoverlay=pi3-disable-wifi$
So, the approach I was looking for:
Would ignore the other configurations in the cmdline.txt
Would only add the add a specific key=value if it was missing
It had to be idempotent
I settled on a simple regex to decide if this was the row I wanted to edit:
If the row contaied console= (as this is the row I'm after)
AND.. If the row does not contain cgroup_memory=1
- name: Adding cgroup_enable=memory to boot parameters for k3s
lineinfile:
path: /boot/cmdline.txt
state: present
regexp: '^((?!.*cgroup_enable=memory).*console.*)$'
line: '\1 cgroup_enable=memory'
backrefs: yes
notify: reboot
- name: Adding cgroup_enable=cpuset to boot parameters for K3s
lineinfile:
path: /boot/cmdline.txt
state: present
regexp: '^((?!.*cgroup_enable=cpuset).*console.*)$'
line: '\1 cgroup_enable=cpuset'
backrefs: yes
notify: reboot
- name: Adding cgroup_memory=1 to boot parameters for K3s
lineinfile:
path: /boot/cmdline.txt
state: present
regexp: '^((?!.*cgroup_memory=1).*console.*)$'
line: '\1 cgroup_memory=1'
backrefs: yes
notify: reboot
And at some point in future, I'll probably condense all three of these into a single loop task. But not today.
Q: "Ansible lineinfile module: Do not add new line. Find some way to not add a new line."
A: It's not possible. New line will be always added by module lineinfile. See source for example
b_lines.insert(index[1], b_line + b_linesep)
This is how a new line is added. Such additions will be terminated with b_linesep. See how the variable is defined
b_linesep = to_bytes(os.linesep, errors='surrogate_or_strict')
The os.linesep is used when you want to iterate through the lines of a text file. The internal scanner recognizes the os.linesep and replaces it with a single "\n".
See What is os.linesep for?.
The task with the module replace doesn't solve this problem either. Neither it creates the line without a newline, nor it modifies existing one this way. In addition to this it's not idempotent.
- name: Enable container features
replace:
path: cmdline.txt
regexp: '^([\w](?!.*\b{{ item }}\b).*)$'
replace: '\1 {{ item }}'
loop:
- "cgroup_enable=cpuset"
- "cgroup_memory=1"
- "cgroup_enable=memory"
It will do nothing if the file is empty
TASK [Enable container features]
ok: [localhost] => (item=cgroup_enable=cpuset)
ok: [localhost] => (item=cgroup_memory=1)
ok: [localhost] => (item=cgroup_enable=memory)
If the line is present in the file this task will change it
shell> cat cmdline.txt
cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory
ASK [Enable container features] *****************************************
ok: [localhost] => (item=cgroup_enable=cpuset)
--- before: cmdline.txt
+++ after: cmdline.txt
## -1 +1 ##
-cgroup_memory=1 cgroup_enable=memory cgroup_enable=cpuset
+cgroup_memory=1 cgroup_enable=memory cgroup_enable=cpuset cgroup_memory=1

Add a string inside a configuration file through ansible

I am in need of adding a new string to one of the configuration file. I have to add say "mycustomimage" with a "," in "images =". So in short, my required output is images= previousimage,mycustomimage
View of mycnf.conf
id=1
images=previousimage
For this, I tried this code
---
- hosts: test_server
- name: Add new string after "," in images
lineinfile:
path: /home/mycnf.conf
regexp: 'images='
insertafter: '^,'
line: mycustomimage
Expected output
id= 1
images=previousimage,mycustomimage
But its not working for me. Any thoughts?
Thanks in advance!
Sid
This will help
- name: replace line
lineinfile:
path: myfile.txt
regexp: "^image="
line: 'image=previousimage,customimage'
From your example
You know the final line you want to have in the file is images=previousimage,mycustomimage
You want to add this line in place of an already existing image=.* line if it exists and does not match the final one.
The following will do the job
- name: Replace line if needed
lineinfile:
path: /home/mycnf.conf
regex: images=.*
line: images=previousimage,mycustomimage
Note: If for any reason there is no matching line in your file for the regex, the line will be added at the end of the file.
Assuming that previousimage is not known you can do two things:
1.Get this line using grep, register to a variable and add the line
- name: get line
shell: grep "^image=" /config/file.something
register: current_image
- name: update image
lineinfile:
path: /config/file.something
regexp: '^image='
line: "{{ current_image.stdout }},{{ new_image | default('customimage') }}"
2.Create a template for this configuration file and render it every time the playbook run and a change is detected:
- set_fact:
images: <list of images retrieved by lookup or static>
- name: update config.something
template:
src: my_template.j2
dest: /config/file.something
The template will look like this:
id={{ id }}
images={{ images | join(",") }}
We'll assume you don't know what previousimage is, that you may have several previous images, and you want to append mycustomimage, and you want the playbook to be idempotent:
---
- hosts: all
connection: ssh
become: no
gather_facts: no
vars:
image_name: mycustomimage
tasks:
- lineinfile:
path: testfile.txt
regexp: '^images=(.*(?<!{{ image_name }}))'
line: '\1,{{ image_name }}'
backrefs: yes
So let's explain the regexp: ^images= you can figure out yourself!
The first parenthesis starts the backref block, and it's going to suck up everything to the end of the line: .*
Then, it looks back at what it just sucked in, and makes sure {{ image_name }} is not at the end: (?<!{{ image_name }})
Finally, we close the backref block with ).

Remove the users from /etc/ssh/sshd_config with Ansible

I am trying to remove users added to the AllowGroups line in the /etc/ssh/sshd_conf file using the following after the comments from #imjoseangel.
The current line is
AllowGroups devops1 devops2 devops3 user01 user02 user03
The desired line is
AllowGroups devops1 devops2 user02 user03
The play is
name: Remove User from AllowGroups (opening dash removed)
replace:
regexp: "(^AllowGroups)\\s.*"
backup: True
path: /etc/ssh/sshd_config.bak
replace: "\\1 {{ item }} "
with_items:
- devops3
- user01
After the play is complete I am getting the following
"AllowGroups user01"
The whole is replaced by last user in the "item".
This solution worked.
tasks:
- name: Remove User from AllowGroup and AllowUsers
replace:
regexp: "{{ item }}"
backup: True
path: /etc/ssh/sshd_config.bak
replace: "\b"
with_items:
- devops1
- user03

Only check whether a line present in a file (ansible)

In ansible, I need to check whether a particular line present in a file or not. Basically, I need to convert the following command to an ansible task. My goal is to only check.
grep -Fxq "127.0.0.1" /tmp/my.conf
Use check_mode, register and failed_when in concert. This fails the task if the lineinfile module would make any changes to the file being checked. Check_mode ensures nothing will change even if it otherwise would.
- name: "Ensure /tmp/my.conf contains '127.0.0.1'"
lineinfile:
name: /tmp/my.conf
line: "127.0.0.1"
state: present
check_mode: yes
register: conf
failed_when: (conf is changed) or (conf is failed)
- name: Check whether /tmp/my.conf contains "127.0.0.1"
command: grep -Fxq "127.0.0.1" /tmp/my.conf
register: checkmyconf
check_mode: no
ignore_errors: yes
changed_when: no
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: checkmyconf.rc == 0
Update 2017-08-28: Older Ansible versions need to use always_run: yes instead of check_mode: no.
User robo's regexp & absent method is quite clean, so I've fleshed it out here for easy use and added improvements from comments by #assylias and #Olivier:
- name: Ensure /tmp/my.conf contains 127.0.0.1
lineinfile:
path: /tmp/my.conf
regexp: '^127\.0\.0\.1.*whatever'
state: absent
check_mode: yes
changed_when: false
register: out
- debug:
msg: "Yes, line exists."
when: out.found
- debug:
msg: "Line does NOT exist."
when: not out.found
With the accepted solution, even though you ignore errors, you will still get ugly red error output on the first task if there is no match:
TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
failed: [localhost] => {"changed": false, "cmd": "grep -Fxq "127.0.0.1" /tmp/my.conf", "delta": "0:00:00.018709", "end": "2015-09-27 17:46:18.252024", "rc": 1, "start": "2015-09-27 17:46:18.233315", "stdout_lines": [], "warnings": []}
...ignoring
If you want less verbose output, you can use awk instead of grep. awk won't return an error on a non-match, which means the first check task below won't error regardless of a match or non-match:
- name: Check whether /tmp/my.conf contains "127.0.0.1"
command: awk /^127.0.0.1$/ /tmp/my.conf
register: checkmyconf
changed_when: False
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: checkmyconf.stdout | match("127.0.0.1")
Notice that my second task uses the match filter as awk returns the matched string if it finds a match.
The alternative above will produce the following output regardless of whether the check task has a match or not:
TASK: [Check whether /tmp/my.conf contains "127.0.0.1"] ***********************
ok: [localhost]
IMHO this is a better approach as you won't ignore other errors in your first task (e.g. if the specified file did not exist).
Use ansible lineinfile command, but this command will update the file with the line if it does not exists.
- lineinfile: dest=/tmp/my.conf line='127.0.0.1' state=present
Another way is to use the "replace module" then "lineinfile module".
The algo is closed to the one used when you want to change the values of two variables.
First, use "replace module" to detect if the line you are looking for is here and change it with the something else. (Like same line + something at the end).
Then if "replace" is true, It means your line is here then replace the new line with a particularity, with the new line looking.
Else the line you are looking for is not here.
Example:
# Vars
- name: Set parameters
set_fact:
newline : "hello, i love ansible"
lineSearched : "hello"
lineModified : "hello you"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
- name: Line is here, change it
lineinfile:
state : present
dest : /dir/file
line : '{{ newline }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
If the file contains "hello", it will become "hello you" then "hello, i love ansible" at the end.
If the file content doesn't contain "hello", the file is not modified.
With the same idea, you can do something if the lineSearched is here:
# Vars
- name: Set parameters
set_fact:
newline : "hello, i love ansible"
lineSearched : "hello"
lineModified : "hello you"
# Tasks
- name: Try to replace the line
replace:
dest : /dir/file
replace : '{{ lineModified }} '
regexp : '{{ lineSearched }}$'
backup : yes
register : checkIfLineIsHere
# If the line is here, I want to add something.
- name: If line is here, do something
lineinfile:
state : present
dest : /dir/file
line : '{{ newline }}'
regexp : ''
insertafter: EOF
when: checkIfLineIsHere.changed
# But I still want this line in the file, Then restore it
- name: Restore the searched line.
lineinfile:
state : present
dest : /dir/file
line : '{{ lineSearched }}'
regexp : '{{ lineModified }}$'
when: checkIfLineIsHere.changed
If the file contains "hello", the line will still contain "hello" and "hello, i love ansible" at the end.
If the file content doesn't contain "hello", the file is not modified.
You can use the file plugin for this scenario.
To set a fact you can use in other tasks ... this works.
- name: Check whether /tmp/my.conf contains "127.0.0.1"
set_fact:
myconf: "{{ lookup('file', '/tmp/my.conf') }}"
ignore_errors: yes
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: "'127.0.0.1' in myconf"
To check the file content as a condition of a task ... this should work.
- name: Greet the world if /tmp/my.conf contains "127.0.0.1"
debug: msg="Hello, world!"
when: "'127.0.0.1' in lookup('file', '/tmp/my.conf')"
Another solution, also useful for other purposes is to loop over the contents of the file, line by line
- name: get the file
slurp:
src: /etc/locale.gen
register: slurped_file
- name: initialize the matches list
set_fact:
MATCHES: []
- name: collect matches in a list
set_fact:
MATCHES: "{{ MATCHES + [line2match] }}"
loop: "{{ file_lines }}"
loop_control:
loop_var: line2match
vars:
- decode_content: "{{ slurped_file.content | b64decode }}"
- file_lines: "{{ decode_content.split('\n') }}"
when: '"BE" in line2match'
- name: report matches if any
debug:
msg: "Found {{ MATCHES | length }} matches\n{{ MATCHES }}"
when: 'listlen | int > 0'
vars:
listlen: "{{ MATCHES | length }}"
This mechanism can be use to get a specific value from a matched line if you use it for example with the jinja regex_replace filter

Resources