I'm creating a user via Chef. His properties are stored in data bag:
{
"id": "developer",
"home": "/home/developer",
"shell": "/bin/zsh",
"password": "s3cr3t"
}
The recipe is:
developer = data_bag_item('users', 'developer')
user developer['id'] do
action :create
supports :manage_home => true
home developer['home']
comment developer['comment']
shell developer['shell']
password developer['password']
end
The problem is that if zsh
is not installed on node, I cannot login as developer
. So, I want to conditionally apply argument for user
resource, like:
user developer['id'] do
action :create
supports :manage_home => true
home developer['home']
comment developer['comment']
if installed?(developer['shell'])
shell developer['shell']
end
password developer['password']
end
How can I achieve this?
To complement @mudasobwa's answer the proper way to do it in chef and avoid missing the shell if it's installed by another recipe or a package resource in the same recipe you have to use lazy attribute evaluation.
Long version for thoose interested on the how and why:
This is a side effect on how chef works, there's a first time compiling the resources to build a collection, at this phase any ruby code in a recipe (outside of a ruby_block resource) if evaluated. Once that is done the resources collection is converged (the desired state is compared to the actual state and relevant actions are done).
The following recipe would do:
package "zsh" do
action :install
end
user "myuser" do
action :create
shell lazy { File.exists? "/bin/zsh" ? "/bin/zsh" : "/bin/bash" }
end
What hapens here is that the evaluation of the shell attribute value is delayed to the converge phase, we have to use a if-then-else construction (here with a ternary operator as I find it more readable) to fallback to a shell we're sure will be present (I used /bin/bash
, but a failsafe value would be /bin/sh
) or the shell attribute will be nil, which is not allowed.
With this delayed evaluation the test on the presence of "/bin/zsh" is done after the package has been installed and the file should be present. In case there was a problem within the package, the user resource will still create the user but with "/bin/bash"
The easiest way to achieve what you want is to check for the shell existence explicitly:
shell developer['shell'] if File.exist? developer['shell']