Configuration management is a hot topic these days. Chef is one of the more popular choices, and does a fairly good job helping you maintain consistent configuration across your environment. That said it isn’t fool proof. I’ve outlined two common scenarios in which you might introduce a configuration issue.
Removing a File, Package, User, or and Chef Managed Resource
There are a few cases when using Chef where you will end up with an unintentionally installed package, user, file, or other resource. Typically this will happen when modifying a recipe to remove a resource. Lets say you have a recipe that installs three packages:
package "a" do action :install end package "b" do action :install end package “c” do action :install end
You may want to remove “package b”, so you might remove it from the recipe:
package "a" do action :install end package "c" do action :install end
This however will leave you with “package b” installed, and unmanaged across all the nodes running this recipe. Chef is no longer responsible for “package b”, and won’t take any action once its been removed from the recipe. In cloud instances, new instances and old instances will now have mismatched configurations, and you may see issues with dependencies across instances.
The proper way to remove a previously chef managed packages is to do the following:
package "a" do action :install end package "b" do action :remove end package "c" do action :install end
If you want to remove the “package b” code from your recipe, wait until you have confirmed all nodes have removed the desired package, and then delete the lines from you recipe.
IMPORTANT:
If you are using chef to manage users, make sure chef removes your users for you, otherwise they will continue to have access. The same goes for any chef managed resource(cron jobs, files, etc…), once chef is in control, let chef remove/uninstall the resource.
Resources Definitions in Loops
I see people use loops to create resources in recipes. Most of the time these are being done for file creation, or execution of an external process. I came across something a few weeks back that was strange:
servers = %w{ "server-a", "server-b"} servers.each { |server| execute "server-command-add" do not_if "/usr/bin/add-server-to-something exists #{server}" command "/usr/bin/add-server-to-something add #{server}" end }
Chef will do something fairly unexpected here; the second command will not execute because a not_if condition is met on the second resource always. This is because the execute resource for “server-b” has two not_if conditions, (“/usr/bin/add-server-to-something exists server-a”, “/usr/bin/add-server-to-something exists server-b”). Chef copies attributes from the first execute resource defined, and concatenates the additional not_if conditional into and array. Because not_if and only_if are defined as arrays, ruby copies an array reference from the first resource to the second resource.
It is unclear whether this is intentional, but you should be aware of this issue when writing chef recipes. The best way to execute this pattern is to give each resource a unique name, like so:
servers = %w{ "server-a", "server-b"} servers.each { |server| execute "server-command-add-#{server}" do not_if "/usr/bin/add-server-to-something exists #{server}" command "/usr/bin/add-server-to-something add #{server}" end }
Conclusion
These are just two examples, and I’m sure there are plenty others. When using automation tools remember to check to see if it achieved the results you expected; never blindly trust the tool.