replace module works different in version 2.1.2?

I wrote the below to ensure certain options are set in a server’s /etc/fstab file for CIS compliance:

  • name: Describe file system options
    set_fact:
    filesystems:

  • fs: /tmp
    options:

  • nodev

  • nosuid

  • fs: /home
    options:

  • nodev

  • fs: /dev/shm
    options:

  • nodev

  • nosuid

  • noexec

  • name: CIS - Set options for file systems
    replace: dest=/etc/fstab
    regexp=‘([1]\s+{{item.0.fs}}\s+\w+\s+(?!.\b{{item.1}}\b))([\w,]+)(\s+[0-9]\s+[0-9])$’
    replace=‘\1\2,{{item.1}}\3’
    with_subelements:

  • “{{filesystems}}”

  • options

My fstab file starts out like this:

/dev/mapper/VolGroup00-root / xfs defaults 1 1
UUID=55b51f79-af10-4590-88df-8aefeeedb3fc /boot xfs defaults 0 0
/dev/mapper/VolGroup00-home /home xfs defaults 0 0
/dev/mapper/VolGroup00-tmp /tmp xfs defaults 0 0
/dev/mapper/VolGroup00-var /var xfs defaults 0 0
UUID=c56d0641-b1ef-4ef5-ba3c-1dfb983e28ce swap swap defaults 0 0

The expectation is that options listed under a given file system name will be added to the options for that file system. For example:

/dev/mapper/VolGroup00-home /home xfs defaults,nodev 0 0
/dev/mapper/VolGroup00-tmp /tmp xfs defaults,nodev,nosuid 0 0

This worked perfectly on Ansible version 1.9.4, but when I ported it to Ansible 2.1.2, it broke. When I run it on 2.1.2, Ansible no longer detects that the options have already been applied and applies them again. Each time the playbook is run against the servers, another set of options is added to the already existing set:

/dev/mapper/VolGroup00-home /home xfs defaults,nodev,nodev 0 0
/dev/mapper/VolGroup00-tmp /tmp xfs defaults,nodev,nosuid,nodev,nosuid 0 0

What changed between Ansible versions 2.1.2 and 1.9.4?
The clients are a mix of OEL and RHEL versions 6 and 7. I get the same results regardless of the OS releases and versions.

-Mark


  1. /-\w ↩︎

It is very definitely related to version 2 of Ansible. I downgraded the version of Ansible on my server to 1.9.4 and ran the playbook again. It ran perfectly and no changes were made to the target system. I upgraded to version 2.2.0 and tested again, and it added another set of options to the fstab file even though the options were already present. So something changed between Ansible versions 1.9 and 2.
-Mark

It appears to be the negative lookahead assertion in the regular expression: “(?!.*\b{{item.1}}\b)”. This is the part that looks to see if the string stored in {{item.1}} is present in the line being examined. If the string is present, the line should be declared a no-match and the line is skipped. If the string is not present, the line is a match and the replace should be execute. For some reason this appears to be broken in Ansible version 2.
-Mark