Is there a way to pass parameters at run time in Chef?

I wanted to pass some attributes to chef-client at runtime. Is there a way to do the same? I was looking into chef-client -j option, but to my knowledge it can be used to specify run_list. Can I pass some attributes in it? If yes, how?


Yes, you can use the -j json file option to populate node attributes.

{
  "my_attribute": "I like attributes!"
}

This will make an attribute named my_attribute available in your cookbooks. For example,

Chef::Log.info(node['my_attribute'])

Or,

if node['my_attribute'] =~ /like/
  package "foo-likes-attributes" do
    action :install
  end
end

Setting an initial run_list is the most common use of the json attributes file for Chef Client. If you're using Chef Client + Chef Server, though, you can simply modify the node object on the server either through the webui (Open Source Chef Server) or management console (Opscode Hosted/Private Chef), or through knife node edit if you're using the command-line tool, knife.

Note that using the JSON file is like modifying the node object on the server, the attributes set here "normal" priority like when they are used in a recipe, and these attribute values will be saved to the Node object on the server at the end of a successful run.

  • Normal attributes applied on a node directly in a recipe (from the documentation)

The ideal way to use attributes is to write them into your cookbook or into a data bag. Here are the file locations for either:

Chef Repo Directory Layout

|- chef-repo
|---- cookbooks
|------- attributes
|---------- default.rb
|------- recipes
|---------- default.rb
|---- data_bags
|------- users
|---------- john.json
|---------- susan.json
|------- databases
|----------- master.json
|----------- slave.json

You can store data in your data bag files in basic JSON format ( knife create data_bag users).

Create user data bag for john:

$ knife data bag create users john

{
  "id" : "john"
  "age" : "27"
  "height" : "60in"
}

Using Data Bags

If you choose to use data bags (I recommend them if you have a lot of different users or database servers (in this context)), you can view more information at https://docs.getchef.com/dsl_recipe_method_data_bag.html. Additionally, it's easier to create a users directory in your data_bags directory, and create the user .json files in there (~/chef-repo/data_bags/users/john.json) with the content above, then upload the data bag to the chef server: knife data bag from file users /path/to/data_bags/users/john.json

Using Attributes

You can also store the data in your attributes file:

attributes file

$ vi ~/chef-repo/cookbooks/my-cookbook-name/attributes/default.rb

default['my-cookbook-name']['user-1'] = "John"
default['my-cookbook-name']['user-1']['age'] = "27"
default['my-cookbook-name']['user-1']['height'] = "60in"

cookbook recipe

$ vi ~/chef-repo/cookbooks/my-cookbook-name/default.rb

template "/root/user-list" do
  action :create
  source "user-list.erb"
end

user-list.erb

$ vi ~/chef-repo/cookbooks/my-cookbook-name/templates/default/user-list.erb

<html>
<head>
 <title>My User List</title>
</head>
<body>
<h1>User List</h1>
User 1: <%= node['my-cookbook-name']['user-1'] %>
User Age: <%= node['my-cookbook-name']['user-1']['age'] %>
User Height: <%= node['my-cookbook-name']['user-1']['height'] %>
</body> 
</html>

you can do it on the command line, but - especially in a knife run - it'll start to look reeeally ooky:

chef-client \
  -o vmware-tools \
  --force-formatter \
  -j '<(echo {\"vmware-tools\":{\"style\":\"rpm\"}})'

But here we're leveraging -j and a <(notation) to create the JSON file on the fly, as artefacts tossed in via knife (or ssh-i, here). If you must do it, the risk is the conflicting delimiters will drive you insane at about the time you get it to work.