Puppet: 'creates' does not create a new file - puppet

I would like to run a command, only if a file does not exist.
This is what I tried:
exec { 'test':
command => '/usr/bin/echo Test',
creates => '/etc/test/test-init'
}
But unfortunately this file is not being created. The puppet agent works without throwing an error.

The creates parameter of an Exec does not cause Puppet to create the designated file. Rather it instructs Puppet to use the existence of that file to determine whether the command should be run, on the supposition that the command will create it when it runs.
That's often used with commands that naturally create a file or directory, but you can do it synthetically, too. For example:
exec { 'test':
command => '/usr/bin/echo Test && touch /etc/test/test-init',
creates => '/etc/test/test-init',
provider => 'shell'
}

Related

Manage multiple files permissions using puppet

How do I set file permission from list of file names from cat command?
For example, below command returns 3 file names:
$ cat /tmp/test | grep file
/etc/systemd/file_1.log
/etc/systemd/file_2.log
/etc/systemd/file_3.log
How do I use puppet to run the command, get the file names and then loop the 3 file names and set permission accordingly?
The files are resources and if you want to manage a resource you have to know it's there so dynamically created log files are not easy. If you know the file names already then you can use something like this and pass an array into the file resource.
file { ['/etc/systemd/file_1.log',
'/etc/systemd/file_1.log',
'/etc/systemd/file_1.log'] :
ensure => 'file',
mode => '0644',
owner => 'root',
group => 'root',
}
An other method might be to use an exec
exec { 'chmod 644 /etc/systemd/file_*.log':
path => ['/usr/bin', '/usr/sbin',],
}
But you really need something like an onlyif or unless or this is going to execute every 30 minutes and that breaks the idempotent rule we try and apply with Puppet code where things only change if they need correcting. So you're going to need a command line that'll test the permissions and return a boolean to the onlyif.
There are more details here https://puppet.com/docs/puppet/5.5/types/exec.html
A alternative (and the way I'd do it) would be to expose the contents of that file via an external fact which passes the list of files to Puppet to use in the catalog compilation. An external fact can be a bash script so I'd create a file called /etc/facter/facts.d/logfiles.sh, obviously I'd deploy this using Puppet.
#!/usr/bin/env bash
logfiles=($(grep file /tmp/test))
echo "logfiles=${logfiles[*]}"
Then in my Puppet code I'd have something like this;
$logfiles.each |String $logfile| {
file { $logfile :
ensure => 'file',
mode => '0644',
owner => 'root',
group => 'root',
}
}
So when the Puppet run happens the list of log files will be returned to Puppet via the facts and then each file listed is defined as a resource with the correct permissions.
How do I set file permission from list of file names from cat command?
There are two main alternatives, but I observe first that your example is of the output from grep, not cat, and that the cat in that example is superfluous. Nevertheless, those details don't change the big picture -- substantially the same approaches are applicable for data output by any command.
It would be more idiomatic to write a custom fact that captures the filenames (as of the time of each catalog request), and use that information to create the appropriate File resources.
Custom facts are not that hard, but the full details are more than would be appropriate for an SO answer. Supposing that you have a fact $facts['systemd_logs'] whose value is an array of the absolute filenames, you can compactly express the whole group of wanted File resources like so:
file { $facts['systemd_logs']:
mode => '0644',
}
(or whatever mode it is that you want).
It would be quicker (and dirtier) to use an Exec resource to run an appropriate command:
exec { 'ensure correct file permissions':
command => 'chmod 0644 $(/bin/grep file /tmp/test)',
onlyif => '/bin/grep -q file /tmp/test',
provider => 'shell',
}

puppet onlyif logical expression

I am trying to set up a Puppet exec resource for a Windows Server based on a Facter variable. But, it looks like onlyif accepts only command files and not an inline expression.
Could someone please help me set this expression in onlyif (either as a conditional expression or as inline-dos command)
exec { 'C:\Users\validate.cmd validate':
onlyif => "$validate_enabled" == true, => This not recognized as command
provider => powershell,
}
Thanks in advance.
There are a couple issues here. The first is that the syntax for the onlyif is incorrect. That attribute executes a command and checks for success, so you would want to change it to this to remove Puppet string and shell syntax issues:
exec { 'C:\Users\validate.cmd validate':
onlyif => $validate_enabled,
provider => powershell,
}
Note the first part of what I wrote (executes a command). You do not want to execute a command in this instance. You want to check the value of a boolean Facter fact. Therefore, you would be checking this with Puppet DSL and not a shell command.
Facter 3/Puppet 4:
# use fact hash
if $facts['validate_enabled'] {
exec { 'C:\Users\validate.cmd validate': provider => powershell }
}
Facter 2/Puppet 3:
# specify global variable for safety
if $::validate_enabled {
exec { 'C:\Users\validate.cmd validate': provider => powershell }
}
This will fix your conditional and give you your desired behavior.

Copying a file only once in puppet

I am new to puppet, I am trying to copy a file to remote server for installation of a package, This file would get deleted a few minutes after installation. Is there a way to copy this file only once.? like the example below.
class absent_file {
file { '/tmp/hello-file':
ensure => 'present',
replace => 'no',
content => "From Puppet\n",
mode => '0644',
once => true
}
}
So, I have figured out how to do it. Instead of using the file module, I ended up using command and touching another which would not get deleted along with installation process. The key can be deleted but the somefile.txt would be still present and the process would become idempotent.
exec {'add key':
command => "echo 'SharedKeyfile' > /key/location && touch /key/location/somefile.txt'",
onlyif => "test ! -f /key/location/somefile.txt",
}

Reading from file /etc/resolv.conf and populating in named.conf.options

I'm using puppet to generate my named.conf.options file, in order to do this I'd like it to use the forwarders defined in /etc/resolv.conf. What's the best way of doing this, I've been doing it like this (where named.conf.options.erb contains ) - but this runs constantly.
file { '/etc/bind/named.conf.options':
ensure => present,
content => template('my_template_location/named.conf.options.erb'),
replace => true,
}
->
exec { "add_nameserver":
command => '/bin/sed -i "s/<name_server>/$(grep nameserver/etc/resolv.conf | tr -d [a-z])/g" /etc/bind/named.conf.options',
}
An exec will always run unless it has something to limit it. There are a number of parameters you can set.
In your case, it sounds like you want the exec to run only when your file changes. You might want to use the refreshonly parameter on your exec.
First, change the require arrow to a notify arrow, from -> to ~>. This will cause puppet to refresh the exec whenever the file changes.
Second, add refreshonly => true to your exec. This will cause the exec to only run when it is refreshed by some other resource.
You'll end up with the following:
file { '/etc/bind/named.conf.options':
ensure => present,
content => template('my_template_location/named.conf.options.erb'),
replace => true,
}
~>
exec { "add_nameserver":
command => '/bin/sed -i "s/<name_server>/$(grep nameserver/etc/resolv.conf | tr -d [a-z])/g" /etc/bind/named.conf.options',
refreshonly => true,
}
You can check out some of the other ways to limit an exec on the Puppet Type Reference Page.
You can not get desired state this way because you are modifying the same resource (file /etc/bind/named.conf.options) with two different declarations.
Normally you have to avoid exec resources in Puppet because is difficult to keep state and idempotence while executing "old-school" commands.
So, the best way to get your desired behaviour is to create a custom fact [1] that exposes your nameservers to any resource and then include it in your ERB template.
Facter.add(:nameservers_array) do
setcode do
nameservers = Facter::Core::Execution.exec('grep nameserver/etc/resolv.conf | tr -d [a-z]')
nameservers_array = nameservers.split(" ")
nameservers_array
end
end
You have another example here: https://www.jethrocarr.com/2013/11/05/exposing-name-servers-with-puppet-facts/
[1] https://docs.puppetlabs.com/facter/latest/fact_overview.html

Need some help in appending more than one line to a file in puppet

Using this manifest
file_line { 'sudo_rule':
path => '/etc/sudoers',
line => '%sudo ALL=(ALL) ALL',
}
Puppet adds a line, but I want more than one line to append to a file.
File_line only
Ensures that a given line is contained within a file. (according to the doc)
So you may add more lines to your puppet configuration file and it will work out of the box since puppet only ensures that they are there but doesn't add them continuously.
Maybe you tell us what your usecase is so that we can help you better. Another option would also be augeas to load config files into a tree like datastructure and edit the appropriate values in place.
Example usage:
augeas { "sshd_config":
context => "/files/etc/ssh/sshd_config",
changes => [
"set PermitRootLogin no",
],
}

Resources