41

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?

Pavel Bernshtam
  • 4,232
  • 8
  • 38
  • 62

10 Answers10

50

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?

udondan
  • 57,263
  • 20
  • 190
  • 175
  • I need to add a new property to my deployment environment. This includes production and simulation servers. My ansible runs on cron and on server start (I create simulation servers from an image on demand) The default value for the property is localhost, but on some servers I will change it manually later. I thought that ansible is good for handling such change – Pavel Bernshtam Mar 16 '15 at 11:34
  • 1
    Ansible will be able to provision your new servers, and configure them as well. Its just that in your playbook where you used lineinfile module, you have specified ansible to always have a line `couchbase.host=127.0.0.1` and over write the previous value. That's how you have configured it. – Zasz Mar 17 '15 at 15:55
  • Try using the when clause as @udondan mentioned – Zasz Mar 17 '15 at 16:00
  • i think in ansible I witness a problem with this approach. its fine if grep finds something. but if grep reurns nothing, I get "msg": "non-zero return code", "rc": 1, "start": "2019-03-09 00:50:15.184315", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []} – openCivilisation Mar 08 '19 at 13:53
  • 3
    @openCivilisation That's because grep returns an exitcode `1` on no match. Ansible assumes non-zero exit codes are failures of the script/command, and thus aborts. What I did was: `shell: 'grep "my_pattern" some_file -c || true'` for the check and then `when: test_grep.stdout == "0"` for the condition. – dthor Mar 18 '19 at 17:19
  • I tried ur answer for blockinline module. Its not adding required lines. Do I need to do add any arguments for block in line –  Jul 21 '20 at 03:38
29

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

Nick Maynard
  • 641
  • 5
  • 10
  • This should be the accepted answer as the use of shell is discouraged by Ansible unless there are no applicable modules. – TYPKRFT Sep 06 '22 at 12:58
7

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"
kmjackson788
  • 81
  • 1
  • 5
4

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 }}"
2

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
avmusa
  • 21
  • 3
1

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
Community
  • 1
  • 1
Je.Go
  • 81
  • 1
  • 7
0

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
zaufi
  • 6,811
  • 26
  • 34
0

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"
Raja S
  • 11
  • 1
0
- 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

CbIpHuK
  • 26
  • 4
-1
- 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'
karel
  • 5,489
  • 46
  • 45
  • 50