There’s a users
promise type for managing local users. However, did you know there is also a custom one for managing local groups?
You might have seen it mentioned in the CFEngine Build announcement, the blog post on Managing local groups, or in the announcement supporting custom bodies post. But let’s take another look. The easiest way to integrate the groups
custom promise type is by using cfbs
, simply cfbs add promise-type-groups
in your project. Next, we need some policy that leverages the groups
promise type. Let’s create groups.cf
in the projects root directory and add it to the project with cfbs add ./groups.cf
, selecting the option to add the groups
bundle to the bundlesequence.
bundle agent groups
{
vars:
"characters" slist => { "yoda", "obi-wan", "luke", "dooku", "anakin" };
users:
"$(characters)" policy => "present";
groups:
"jedi"
policy => "present",
members => jedi;
"sith"
policy => "present",
members => sith;
}
body members jedi
{
include => { "yoda", "obi-wan", "luke" };
exclude => { "dooku", "anakin" };
}
body members sith
{
exclude => { "yoda", "obi-wan", "luke" };
include => { "dooku", "anakin" };
}
After running cfbs build
and cfbs install
, we can see it in action when we run the policy:
cf-agent -KIf update.cf && cf-agent -KI
<snip>
info: Created user 'yoda'
info: Created user 'obi-wan'
info: Created user 'luke'
info: Created user 'dooku'
info: Created user 'anakin'
info: Created group 'jedi'
info: Added user 'yoda' to group 'jedi'
info: Added user 'obi-wan' to group 'jedi'
info: Added user 'luke' to group 'jedi'
info: Created group 'sith'
info: Added user 'dooku' to group 'sith'
info: Added user 'anakin' to group 'sith'
<snip>
It created the groups and users as expected. And if we add anakin
to the jedi
group manually, we see that it gets removed on the subsequent execution:
[root@hub cfbs]# usermod --groups jedi --append anakin
[root@hub cfbs]# getent group jedi
jedi:x:1006:yoda,obi-wan,luke,anakin
[root@hub cfbs]# cf-agent -KI
info: Removed user 'anakin' from group 'jedi'
[root@hub cfbs]# getent group jedi
jedi:x:1006:yoda,obi-wan,luke
Unfortunately, bodies in custom promise types cannot currently accept a list directly as a parameter, so if you try to abstract the membership like this:
bundle agent groups
{
vars:
"jedi" slist => { "yoda", "obi-wan", "luke" };
"sith" slist => { "dooku", "anakin" };
"characters" slist => { @(jedi), @(sith) };
users:
"$(characters)" policy => "present";
groups:
"jedi"
policy => "present",
members => members( @(jedi), @(sith) ) ;
"sith"
policy => "present",
members => members( @(sith), @(jedi) ) ;
}
body members members(include, exclude)
{
include => { "@(include)" };
exclude => { "@(exclude)" };
}
It won’t work, you will see errors about undefined variables:
error: groups promise with promiser 'jedi' has unresolved/unexpanded variables
error: groups promise with promiser 'sith' has unresolved/unexpanded variables
error: groups promise with promiser 'jedi' has unresolved/unexpanded variables
error: groups promise with promiser 'sith' has unresolved/unexpanded variables
But that can be worked around by simply passing the name of the variable to the members’ body and expanding it there like this:
bundle agent groups
{
vars:
"jedi" slist => { "yoda", "obi-wan", "luke" };
"sith" slist => { "dooku", "anakin" };
"characters" slist => { @(jedi), @(sith) };
users:
"$(characters)" policy => "present";
groups:
"jedi"
policy => "present",
members => members( "groups.jedi", "groups.sith" ) ;
"sith"
policy => "present",
members => members( "groups.sith", "groups.jedi" ) ;
}
body members members(include, exclude)
{
include => { "@($(include))" };
exclude => { "@($(exclude))" };
}
Happy Friday! 🎉