The correct way to loop in a Chef (solo) recipe

chefchef-solo

Can someone please explain to me how chef works? That is a pretty broad question, so to narrow it down I have this very simple recipe that loops over a list of users and creates each one if they do not already exist. It does not work.

From what I can tell the loop seems to be happening as I would expect. Once the loop has completed my bash commands to create each user are executed, once for each iteration in the loop. However, when the bash commands are executed they only seem to have the user value from the first loop iteration.

What is the correct way to write a recipe that loops over variable data similar to this example?

Here is the recipe:

node[:users].each do |user|
  puts "in loop for #{user['username']}"
  bash "create_user" do
    user "root"
    code do
      puts "running 'useradd' for #{user['username']}"
      "useradd #{user['username']}"
    end
    not_if do
      puts "checking /etc/passwd for #{user['username']}"
      "cat /etc/passwd | grep #{user['username']}"
    end
  end
end

I'm testing this using Vagrant with the following setup:

Vagrant::Config.run do |config|
  config.vm.box = "precise32"
  config.vm.box_url = "http://files.vagrantup.com/precise32.box"
  config.vm.provision :chef_solo do |chef|
    chef.add_recipe "sample"
    chef.json = {
      :users => [
        {:username => 'testA'},
        {:username => 'testB'},
        {:username => 'testC'},
        {:username => 'testD'},
        {:username => 'testE'},
      ],
    }
  end
end

The messages that are generated by the puts statements in the recipe look like this:

2013-03-08T01:03:46+00:00] INFO: Start handlers complete.
in loop for testA

in loop for testB

in loop for testC

in loop for testD

in loop for testE

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Chef Run complete in 0.026071 seconds

Best Answer

make your script name unique...

bash "create_user_#{user}" do

FWIW, I've used https://github.com/fnichol/chef-user several times which allows you to create/remove users based on attributes and databags.

Related Topic