I would like to change a line in /etc/default/grub with puppet to this:


I've tried to used augeas which seems to do this magic:

   exec { "update_grub":
    command => "update-grub",
    refreshonly => true,

  augeas { "grub-serial":
    context => "/files/etc/default/grub",
    changes => [
      "set /files/etc/default/grub/GRUB_CMDLINE_LINUX[last()] cgroup_enable=memory",
    notify => Exec['update_grub'],

It seems to work, but the result string is not in quotes and also I want to make sure that any other values will be separated by space.


Is there some mechanism how to append values and escape the whole thing?

GRUB_CMDLINE_LINUX="quiet splash cgroup_enable=memory"

For the quoting part, you could use augeasproviders's shellvar provider and force the quoting style:

shellvar { 'GRUB_CMDLINE_LINUX':
  ensure   => present,
  target   => '/etc/default/grub',
  value    => 'cgroup_enable=memory',
  quoted   => 'double',

This will use Augeas internaly (as a Ruby library) on the agent, only in a smarter way.

As for appending to existing values, there's two options:

  • Get the current value using a fact (use for example augeasfacter to retrieve it), analyze it in your manifest and append to it using the shellvar type;
  • Improve the shellvar provider so that it appends to the value instead of replacing it.

First option

The fact

The following file can be distributed in your module in ${modulepath}/${modulename}/lib/facter/grub_cmdline_linux.rb and deployed using pluginsync.

require 'augeas'
Facter.add(:grub_cmdline_linux) do
  setcode do
    Augeas.open(nil, '/', Augeas::NO_MODL_AUTOLOAD) do |aug|
        :lens => 'Shellvars.lns',
        :name => 'Grub',
        :incl => '/etc/default/grub',
        :excl => []
      aug.get('/files/etc/default/grub/GRUB_CMDLINE_LINUX').gsub(/['"]/, '')

This will return the current value of the fact as a string, and remove the quotes around the value. It requires the Augeas Ruby library on the agent, which I suppose you have if you use the augeas type already.

Puppet code

The next step is to make use of this value to check if your target value is included. You can split the string and use the stlib module functions for this:

$value = 'cgroup_enable=memory'

# Split string into an array
$grub_cmdline_linux_array = split($::grub_cmdline_linux, ' ')

# Check if $value is already there to determine new target value
$target = member($grub_cmdline_linux_array, $value) ? {
  true  => $grub_cmdline_linux_array,
  false => flatten([$grub_cmdline_linux_array, $value]),

# Enforce new target value
# Use the array and force the 'string' array type
shellvar { 'GRUB_CMDLINE_LINUX':
  ensure     => present,
  target     => '/etc/default/grub',
  value      => $target,
  array_type => string,

Run it

Notice: /Stage[main]//Shellvar[GRUB_CMDLINE_LINUX]/value: value changed ['quiet splash usbcore.old_scheme_first=1'] to 'quiet splash usbcore.old_scheme_first=1 cgroup_enable=memory'

Check idempotence:

Notice: Finished catalog run in 0.17 seconds

Second option

If you'd like to have a go at the second option, the best way would probably be to send a (nice) PR to augeasproviders's shellvar type adding an array_append parameter (or a better name):

shellvar { 'GRUB_CMDLINE_LINUX':
  ensure       => present,
  target       => '/etc/default/grub',
  value        => 'cgroup_enable=memory',
  array_append => true,
  array_type   => 'double',

This parameter would treat the value as an array, and append to the existing values if the value is not found already. This would require Ruby coding, but would be reusable in lots of other cases ;-)

Hint: this should happen here.


(for ubuntu users only)

As altering /etc/default/grub is not so nice ..

Just deploy /etc/default/grub.d/memory.cfg


Then update-grub

(see the /etc/default/grub.d/*.cfg feature )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.