Puppet Exec Resource: Conditionally Run Command Only When File Does Not Exist


2 views

When working with Puppet manifests, a common automation scenario requires executing a command only when a specific file is absent from the system. The original example demonstrates this perfectly:

exec { 'git add url':
    command => 'git remote add origin https://github.com/testing/puppet.git',
    require => Exec['git init'],
    cwd => '/home/vagrant/django',
    user => 'vagrant',
    onlyif => "not sure what to put here"
}

The solution lies in Puppet's onlyif parameter combined with shell test commands. Here's the proper implementation:

exec { 'git add url':
    command => 'git remote add origin https://github.com/testing/puppet.git',
    require => Exec['git init'],
    cwd => '/home/vagrant/django',
    user => 'vagrant',
    onlyif => "test ! -f /usr/local/bin/papply"
}

For more complex conditions or better readability, consider these variations:

# Using unless parameter (reverse logic)
unless => "test -f /usr/local/bin/papply"

# Using Ruby DSL for complex conditions
onlyif => "! File.exist?('/usr/local/bin/papply')"

# Checking multiple files
onlyif => "test ! -f /path1 && test ! -f /path2"

Here's a complete manifest example for initializing a Git repo only when the remote isn't configured:

class git_setup {
    exec { 'init_repo':
        command => 'git init',
        cwd => '/home/vagrant/django',
        user => 'vagrant',
        creates => '/home/vagrant/django/.git',
    }
    
    exec { 'add_remote':
        command => 'git remote add origin https://github.com/testing/puppet.git',
        cwd => '/home/vagrant/django',
        user => 'vagrant',
        unless => 'git config --get remote.origin.url',
        require => Exec['init_repo'],
    }
}
  • Always use absolute paths in conditions
  • Consider using creates parameter when appropriate
  • Test conditions manually before implementing
  • Chain dependent resources with require

If your condition isn't working as expected:

# Test the condition directly on the target system
sudo -u vagrant test ! -f /usr/local/bin/papply && echo "Would execute" || echo "Would skip"

# Enable Puppet debug logging
puppet apply --debug manifest.pp

# Verify file permissions
ls -la /usr/local/bin/papply

When managing infrastructure with Puppet, a common requirement is to execute commands only when specific conditions are met. The particular case we're addressing involves running a git command only when a target file (/usr/local/bin/papply) doesn't exist.

Puppet provides several ways to handle conditional execution of resources. The exec type supports three main attributes for this purpose:

  • onlyif: Runs command only if test returns 0
  • unless: Runs command unless test returns 0
  • creates: Doesn't run command if specified file exists

The most robust approach uses the unless parameter with a test command:

exec { 'git add url':
    command => 'git remote add origin https://github.com/testing/puppet.git',
    require => Exec['git init'],
    cwd => '/home/vagrant/django',
    user => 'vagrant',
    unless => 'test -f /usr/local/bin/papply'
}

Other valid implementations include:

Using onlyif with negation:

onlyif => 'test ! -f /usr/local/bin/papply'

Using creates if you want to create the file:

exec { 'git add url':
    # ... other parameters ...
    creates => '/usr/local/bin/papply'
}
  • Always use absolute paths for file checks
  • Consider adding timeout parameters for long-running commands
  • Test your conditional logic thoroughly in development
  • For complex conditions, consider writing custom facts or functions

Here's a complete example managing a git repository only when the deployment script is missing:

exec { 'initialize git repo':
    command => 'git init',
    cwd => '/home/vagrant/django',
    user => 'vagrant',
    unless => 'test -d .git'
}

exec { 'git add remote':
    command => 'git remote add origin https://github.com/testing/puppet.git',
    require => Exec['initialize git repo'],
    cwd => '/home/vagrant/django',
    user => 'vagrant',
    unless => 'test -f /usr/local/bin/papply || git remote -v | grep -q origin'
}