Appending a line to a file if it doesn't exist using Puppet

Two things:

Augeas

Puppet integrates well with a tool called Augeas:

Augeas is a lovely tool that treats config files (well, anything really, but it’s mostly about config files) as trees of values. You then modify the tree as you like, and write the file back.

For files which Augeas understands, it's very easy to make programmatic changes without having to worry about comments, whitespace, parsing, or any other sticky details. Also, Augeas suppors the sudoers file format.

Sudoers includes

As of sudo version 1.7.2, it supports an includedir directive which allows you to specify a directory to extend sudoers. Files in that directory must contain snippets of valid sudoers code, and sudo will read and use them exactly as though they'd been included in the original file. I make use of this in Puppet with a custom resource:

define sudo-include( content ) {
    file { "/etc/sudoers.d/$name":
        content => $content,
        mode => 0440,
        user => root,
        group => root,
    }
}

[...]

sudo-include { "moreusers":
    content => "\
joe   ALL=(ALL)  ALL
mike  ALL=(ALL)  NOPASSWD:ALL
"
}

When you use the sudo-include resource, it automatically creates the file in the right place with the right permissions, and all you need to do is give it a name and content.


Handyman5 suggested using Augeas. Here is an example of how this could be done:

If you want to do have both entries on one line using +=, you can use Augeas this way:

augeas { "Add http_proxy & https_proxy to sudoers":
   context => "/files/etc/sudoers",
   # Only if no node exists for http_proxy
   onlyif  => "match Defaults/env_keep/var[. = 'http_proxy'] size==0",
   changes => [
     # Create a new Defaults line for the two variables
     "ins Defaults after Defaults[last()]",
     # Make this Defaults line a += type
     "clear Defaults[last()]/env_keep/append",
     # assign values to the two variables
     "set Defaults[last()]/env_keep/var[1] http_proxy",
     "set Defaults[last()]/env_keep/var[2] https_proxy",
   ],
 }

However, you might want to avoid correlating the two variables, so that it will still work if one variable is already set. In that case, I would split the problem in two, creating a new Defaults node only for http_proxy, and reusing it (with a dependency) for https_proxy.