using return value of a shell command as a puppet conditional

5,021 views
Skip to first unread message

Ashley Gould

unread,
Jan 31, 2011, 4:12:50 PM1/31/11
to puppet...@googlegroups.com
Hi all,

My first post. I am just getting started with puppet and have made
good progress with some basics using templates and files.

I would like to perform and an action based on whether or not a
particular system binary executes successfully, but I don't see an
obvious way to set puppet conditionals based on an exit value of an
arbitrary command. Is there a well known recipe that does something
like this shell snippet?:

if [ "/usr/bin/my_harmless_binary 2>&1 >/dev/null" ]; then
echo "my_harmless_binary succeeded"
cp /tmp/file1 /data/
else
echo "my_harmless_binary FAILED"
fi


--

-ashley

Did you try poking at it with a stick?

cyrus

unread,
Jan 31, 2011, 5:06:13 PM1/31/11
to Puppet Users
If I understand your question you want to use the exec resource and
specify the onlyif (or unless) parameter.

Sort of like:

exec {
"cp /tmp/file1 /data",
path => [ "/bin" ],
onlyif => "/usr/bin/my_harmless_binary";

jcbollinger

unread,
Jan 31, 2011, 5:14:29 PM1/31/11
to Puppet Users
There are at least two possibilities. The more general, more puppetly
way is to write a custom fact that performs your test, and have your
manifests test the value of the fact as needed to determine the
appropriate configuration for the client. Custom facts are easy to
write and can be distributed through Puppet itself. This approach
does assume that the test is stable, however -- that is, that the
result will not change between when the client requests its catalog
and when it applies it. Also, this isn't so great if it could be a
problem for Puppet to apply a cached catalog compiled using an out-of-
date test result.

Alternatively, if the action you want to perform conditionally is a
command that you would use an Exec for anyway, then making it
conditional on some command is exactly what Exec's "onlyif" parameter
is for.


John

Ashley Gould

unread,
Jan 31, 2011, 6:31:24 PM1/31/11
to Puppet Users

thank you both. my current task will use a file action. I'm trying
to make puppet chose between alturnative versions of a file based on
the result of a binary.

I will work on the custom fact idea. if such an issue is coming at
me so early in the game, then I may as well get started learning
how to build facts.


>
> John
>
> --
> You received this message because you are subscribed to the Google Groups "Puppet Users" group.
> To post to this group, send email to puppet...@googlegroups.com.
> To unsubscribe from this group, send email to puppet-users...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.

Daniel Pittman

unread,
Jan 31, 2011, 6:46:32 PM1/31/11
to puppet...@googlegroups.com, Ashley Gould
On Mon, Jan 31, 2011 at 15:31, Ashley Gould <ago...@ucop.edu> wrote:
> On Mon, Jan 31, 2011 at 02:14:29PM -0800, jcbollinger wrote:
>> On Jan 31, 3:12 pm, Ashley Gould <ago...@ucop.edu> wrote:
>>
>> > if [ "/usr/bin/my_harmless_binary 2>&1 >/dev/null" ]; then
>> >   echo "my_harmless_binary succeeded"
>> >   cp /tmp/file1 /data/
>> > else
>> >   echo "my_harmless_binary FAILED"
>> > fi

[...]

> thank you both. my current task will use a file action. I'm trying
> to make puppet chose between alturnative versions of a file based on
> the result of a binary.
>
> I will work on the custom fact idea.  if such an issue is coming at
> me so early in the game, then I may as well get started learning
> how to build facts.

For what it is worth, I would say that the reason you are running into
this so early in the process is that you are not yet using puppet the
way it expects to be used – and, so, you run into these complications.

In the puppet model you would not ask the system about that binary,
then do something: you would say "machine A should have the binary,
and the something, while machine B should not have either".

The idea is that you declare, as far as possible, the state your
system should be – not what calculations you make to get from the
current to the desired state. Fact should, ideally, be things about
the system that don't vary (memory size, disk space) with state
changes, not questions about the current state.

So, I would strongly encourage you to "answer" the question about that
binary by deciding if the machine should have it installed, and if it
should work, and then implementing *that* in puppet. The file install
bit should just fall into line after that.

Regards,
Daniel
--
⎋ Puppet Labs Developer – http://puppetlabs.com
✉ Daniel Pittman <dan...@puppetlabs.com>
✆ Contact me via gtalk, email, or phone: +1 (877) 575-9775
♲ Made with 100 percent post-consumer electrons

Ashley Gould

unread,
Jan 31, 2011, 9:14:51 PM1/31/11
to puppet...@googlegroups.com
> > thank you both. my current task will use a file action. I'm trying
> > to make puppet chose between alturnative versions of a file based on
> > the result of a binary.
> >
> > I will work on the custom fact idea.  if such an issue is coming at
> > me so early in the game, then I may as well get started learning
> > how to build facts.
>
> For what it is worth, I would say that the reason you are running into
> this so early in the process is that you are not yet using puppet the
> way it expects to be used – and, so, you run into these complications.
>
> In the puppet model you would not ask the system about that binary,
> then do something: you would say "machine A should have the binary,
> and the something, while machine B should not have either".
>
> The idea is that you declare, as far as possible, the state your
> system should be – not what calculations you make to get from the
> current to the desired state. Fact should, ideally, be things about
> the system that don't vary (memory size, disk space) with state
> changes, not questions about the current state.
>
> So, I would strongly encourage you to "answer" the question about that
> binary by deciding if the machine should have it installed, and if it
> should work, and then implementing *that* in puppet. The file install
> bit should just fall into line after that.
>


You caught me. I'm a recoveing cfengine junky. It will take a while
to stop thinking in terms of making actions dependent on client's
membership in a (cf) class.

So here are the specifics of what I'm trying to do in this particular
task. I welcome any ideas on following the puppet way:

We run a 3rd party tool called centrifyDC to integrate our linux
servers with AD. a client can either be "joined" to a centrify (AD)
zone or not. I'm writing a puppet module to manage site
customizations to various files in /etc/pam.d. Trouble is centrify,
when joining to AD, will prepend it's own pam configs to my existing
pam files. so before I can push my custom pam configs, I need to
know if the client is joined to AD or not. the command to check the
centify status is /usr/bin/adinfo. it is installed on all nodes via
puppet.


My first fact (I am so proud):

# adinfo.rb
# Mon Jan 31 15:54:02 PST 2011
# ago...@ucop.edu
#
# a custom fact returning the centrify zone

if FileTest.exists?("/usr/bin/adinfo")
Facter.add(:adinfo) do
setcode do
%x{/usr/bin/adinfo --zone}.chomp
end
end
end


modules/pam/manifests/init.pp:
# module pam
# pmlab01-vhost.ucop.edu
# Fri Jan 28 17:34:53 PST 2011
# ago...@ucop.edu

# Pam configs
class pam {
file { "/etc/pam.d":
owner => root, group => root, mode => 644,
if $adinfo {
source => "puppet:///pam/centrify",
} else {
source => "puppet:///pam/no_centrify",
}
recurse => true,
}
}

On Mon, Jan 31, 2011 at 03:46:32PM -0800, Daniel Pittman wrote:
> On Mon, Jan 31, 2011 at 15:31, Ashley Gould <ago...@ucop.edu> wrote:
> > On Mon, Jan 31, 2011 at 02:14:29PM -0800, jcbollinger wrote:
> >> On Jan 31, 3:12 pm, Ashley Gould <ago...@ucop.edu> wrote:
> >>
> >> > if [ "/usr/bin/my_harmless_binary 2>&1 >/dev/null" ]; then
> >> >   echo "my_harmless_binary succeeded"
> >> >   cp /tmp/file1 /data/
> >> > else
> >> >   echo "my_harmless_binary FAILED"
> >> > fi
>
> [...]

> Regards,
> Daniel
> --
> ⎋ Puppet Labs Developer – http://puppetlabs.com
> ✉ Daniel Pittman <dan...@puppetlabs.com>
> ✆ Contact me via gtalk, email, or phone: +1 (877) 575-9775
> ♲ Made with 100 percent post-consumer electrons

--

Daniel Pittman

unread,
Jan 31, 2011, 9:27:20 PM1/31/11
to puppet...@googlegroups.com, Ashley Gould
On Mon, Jan 31, 2011 at 18:14, Ashley Gould <ago...@ucop.edu> wrote:
>> > thank you both. my current task will use a file action. I'm trying
>> > to make puppet chose between alturnative versions of a file based on
>> > the result of a binary.

[...]

> You caught me.  I'm a recoveing cfengine junky.  It will take a while
> to stop thinking in terms of making actions dependent on client's
> membership in a (cf) class.
>
> So here are the specifics of what I'm trying to do in this particular
> task.  I welcome any ideas on following the puppet way:

OK. So, in the circumstances, where membership of the domain with
centrifyDC is *not* managed by puppet, your use of a fact is the right
thing to do. For better or worse, "am I joined to a domain" is a fact
about the machine that comes from outside of puppet.

In the longer term I would hope to have that information pushed out
from the puppet system, so that if a node *should* be joined with
centrifyDC puppet will make it so, but until then what you have is
great.

[...]

> My first fact (I am so proud):
>
> # adinfo.rb
> # Mon Jan 31 15:54:02 PST 2011
> # ago...@ucop.edu
> #
> # a custom fact returning the centrify zone
>
> if FileTest.exists?("/usr/bin/adinfo")
>        Facter.add(:adinfo) do
>                setcode do
>                        %x{/usr/bin/adinfo --zone}.chomp
>                end
>        end
> end

Perfect. You even handled the case where centrify isn't installed
correctly, so this will not blow up random other systems. :)

Probably the one thing to watch out for is that *only* the empty
string is reliably false in puppet, in context, so you can't just
return the (Ruby) false value or something. That gets turned into a
string, and the string "false" is actually true in puppet. Just for
the future. :)

> class pam {
>        file { "/etc/pam.d":
>                owner   => root, group   => root, mode    => 644,
>                if $adinfo {
>                        source  => "puppet:///pam/centrify",
>                } else {
>                        source  => "puppet:///pam/no_centrify",
>                }

You actually want:

mode => 0644,
$adinfo ? {
'' => '.../no_centrify',
default => '.../centrify' # Assuming that "any value" means "in a domain"
},
whatever => else

The if statement is not used in that context, only a case statement.

Ashley Gould

unread,
Feb 2, 2011, 1:52:43 PM2/2/11
to puppet...@googlegroups.com
On Mon, Jan 31, 2011 at 06:27:20PM -0800, Daniel Pittman wrote:
> In the longer term I would hope to have that information pushed out
> from the puppet system, so that if a node *should* be joined with
> centrifyDC puppet will make it so, but until then what you have is
> great.

I have considered that option, but I'll need to learn to walk first.
What makes it difficult for puppet to manage this task is that the
centrifyDC tools require authorization as AD admin user to join a
node to AD. This can be scripted, but I don't want to hardcode AD
admin passwords into puppet manifests. Again, suggestions are very
welcome.

Nigel Kersten

unread,
Feb 2, 2011, 8:02:39 PM2/2/11
to puppet...@googlegroups.com, Ashley Gould

Are you ok with keeping these credentials on disk at all? If so, you
could feed the relevant join exec the data from local disk.

Another alternative would be to set up an out-of-band process where
the clients reuse their SSL certificates to make client-authenticated
requests to a host that returns the relevant credentials only during
the times when the node needs them.

>
>
> --
>
> -ashley
>
> Did you try poking at it with a stick?
>

Daniel Pittman

unread,
Feb 2, 2011, 8:10:51 PM2/2/11
to puppet...@googlegroups.com, Ashley Gould
On Wed, Feb 2, 2011 at 17:02, Nigel Kersten <ni...@puppetlabs.com> wrote:
> On Wed, Feb 2, 2011 at 10:52 AM, Ashley Gould <ago...@ucop.edu> wrote:
>> On Mon, Jan 31, 2011 at 06:27:20PM -0800, Daniel Pittman wrote:
>>> In the longer term I would hope to have that information pushed out
>>> from the puppet system, so that if a node *should* be joined with
>>> centrifyDC puppet will make it so, but until then what you have is
>>> great.
>>
>> I have considered that option, but I'll need to learn to walk first.
>> What makes it difficult for puppet to manage this task is that the
>> centrifyDC tools require authorization as AD admin user to join a
>> node to AD.  This can be scripted, but I don't want to hardcode AD
>> admin passwords into puppet manifests.  Again, suggestions are very
>> welcome.
>
> Are you ok with keeping these credentials on disk at all? If so, you
> could feed the relevant join exec the data from local disk.
>
> Another alternative would be to set up an out-of-band process where
> the clients reuse their SSL certificates to make client-authenticated
> requests to a host that returns the relevant credentials only during
> the times when the node needs them.

For what it is worth, I "solved" a similar problem that we had by
doing it by hand. We had sufficiently few rollouts where this
mattered that it was just easier to document that step required hand
action than to automate it and solve these security issues.

(Also, I went looking and found zero attempts to solve this in a
reusable, FOSS way, let alone working solutions.)

Nigel Kersten

unread,
Feb 2, 2011, 8:14:31 PM2/2/11
to puppet...@googlegroups.com, Ashley Gould
On Wed, Feb 2, 2011 at 5:10 PM, Daniel Pittman <dan...@puppetlabs.com> wrote:
> On Wed, Feb 2, 2011 at 17:02, Nigel Kersten <ni...@puppetlabs.com> wrote:
>> On Wed, Feb 2, 2011 at 10:52 AM, Ashley Gould <ago...@ucop.edu> wrote:
>>> On Mon, Jan 31, 2011 at 06:27:20PM -0800, Daniel Pittman wrote:
>>>> In the longer term I would hope to have that information pushed out
>>>> from the puppet system, so that if a node *should* be joined with
>>>> centrifyDC puppet will make it so, but until then what you have is
>>>> great.
>>>
>>> I have considered that option, but I'll need to learn to walk first.
>>> What makes it difficult for puppet to manage this task is that the
>>> centrifyDC tools require authorization as AD admin user to join a
>>> node to AD.  This can be scripted, but I don't want to hardcode AD
>>> admin passwords into puppet manifests.  Again, suggestions are very
>>> welcome.
>>
>> Are you ok with keeping these credentials on disk at all? If so, you
>> could feed the relevant join exec the data from local disk.
>>
>> Another alternative would be to set up an out-of-band process where
>> the clients reuse their SSL certificates to make client-authenticated
>> requests to a host that returns the relevant credentials only during
>> the times when the node needs them.
>
> For what it is worth, I "solved" a similar problem that we had by
> doing it by hand.  We had sufficiently few rollouts where this
> mattered that it was just easier to document that step required hand
> action than to automate it and solve these security issues.

I've done this a few times too. Have a single class that the relevant
chunks of your manifests depend upon, and then have a cluster of Execs
that just check for the existence of this data on the local client.

I kept wanting to be able to notify a notify resource so you could get
a nice human oriented error, but I don't think I worked out a good
solution for that.

> (Also, I went looking and found zero attempts to solve this in a
> reusable, FOSS way, let alone working solutions.)

Yep. I've been dreaming of a Puppet-integrated Password Safe for a while :)


>
> Regards,
>    Daniel
> --
> ⎋ Puppet Labs Developer – http://puppetlabs.com
> ✉ Daniel Pittman <dan...@puppetlabs.com>
> ✆ Contact me via gtalk, email, or phone: +1 (877) 575-9775
> ♲ Made with 100 percent post-consumer electrons
>

Nick Moffitt

unread,
Feb 3, 2011, 5:45:57 AM2/3/11
to puppet...@googlegroups.com
Nigel Kersten:

> On Wed, Feb 2, 2011 at 5:10 PM, Daniel Pittman <dan...@puppetlabs.com> wrote:
> > (Also, I went looking and found zero attempts to solve this in a
> > reusable, FOSS way, let alone working solutions.)
>
> Yep. I've been dreaming of a Puppet-integrated Password Safe for a while :)

Alas! I'm currently enjoying a powerful need for such a thing. My one
need is that someone who compromises a puppet client host shouldn't have
access to the safe except in specific circumstances specified by
external conditions.

I'd also like a pony. Have it on my desk by Monday.

--
"N'aimez pas votre voiture?
Alor, l'heure est arrive pour la brul�!"
-- Mark Jaroski

Jeff McCune

unread,
Feb 3, 2011, 9:16:03 AM2/3/11
to puppet...@googlegroups.com, Ashley Gould

In my previous life, we created a special account in AD with only the
permission to create a computer account in the directory. The special
account didn't have login rights or anything. It should be well
documented in your centrifyDC tools how to create such a limited
account.

Once you have this account in place, you can lock it.

You can then unlock the account when you need to build a machine and
have it automatically lock again after 15 minutes or so, if you want.

Hope this helps,
--
Jeff McCune
http://www.puppetlabs.com/

Nigel Kersten

unread,
Feb 5, 2011, 12:56:58 AM2/5/11
to puppet...@googlegroups.com, Nick Moffitt
On Thu, Feb 3, 2011 at 11:45 AM, Nick Moffitt <ni...@zork.net> wrote:
> Nigel Kersten:
>> On Wed, Feb 2, 2011 at 5:10 PM, Daniel Pittman <dan...@puppetlabs.com> wrote:
>> > (Also, I went looking and found zero attempts to solve this in a
>> > reusable, FOSS way, let alone working solutions.)
>>
>> Yep. I've been dreaming of a Puppet-integrated Password Safe for a while :)
>
> Alas!  I'm currently enjoying a powerful need for such a thing.  My one
> need is that someone who compromises a puppet client host shouldn't have
> access to the safe except in specific circumstances specified by
> external conditions.
>
> I'd also like a pony.  Have it on my desk by Monday.

I actually did some work on this on the plane recently, re-using the
certificates that nodes already have to do arbitrary encryption and
decryption.

It's not as seamless as I'd like, I've essentially subclassed the file
type, but it's giving me some ideas about how we might want to come up
with something more integrated.

I'll polish it up and put it up on github next week when I get back from FOSDEM.

kcrwfrd

unread,
May 9, 2011, 4:03:10 PM5/9/11
to Nigel Kersten, puppet...@googlegroups.com
On Feb 5, 1:56 am, Nigel Kersten <ni...@puppetlabs.com> wrote:

Hi Nigel,

I found this via a google search today. I've come up with a few
possible solutions, but I don't like them. I was thinking of using
the existing cert as well.

Have you posted the code you came up with?

Kyle

Nigel Kersten

unread,
May 9, 2011, 4:16:11 PM5/9/11
to kcrwfrd, puppet...@googlegroups.com
No, I'll spend some time tonight finding it. It ended up on another laptop and bad Nigel didn't commit it to version control anywhere external, so it's not as obvious as it should be... It's also very ghetto, so don't expect any polish.

There were some limitations with the size of the text you could encrypt, so I had to chunk. I think RI ran into the same issue a while ago.

Ideally we'd do this in a much more transparent manner.

If you care a lot about this, please put in a feature request with as much detail as you can provide. The more the community registers interest in features, the more likely it is that someone steps up and gets it done :)


--
Nigel Kersten
Product, Puppet Labs
@nigelkersten

Reply all
Reply to author
Forward
0 new messages