asking
asking

Reputation: 305

Ansible lineinfile insertafter isn't always working

So I'm trying to adding value from user's input to properties.yaml using insertafter lineinfile

this is what my code looks like:

- name: Update file
  lineinfile:
    path: "~/test/properties.yaml"
    insertafter: "this_line:"
    line: 'test123'
    mode: 0644
  become: yes

This is what my properties.yaml looks like:

this_line:
  another_line:
    - 'test1'
    - 'test2'
    - 'test3'

that_line:
  another_line:
    - 'test1'
    - 'test2'
    - 'test3'

When I tried run it for the first time, it worked, and my properties.yaml change to this:

this_line:
test123
  another_line:
    - 'test1'
    - 'test2'
    - 'test3'

that_line:
  another_line:
    - 'test1'
    - 'test2'
    - 'test3'

However, After trying to run it the second time to add line after that_line , it won't work. I was expecting something like this:

this_line:
test123
  another_line:
    - 'test1'
    - 'test2'
    - 'test3'

that_line:
test123
  another_line:
    - 'test1'
    - 'test2'
    - 'test3'

I also tried this code:

- name: Update file
  lineinfile:
    path: "~/test/properties.yaml"
    insertafter: "that_line:"
    line: 'test12345'
    mode: 0644
  become: yes

And it does change to this:

this_line:
test123
  another_line:
    - 'test1'
    - 'test2'
    - 'test3'

that_line:
test12345
  another_line:
    - 'test1'
    - 'test2'
    - 'test3'

So sometimes it works and the other it didn't. Is there something I'm missing from the use of insertafter? Thankyou.

Upvotes: 1

Views: 1082

Answers (1)

Vladimir Botka
Vladimir Botka

Reputation: 68254

Short answer: There will be no changes if the line is present in the file.

Details: The module lineinfile quote: "ensures a particular line is in a file ... when you want to change a single line in a file only." For example, given the simplified file

shell> cat /tmp/test/properties.yaml 
test123
this_line
that_line

The playbook

shell> cat pb.yml
- hosts: localhost

  tasks:

    - lineinfile:
        path: /tmp/test/properties.yaml
        line: test123

    - lineinfile:
        path: /tmp/test/properties.yaml
        insertafter: that_line
        line: test123

    - lineinfile:
        path: /tmp/test/properties.yaml
        insertafter: this_line
        line: test123

doesn't change anything if the line is present anywhere in the file

shell> ansible-playbook pb.yml

PLAY [localhost] *****************************************************************************

TASK [lineinfile] ****************************************************************************
ok: [localhost]

TASK [lineinfile] ****************************************************************************
ok: [localhost]

TASK [lineinfile] ****************************************************************************
ok: [localhost]

PLAY RECAP ***********************************************************************************
localhost: ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Solution: You can use the module blockinfile if you want to insert multiple line(s) that already exist in a file. For example, let's say you want insert line

test123

after the line this_line in the file

shell> cat /tmp/test/properties.yaml 
test123
this_line
that_line

You'll have to mark the block first. Download mark_block.yml

shell> cat tasks/mark-block.yml 
---

- name: "mark-block: Check begin marker {{ item.1.marker }}"
  ansible.builtin.command:
    cmd: >
      grep -q '# BEGIN ANSIBLE MANAGED BLOCK {{ item.1.marker }}' {{ item.0.path }}
  register: checkmarker
  ignore_errors: true
  changed_when: false

- block:
    - name: "mark-block: Create begin marker {{ item.1.marker }}"
      ansible.builtin.replace:
        path: '{{ item.0.path }}'
        regexp: '{{ item.1.regex1 }}'
        replace: |-
          {{ '#' }} BEGIN ANSIBLE MANAGED BLOCK {{ item.1.marker }}
          {{ item.1.replace1 }}
    - name: "mark-block: Create end marker {{ item.1.marker }}"
      ansible.builtin.replace:
        path: '{{ item.0.path }}'
        regexp: '({{ item.1.regex1 }}[\s\S]*?){{ item.1.regex2 }}'
        replace: |-
          \g<1>
          {{ item.1.replace2 }}
          {{ '#' }} END ANSIBLE MANAGED BLOCK {{ item.1.marker }}
  when:
    - not ansible_check_mode
    - checkmarker.rc != 0

# EOF
...

Declare the variable cl_files. The regex/replace pairs are trivial because the first and last lines of the block are the same

    cl_files:
      - path: /tmp/test/properties.yaml
        markers:
          - marker: 'this_line'
            regex1: 'this_line'
            replace1: 'this_line'
            regex2: 'this_line'
            replace2: 'this_line'

Mark the block

    - name: "Mark block {{ item.1.marker }}"
      ansible.builtin.include_tasks: tasks/mark-block.yml
      with_subelements:
        - "{{ cl_files }}"
        - markers
      loop_control:
        label: "{{ item.0.path }}"

gives

shell> cat /tmp/test/properties.yaml 
test123
# BEGIN ANSIBLE MANAGED BLOCK this_line

this_line
# END ANSIBLE MANAGED BLOCK this_line
that_line

Now you can use the marker and update the block

    - blockinfile:
        path: /tmp/test/properties.yaml
        marker: "# {mark} ANSIBLE MANAGED BLOCK this_line"
        block: |-
          this_line
          test123

gives

shell> cat /tmp/test/properties.yaml 
test123
# BEGIN ANSIBLE MANAGED BLOCK this_line
this_line
test123
# END ANSIBLE MANAGED BLOCK this_line
that_line

Example of a complete playbook for testing

- hosts: localhost

  vars:

    cl_files:
      - path: /tmp/test/properties.yaml
        markers:
          - marker: 'this_line'
            regex1: 'this_line'
            replace1: 'this_line'
            regex2: 'this_line'
            replace2: 'this_line'

  tasks:

    - name: "Mark block {{ item.1.marker }}"
      ansible.builtin.include_tasks: tasks/mark-block.yml
      with_subelements:
        - "{{ cl_files }}"
        - markers
      loop_control:
        label: "{{ item.0.path }}"

    - blockinfile:
        path: /tmp/test/properties.yaml
        marker: "# {mark} ANSIBLE MANAGED BLOCK this_line"
        block: |-
          this_line
          test123

See the Ansible role config_light. Review the documentation.

Upvotes: 1

Related Questions