puppet file_line: remove line with unknown number of whitespaces

by Acki   Last Updated May 16, 2019 00:01 AM

I'd like to remove a line from the sudoers. All works fine as long as I give the exact line. But there might be differences in white space on some machines. Couldn't find a workaround even with "match".

file_line { '/etc/sudoers':
          ensure => absent,
          path   => '/etc/sudoers',
          line   => 'myuser  ALL=NOPASSWD:/bin/su -', }

Any ideas? Thanks Acki

Tags : puppet


Answers 4


Put your user information into a new file, place this new file in /etc/sudoers.d

See the sudoers man page for better details. (Mostly just observe that name of file in /etc/sudoers.d cannot contain '~' or a '.'. So john_doe is good, while john.doe is bad)

This will completely remove the difficulty you're having, trying to exactly match entries in a file. Many services have ?.d subdirectories to make this easier. (cron.d, sysctl.d, /etc/modprobe.d,... and so on)

lornix
lornix
May 27, 2014 13:17 PM

file_line is an "ok" resource for ensuring lines appear in a file but not so good for ensuring lines are not there -- which is why I completely stopped using it.

If you really need to manage this file piecemeal and need to ensure the line is absolutely gone, the augeas resource will accomplish it much better. It's rather complicated but does the job really well. And there should already be a lens for the sudoers file.

But lornix is correct. You should not be managing this file this way. It is laden with dangers and there be monsters here.

Prefer templates and include files.

To answer your question explicitly: You can't. One look at the source code for file_line shows that it only looks for an exact match -- white space and all.

  def destroy
    local_lines = lines
    File.open(resource[:path],'w') do |fh|
      fh.write(local_lines.reject{|l| l.chomp == resource[:line] }.join(''))
    end
  end
rojs
rojs
June 01, 2014 17:00 PM

I would not use ensure => absent here as it only allows for an exact match. A simple but effective workaround would be using a comment (or empty) line along with a match regex to look for to replace:

file_line { 'sudoers-myuser':
          path   => '/etc/sudoers',
          line   => '# myuser  ALL=NOPASSWD:/bin/su -', 
          match  => 'myuser.*ALL=NOPASSWD:/bin/su.*-',
}

This would introduce line even if the match is not present in the file, but as the content is a no-op, it should not present any serious issues. Take care to escape regex special characters in the match line.

syneticon-dj
syneticon-dj
June 09, 2015 15:14 PM

Here's an interesting solution I just discovered. Posting as an answer for anyone in the future who can benefit from it.
file_line { 'sudoers-myuser': ensure => 'absent', path => '/etc/sudoers', match => 'myuser.ALL=NOPASSWD:/bin/su.-', match_for_absence => true, }

Note: For some reason, if we want to remove an entry based on 'match', 'replace' must be false.

Explanation (as best I can offer).
file_line does not allow for a RegEx in the line parameter, only in the match parameter.

Normally, 'line' is required, but match_for_absence obviates the need for 'line'.

The documentation states "match_for_absence: An optional value to determine if match should be applied when ensure => absent. If set to true and match is set, the line that matches match will be deleted."

Should you need to remove multiple matches of the same RegEx, add this parameter:
multiple => true,

more details here file_line cannot delete lines by regexp?

Scottie H
Scottie H
May 15, 2019 23:12 PM

Related Questions


Updated September 13, 2015 12:00 PM

Updated September 23, 2015 08:00 AM

Updated January 11, 2017 09:00 AM

Updated April 16, 2017 17:01 PM

Updated April 04, 2019 05:01 AM