Different usernames and passwords in Chef recipe

Hi All,

I’ve been working with Chef on Windows for configuring IIS. While I am getting on fine configuring the basics I’ve got to a point where I’m stuck. I can’t work out how to use different usernames and passwords in the same Chef recipe.

Lets say I have two servers that are identical, Server A and Server B, they are both test environments. The config on both servers is identical other than the username and passwords they use for the service account.

Server A
RecipeWebSite to setup IIS, but uses:
UsernameA
PasswordA

Server B
RecipeWebSite to setup IIS, but uses:
UsernameA
PasswordB

For usernames, I’ve gone down the route of calculating the username based on the server name within the recipe like this:

# Calculate username
base_username = "service_account_"
server = "#{ENV['COMPUTERNAME']}"
server_no = server[-2, 2]
pool_username = base_username+server_no

However, this won’t work for the passwords.

In my case, I actually have 10 test environments, so there are quite a number of usernames and passwords I need to substitute.

Thanks, AA.

As long as it is test environments, just set user name and password as attributes on the node or environment, this should cover your use case.

For more complex scenario, use a crypted databag with an entry per server, or chef-vault for more control, there’s a bunch of possibilities…

Thank you for the reply Tensibai.

I’ve read about attributes and data bags, and either seems like a possible way. One thing I don’t understand is how to switch in the recipe based on the hostname. A lot of the examples I come across show how to use attributes / data bags to store data for more than one service on the same server, but not to use specific attributes based on which server it is being run on.

Ok, let’s try a “raw” example with 3 alternatives methods in order of complexity in MY opinion:

  1. using the node object

  2. using a wrapper cookbook (My preferred method)

  3. using data bags

cookbook example:
attributes/default.rb:

default['app']['user'] = "default_user"
default['app']['pass'] = "default_pass"

recipes/default.rb

log "Test: #{user} #{password}"

Now edit the nodes:

knife edit "server A" and update the normal block to something like:

 "normal": {
    "tags": [
    ],
   "app": {
      "user": "serverA_user",
      "pass": "serverA_password"
    }
 },

knife edit "server B" and update the normal block to something like:

 "normal": {
    "tags": [
    ],
   "app": {
      "user": "serverB_user",
      "pass": "serverB_password"
    }
 },

Here the node attributes are at the normal level and will override the cookbook values.

another option would be to make a cookbook for each server and override the values here

serverA cookbook:

in metadata.rb add a line depends example

attributes/default.rb:

default['app']['user'] = "serverA_user"
default['app']['pass'] = "SERVERA_pass"

recipes/default.rb

include_recipe 'example'

(Same for server B)

Then each node needs to get its own cookbook in its runlist and not the example cookbook, the values in the serverX xookbook will be loaded after the example cookbook and so they’ll be overridden.

And for Databags you can do something like app_creds/svA.json:

{
  "id": "server_A",
  "user": "serverA_user",
  "pass": "serverA_pass"
}

Same for serverB a file app_creds/svB.json with new values.

and do knife data bag from files app_creds app_creds

And in the recipe of the example cookbook we do a modification to retrieve the data bag item according to the server:

example/recipes/default.rb

sv=data_bag_item("app_cred",node['name'])

log "Test #{sv['user']} #{sv['pass']}"

For the data bag part, I didn’t use them for a while so I may did a mistake here, check the knife documentation to be sure.

Thank you for the effort you’ve put in to explain this to me. I really appreciate it.

I have tried to implement it using a data bag as I eventually want to put encrypted passwords in there. What I have come up with is:

Data bag: tstserver_data

Item:

{
  "web_username": "web_userA",
  "ftp_username": "ftp_userA",
  "id": "tstserverA"
}

and item:

{
  "web_username": "web_userB",
  "ftp_username": "ftp_userB",
  "id": "tstserverB"
}

Recipe test.rb:

sv=data_bag_item("tstserver_data",node['name'])

file 'P:\usernames.txt' do
  content "#{sv['id']} #{sv['web_username']} #{sv['ftp_username']}"
end

However on my server I am getting the error:

Exception type: Chef::Exceptions::InvalidDataBagItemID
 Exception message: Data Bag items must have an id matching /^[\.\-[:alnum:]_]+$/, you gave: nil

Can you give me a pointer as to where I am going wrong please?

My bad, should be node['hostname'] for shortvalue, strange enough it does give nil that said.

Can you try with a log before to check the values like this:

Chef::Log.warn "Differents values:\n Name: #{node['name']}\n Name method: #{node.name}\n Hostname: #{node['hostname']}\n Fqdn: #{node['fqdn']}"
sv=data_bag_item("tstserver_data",node['hostname'])

Sorted it:

sv=data_bag_item("tstserver_data",node['hostname'].downcase)

Looking in the node file, hostname or machinename would have worked.

I am now able to store my usernames on the Chef server, and use the same chef recipe for all my test servers. Thank you for all your help.

Time to use it, and write a blog post up :slight_smile: