playbook to comment out one line and add another line in a file

how do i use ansible to remotely comment out a line in a docker-compose file and add a line

like this

#image: gralog/graylog:4.2.5
image: graylog/graylog4.2.8

then do a docker compose down
and docker-compose up -d

i need to run this on multiple nodes

I assume, the expected result is below. If not confirm your example
and ignore the rest here

#image: graylog/graylog4.2.5
image: graylog/graylog4.2.8

For example, create a dictionary of the disabled and enabled versions

    versions:
      4.2.5: false
      4.2.8: true

Then the *lineinfile* task below should comment out disabled versions
and add enabled versions

    - lineinfile:
        path: docker-compose
        regex: '^\s*#*\s*image: graylog/graylog{{ item.key }}\s*$'
        line: '{{ hash }}image: graylog/graylog{{ item.key }}'
      loop: "{{ versions|dict2items }}"
      vars:
        hash: "{{ item.value|ternary('', '#') }}"

do ytou mean like this

See the complete example below

cat docker-compose

image: graylog/graylog4.2.5

Got it, thanks!

I was abl to get my first node changed but the other 2 had this added

image: gralog/graylog:4.2.5
#image: graylog/graylog4.2.5
image: graylog/graylog4.2.8

I don’t think you can get line_in_file to do what you want. line_in_file is designed to operate on a single line. Keeping the commented out line greatly complicates matters. How many patchlevels to you intend to maintain as comments?

You may be able to do it with replace and a lot of testing. But I’d strongly encourage you to reconsider using # as a poor replacement for revision control.

'y' is missing in 'gralog/graylog:4.2.5' in the first line. This is
the reason why the first line wasn't replaced. Instead, the second
line was added.

it only works for first node. When i run the playbook against 3 nodes the 2nd and 3rd node have this

image: graylog/graylog:4.2.5
#image: graylog/graylog4.2.5
image: graylog/graylog4.2.8

this is the playbook

I tested with another pb and it works fine on all 3 nodes

it only works for first node. When i run the playbook against 3 nodes the
2nd and 3rd node have this

image: graylog/graylog:4.2.5
#image: graylog/graylog4.2.5
image: graylog/graylog4.2.8

Quoting from *lineinfile* parameter *regexp*
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html#parameter-regexp

  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.

Note the difference between the states *present/absent*. If *absent*
all matches are removed. If *present* only the last one is replaced.
The default state is *present*.

One of the scenarios on how to proceed might be removing all redundant
lines first. For example, given the file

cat docker-compose

image: graylog/graylog4.2.5
#image: graylog/graylog4.2.5
image: graylog/graylog4.2.8

The playbook

cat pb.yml

- hosts: localhost
  vars:
    versions:
      4.2.5: false
      4.2.8: true
  tasks:
    - name: Remove versions
      lineinfile:
        state: absent
        path: docker-compose
        regexp: '^\s*#*\s*image: graylog/graylog{{ item.key }}\s*$'
      loop: "{{ versions|dict2items }}"
      when: force_remove_versions|d(false)|bool
    - name: Add versions
      lineinfile:
        path: docker-compose
        regexp: '^\s*#*\s*image: graylog/graylog{{ item.key }}\s*$'
        line: '{{ hash }}image: graylog/graylog{{ item.key }}'
      loop: "{{ versions|dict2items }}"
      vars:
        hash: "{{ item.value|ternary('', '#') }}"

removes all matching lines and creates the specified ones

ansible-playbook pb.yml -e force_remove_versions=true

PLAY [localhost]

> cat pb.yml
- hosts: localhost
  vars:
    versions:
      4.2.5: false
      4.2.8: true
  tasks:
    - name: Remove versions
      lineinfile:
        state: absent
        path: docker-compose
        regexp: '^\s*#*\s*image: graylog/graylog{{ item.key }}\s*$'
      loop: "{{ versions|dict2items }}"
      when: force_remove_versions|d(false)|bool
    - name: Add versions
      lineinfile:
        path: docker-compose
        regexp: '^\s*#*\s*image: graylog/graylog{{ item.key }}\s*$'
        line: '{{ hash }}image: graylog/graylog{{ item.key }}'
      loop: "{{ versions|dict2items }}"
      vars:
        hash: "{{ item.value|ternary('', '#') }}"

The playbook is idempotent

cat docker-compose

#image: graylog/graylog4.2.5
image: graylog/graylog4.2.8

ansible-playbook pb.yml

PLAY [localhost]

i got it to work the simple way with multiple tasks

I have another requirement to down the container on node01 first

docker-compose down
then run the pb,
then docker-compose up -d

wait 30secs or so

repeat with node02
repeat with node03

is there docker module or I should just use shell module?

I believe it’s always better to use designated Modules over or that said … you may want to get familiar (or at least be aware of ) where you can find any modules. that said … here is the (from that list) you can study it online or with the built-in help like one last comment … just like using designated modules for designated tasks in Ansible it’s always best to use designated Threads for different problems. This may not make a difference for you just now, but others who may be following the list have a chance to benefit from the solution somebody is digging out

does this mean I cannot keep the old commented line?

#image: graylog/graylog4.2.5

requirement is I need to have this line commented

Assuming you only have one line for each version, it will assure that the line for any version that’s marked false will be commented, and any line for a version marked true will be uncommented.

Where it gets hairy is when you have more than one line for any version. In that case, the second task only manages the first of the lines for that version. That shouldn’t happen, but you’ve already seen that it does sometimes. When it does, you can run the playbook with force_remove_versions set to true, and it will remove all the matching lines - both good and bad - before the second task puts the single entries back in the file, properly commented or not as the case may be.

does this mean I cannot keep the old commented line?

#image: graylog/graylog4.2.5

It's tricky. You can write scripting to do anything you want to do,
but it's not necessarily built-in precisely the way you want it to
behave.

It's why many config files, such as those in /etc/sudoers.d/, deploy
files rather than editing lines of /etc/sudoers.

Quoting from *lineinfile* parameter *regexp*
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html#parameter-regexp

  For state=present, the pattern to replace if found. Only the last
  line found will be replaced.

Thank you!

Assuming you only have one line for each version, it will assure
that the line for any version that's marked false will be
commented, and any line for a version marked true will be
uncommented.
Where it gets hairy is when you have more than one line for any
version. In that case, the second task only manages the first of

                                                           ^^^^^
                                                           last