Ansible lineinfile insertafter injects line at end of file - yaml

I'm using lineinfile as follows:
lineinfile dest=./hosts_exp insertafter='\[hosts1\]' line="xxxxxxxxx" state=present
My hosts_exp is as follows:
[local]
localhost
[hosts1]
[hosts2]
[hosts3]
lineinfile inserts the text after [hosts3] instead of inserting it after [hosts1].

use:
lineinfile:
dest: "./hosts_exp"
line: "xxxxxxxxx"
insertafter: '^\[hosts1\]'
state: present

It appears redundant, but you need to specify the regex too:
lineinfile:
dest: ./hosts_exp
insertafter: '\[hosts1\]'
regexp: '\[hosts1\]'
line: "xxxxxxxxx"
state=present
Why? The regexp says "look for this line". The insertafter says "inject the line here".
I tested this; here's the commit. There are a few minor changes in my commit from the line above, use as necessary.

example:
- name: "blah"
lineinfile:
dest: "/test.sh"
insertafter: 'test text'
line: "text add"
state: present

Use backrefs: yes
lineinfile:
backrefs: yes
dest: ./hosts_exp
insertafter: '\[hosts1\]'
regexp: '\[hosts1\]'
line: "xxxxxxxxx"
state=present
This flag changes the operation of the module slightly; insertbefore and insertafter will be ignored, and if the regexp doesn't match anywhere in the file, the file will be left unchanged. If the regexp does match, the last matching line will be replaced by the expanded line parameter.

Related

Ansible lineinfile won't add at end of file if line is already present somewhere

The code:
- name: Mark ending
delegate_to: localhost
lineinfile:
create: yes
insertafter: 'EOF'
path: '{{ output_file }}'
line: '=================='
Ansible version 2.8.6
This line is only added the first time the command is executed. I would expect it to add it every time.
The documentation recommends insertafter: 'EOF' for inserting lines at the end of a file, so that's what I'm using.
But now it seems to ignore every insert of this after the first.
More is inserted in between. I'm just using this command to indicate a particular thing has passed.
Setting validate: '' Changes nothing. How do I fix this?

Ansible: lineinfile module using regexp and backrefs

I am trying to ensure that a line ends with 'audit=1"'. The before state is:
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=vg00/lv_root rhgb
quiet"
The after state should be:
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=vg00/lv_root rhgb quiet
audit=1"
Working with the Ansible lineinfile module I can add that statement if its missing, but repeated runs keep appending 'audit=1"' even if it's present. I have been playing with the regexp and negative look-ahead assertions, and just cannot get the syntax correct.
Here is example play I have:
- name: grub - adjust
lineinfile:
path: /home/vwadmin/grub-test
regexp: '(GRUB_CMDLINE_LINUX=.*)"(?!audit=1")'
line: '\1 audit=1"'
backrefs: yes
state: present
Edit: Using Pacifist's suggestion I have a working solution. Confirmed to add my audit=1 statement if missing, and do nothing if present.
- name: grub - check
shell: grep -c 'audit' /etc/default/grub
register: grub_file
ignore_errors: true
- name: grub - adjust
lineinfile:
path: /etc/default/grub
regexp: '(GRUB_CMDLINE_LINUX=.*)"'
line: '\1 audit=1"'
backrefs: yes
state: present
when: grub_file.stdout == "0"
Note: I can't help but feel the reason I couldn't get it working without a pre-step, and why it doesn't work properly with check_mode, is that the negative look-ahead is not defined correctly for this case. From what I understand, the regexp match should fail when the negative look-ahead succeeds, which should cause the module to not take action. Looking at another example of this on GG I am wondering if root issue is the greedy nature of the .* wildcard in the first group match. /shrug
lineinfile is doing its work that is adding whatever is given in line and line is identified by your regexp. So no matter what value your setting already has, it will be overridden by your new line.
There are two approaches that you can use :
Using check_mode with failed_when.
Using shell module to grep the content beforehand.
Check_Mode (I can not get this working though but it has been recommended everywhere else):
- name: grub - adjust
lineinfile:
name: /home/vwadmin/grub-test
regexp: '(GRUB_CMDLINE_LINUX=.*)'
line: '\1 audit=1"'
backrefs: yes
state: present
check_mode: yes
register: presence
failed_when: presence.changed
Using grep (This one is working fine for me)
- name: grub - adjust
shell: grep -c 'audit' /home/vwadmin/grub-test
register: file
ignore_errors: true
- name: Present
debug: msg="audit is present"
when: file.stdout != "0"
- name: Not Present
debug: msg="audit is not present"
when: file.stdout == "0"
i wanted to do something similar with console kernel options and ended up with this:
- name: "Ensure console kernel options are set"
lineinfile:
path: /etc/default/grub
regexp: ^GRUB_CMDLINE_LINUX=\"(?P<defaults>.*?)\s*(?P<console> console=.*)?\"$
line: GRUB_CMDLINE_LINUX="\g<defaults> {{ kernel_console }}"
backrefs: yes
register: grub_changed
- name: "Rebuild grub"
command: grub2-mkconfig –o /boot/grub2/grub.cfg >/dev/null
when:
- grub_changed.changed
the key part of the regexp is (?P<defaults>.*?): the last question mark - which makes the match less greedy, allows the optional console=.* to be picked up without continually appending another console to the end of the line in the line as it did with your audit. main issue is that it assumes that its always the last (set of) options in the line and i always end up with a white space if there is no console defined. but it works!

Ansible add a line in file

I am trying to add a new line in a file through ansible lineinfile module. Below is the task defined in the playbook.
lineinfile:
path: /etc/httpd/file.conf
line: myfilecontent
It works fine and add a new line in the file. But when i modify the line content to some another value i.e mynewfilecontent it adds another line instead of updating it.
lineinfile:
path: /etc/httpd/file.conf
line: mynewfilecontent
Help is appreciated.
Thanks
Use the state parameter of lineinfile module and create the structure below
my_lines:
- line: myfilecontent
state: absent
- line: mynewfilecontent
state: present
The state parameter controls if the line is present, or absent in the file. See the example below
- hosts: localhost
vars:
my_lines:
- line: myfilecontent
state: absent
- line: mynewfilecontent
state: present
tasks:
- lineinfile:
path: /tmp/test.conf
create: yes
line: "{{ item.line }}"
state: "{{ item.state }}"
loop: "{{ my_lines }}"
Note 1.
The structure can be simplified by adding the state parameter only if the line is absent
my_lines:
- line: myfilecontent
state: absent
- line: mynewfilecontent
Declare default state present in the loop
state: "{{ item.state|default('present') }}"
Note 2.
The default state, defined in the module, is present, hence the state parameter may be omitted if not present in the data structure
state: "{{ item.state|default(omit) }}"
All 3 variations above are functionally equivalent.
If you want your line to potentially replace an existing line in the file then you need to provide a regexp argument to the lineinfile module.
The regular expression to look for in every line of the file.
For state=present, the pattern to replace if found. Only the last line found will be replaced.
For state=absent, the pattern of the line(s) to remove.
If the regular expression is not matched, the line will be added to the file in keeping with insertbefore or insertafter settings.
When modifying a line the regexp should typically match both the initial state of the line as well as its state after replacement by line to ensure idempotence.
The last line which matches your regexp will be replaced with your line value; if there is no match then a new line will be added.
Without a regexp argument the module will just check for an exact match to your line value.
See https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html.

replace a line in a .profile using ansible

I'm trying to insert a line at the end of my .profile using ansible. It should add to the end if the line is absent. I'm trying the following, but it does'nt do anything:
- name: update profile
lineinfile:
dest: ~/.profile
regexp: 'PATH=$PATH:$HOME/.local/bin'
state: absent
insertafter: EOF
line: 'PATH=$PATH:$HOME/.local/bin'
Thanks in advance for any suggestions
Use state: present if you want line to exist, and not sure why you want to use regexp in this case.
Also EOF is the default value for insertafter, so no need to define it.
- name: update profile
lineinfile:
dest: ~/.profile
state: present
line: 'PATH=$PATH:$HOME/.local/bin'

How to uncomment line with Ansible replace module?

It's very common for vanilla configurations files to have settings commented out in default configuration files. For example
#HEAP_SIZE=2g
How do I remove the comment character, "#" in this case, with the Ansible replace module?
- name: Uncomment out HEAP_SIZE
replace:
dest: //etc/some_path/app.conf
replace="what to put here to remove #???"
regex="#HEAP_SIZE=2g"
Resulting in
HEAP_SIZE=2g
You can use lineinfile for your task:
- name: Uncomment parameters
lineinfile:
dest: app.conf
regexp: (?i)^\s*#\s*({{ item }}.*)
line: \1
backrefs: yes
with_items:
- heap_size
- aNoThEr_setting
Simply:
replace: "HEAP_SIZE=2g"
You'd also want to make sure it's from the beginning of the line.
Generally for this use case lineinfile module fits better.

Resources