12

I'm new to Ansible and trying to modify a line in /etc/default/grub to enable auditing.

I need to add audit=1 within the quotes somewhere on a line that looks like:

GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap biosdevname=0 net.ifnames=0 rhgb quiet net.ifnames=0"

So far I've managed to delete the line and am only left with

net.ifnames=0, audit=1

when I use something like

lineinfile:
  state: present
  dest: /etc/default/grub
  backrefs: yes
  regexp: "net.ifnames=0"
  line: "\1 audit=1"

Can this be done?

Jeff Bilbro
  • 149
  • 1
  • 1
  • 9

2 Answers2

22

You may try this:

- lineinfile:
    state: present
    dest: /etc/default/grub
    backrefs: yes
    regexp: '^(GRUB_CMDLINE_LINUX=(?!.* audit)\"[^\"]+)(\".*)'
    line: '\1 audit=1\2'

This will add audit=1 (with a leading space) just before closing double quote. It will not match without double quotes. And it tries to be idempotent: doesn't match lines that already have audit (with a leading space) after GRUB_CMDLINE_LINUX=.

I'd recommend to use sites like regex101 to test your regular expressions first (there's also a substitution mode there).
When you're satisfied with the result, proceed with the Ansible task.

Carolus
  • 477
  • 4
  • 16
Konstantin Suvorov
  • 65,183
  • 9
  • 162
  • 193
  • @Jeff Bilbro - if you need to only add it to lines that have "net.ifnames=0" you'll need to add that to the regexp line that Konstantin Survorov provided. Probably something like this: `regexp: '^(GRUB_CMDLINE_LINUX=.*net.iframes=0.*(?!.*audit)\"[^\"]+)(\".*)'` – dan_linder Oct 01 '16 at 15:32
  • 2
    To make this also work when GRUB_CMDLINE_LINUX is empty you can use `^(GRUB_CMDLINE_LINUX=(?!.*audit)\"[^\"]*)(\".*)` – acidtv May 15 '19 at 10:15
  • For anyone else landing here with a somewhat related question: On Ubuntu 18.04 I wanted to use `GRUB_CMDLINE_LINUX_DEFAULT=` instead of `GRUB_CMDLINE_LINUX=` because that seems to be the one used normally (where all the other parameters are). – Carolus Dec 15 '19 at 19:43
  • Can anyone explain why does this permit any characters after the second double quote? Doesn't a proper line start and end with double quotes (or not have any double quotes on the line at all)? – Carolus Dec 15 '19 at 19:47
  • 1
    could this line be added to a separate file in `/etc/default/grub.d/` dir and have the same effect? – Jean Monet Dec 23 '20 at 19:44
9

I wanted to make sure the parameter is also set to the correct value, so I used this replace invocation:

replace:
  path: /etc/default/grub
  regexp: '^(GRUB_CMDLINE_LINUX=(?:(?![" ]{{ option | regex_escape }}=).)*"?)\s*(?:{{ option | regex_escape }}=\S+\s*)?(.*")$'
  replace: '\1 {{ option }}={{ value }}\2'
vars:
  option: audit
  value: 1

This works if the option wasn't present previously, if it was but had the wrong option (only changes the value then) and if the whole string was empty (but adds a space before the option then). Also, it uses regex_escape to correctly work with option names that contain dots and the likes, and you only have to specify them once.

jplitza
  • 191
  • 1
  • 4
  • Very, very nice! – solarchemist Oct 24 '21 at 07:31
  • 1
    There is one problem with the regexp: if the option is position as the first after the quote, for example: ```GRUB_CMDLINE_LINUX="elevator=noop"``` then your regex will chew up the initial quote and resulting line ends without he first quote and it breaks grub. This is the fix: ```regexp: '^(GRUB_CMDLINE_LINUX="(?:(?!{{ option | regex_escape }}=).)*)([ ]{{ option | regex_escape }}=\w+[ ])?[ ]?(?:{{ option | regex_escape }}=\S+)?(.*\")$'``` and finally ```replace: '\1{{ option }}={{ value }}\3'``` – Jakov Sosic Dec 27 '22 at 01:11
  • You are right, fixed that (although in another way, since your answer probably matches partial options, like `ata.elevator` or something like that) – jplitza Jan 02 '23 at 10:50