Many Puppet users encounter this common frustration: you want to check whether a file or directory actually exists on the filesystem, but Puppet's native resource references don't provide this functionality. The File
resource type in Puppet is declarative - it describes what the system should look like, not what it currently looks like.
These common attempts don't work as expected:
if File['/some/path'] {
# This always returns true because it's checking resource definition
}
if defined(File['/some/path']) {
# Only checks if the resource is declared in Puppet code
}
Puppet provides a file()
function that can read file contents during catalog compilation. We can leverage its behavior when files don't exist:
$file_exists = file('/some/path', '/dev/null') != '/dev/null'
if $file_exists {
notify { 'File exists': }
} else {
fail('The specified file does not exist')
}
For directories, we need a slightly different approach:
define directory_exists($path) {
$check = generate('/bin/sh', '-c', "test -d ${path} && echo exists || echo missing")
if $check == 'missing' {
fail("Directory ${path} does not exist")
}
}
directory_exists { 'check_my_dir':
path => '/some/directory'
}
For more complex scenarios, consider creating a custom fact:
# In module/lib/facter/file_exists.rb
Facter.add('file_exists') do
setcode do
File.exist?('/some/path').to_s
end
end
Then use it in your manifest:
if $facts['file_exists'] == 'true' {
# Your code here
}
Remember that these checks happen during catalog compilation. For frequently changing filesystems, consider:
- Using exported resources
- Implementing the check in an exec resource with
onlyif
- Creating a custom provider
Here's a complete example that checks multiple paths:
class check_paths {
$paths_to_check = ['/etc/puppetlabs', '/tmp/nonexistent']
$paths_to_check.each |$path| {
$check = generate('/bin/sh', '-c', "test -e ${path} && echo exists || echo missing")
if $check == 'missing' {
notify { "${path} does not exist": }
# Or fail() if you want to stop execution
} else {
notify { "${path} exists": }
}
}
}
When working with Puppet manifests, you might need to verify whether a file or directory exists on the target system before proceeding with certain operations. The naive approach of using if File["/some/path"]
doesn't work as expected because it checks for resource declaration in the catalog, not actual filesystem existence.
Puppet doesn't provide a direct way to check filesystem existence in its DSL, but we can achieve this through several methods:
# Method 1: Using the find_file function (Puppet 4.8+)
if find_file('/some/path') != undef {
# Path exists
} else {
fail("Required path /some/path does not exist")
}
# Method 2: Using a custom fact
# Add this to your custom fact library
Facter.add('path_exists') do
setcode do
File.exist?('/some/path')
end
end
# Then in your manifest:
if $facts['path_exists'] {
# Proceed
}
For older Puppet versions, you can use the generate
function to call external commands:
$path_exists = generate('/usr/bin/test', '-e', '/some/path')
if $path_exists.empty {
fail("Path /some/path does not exist")
}
When implementing these checks:
- Prefer custom facts for performance (they're evaluated once per run)
- Use absolute paths for all file operations
- Consider adding descriptive fail messages
- Test edge cases (symlinks, permissions, etc.)
Here's a complete example that creates a directory only if a prerequisite path exists:
# In custom fact (e.g., modules/custom/lib/facter/prerequisite_exists.rb)
Facter.add('prerequisite_exists') do
setcode do
File.directory?('/important/prerequisite')
end
end
# In your manifest
if $facts['prerequisite_exists'] {
file { '/dependent/directory':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
} else {
notify { 'prerequisite-warning':
message => "Skipping creation: /important/prerequisite not found",
loglevel => 'warning',
}
}
When troubleshooting file existence checks:
- Run
puppet facts
to verify custom fact values - Check system logs for permission issues
- Test commands manually on target nodes
- Use
puppet apply --debug
for detailed execution tracing