How does Chef recipe get/retrieve parameters from databag?

2.2k views Asked by At

I have been trying to learn Chef and trying to test a small Chef cookbook that would do dcpromo of a Windows 2008 R2 server.

I don't remember exactly where I got the two files originally, but I was able to get it working.

The original dcpromo_unattend.txt.erb file was:

[DCINSTALL]
SafeModeAdminPassword=<%= @admin_password %>
RebootOnCompletion=Yes
ReplicaOrNewDomain=domain
NewDomain=forest
NewDomainDNSName=<%= @domain_name %>
ForestLevel=3
DomainLevel=3
InstallDNS=yes

and the default.rb had this part in it:

template dcpromo_file do
  source "dcpromo_unattend.txt.erb"
  variables({
    :admin_password => '',
    :domain_name => ''
  })
end

I wasn't quite sure what how to pass in the admin_password and domain_name parameters, so I hard-coded both in the dcpromo_unattend.txt.erb file, and, after some tweaking, was able to make the cookbook work.

Now, I'd like to be able to put the admin_password and domain_name values into a databag, so I tried adding:

begin
  dcpromote = Chef::DataBagItem.load(:dcpromote, :main)
rescue
  Chef::Log.fatal("Could not find the 'main' item in the 'dcpromote' data bag - Raising fatal error!!")
  raise
end

and changed the original template section to:

template dcpromo_file do
  source "dcpromo_unattend.txt.erb"
  variables({
    :admin_password => dcpromote['admin_password'],
    :domain_name => dcpromote['domain_name']
  })
end

and I created a databag named "dcpromote", but that doesn't seem to be working.

Can someone explain how the original template code is supposed to work, i.e., where is it suppose to be retrieving the admin_password and domain_name parameters from?

Also, can anyone tell me what is wrong with the changes that I made to get this to read the admin_password and domain_name from the "dcpromote" databag?

Thanks, Jim

EDIT: I guess that I've been staring at this for a few more hours, and, actually, I don't even understand how what I did is working.

What I mean is the erb file I have has the password and domain hard-coded:

[DCINSTALL]
SafeModeAdminPassword=xxxxxxxxx
RebootOnCompletion=Yes
ReplicaOrNewDomain=domain
NewDomain=forest
NewDomainDNSName=WHATEVER.com
ForestLevel=4
DomainLevel=4
InstallDNS=yes

Notice that there is NO reference to admin_password or domain_name in that file.

So, how does this part of the recipe/default.rb even working?

template dcpromo_file do
  source "dcpromo_unattend.txt.erb"
  variables({
    :admin_password => '',
    :domain_name => ''
  })
end

Can someone explain exactly what this part of the recipe code is doing:

  variables({
    :admin_password => '',
    :domain_name => ''
  })

??

Thanks, Jim

EDIT 2:

Adding entire default.rb after changes suggested by @Draco Ater:

#
# Cookbook Name:: dcpromote
# Recipe:: default
#
# Copyright (c) 2015 The Authors, All Rights Reserved.
# 


class ServerHelper 
    extend ::Windows::Helper

  class << self
    def dism
      @@dism ||= locate_sysnative_cmd("dism.exe")
    end

    def powershell
      @@powershell ||= locate_sysnative_cmd('WindowsPowershell\v1.0\powershell.exe')
    end

    def feature_installed?(feature)
      cmd = Mixlib::ShellOut.new("#{dism} /online /Get-Features", {:returns => [0,42,127]}).run_command
      !!(cmd.stderr.empty? && (cmd.stdout =~  /^Feature Name : #{feature}.?$\n^State : Enabled.?$/i))
    end
  end
end

windows_reboot 60 do
  action :nothing
end

#
# Following snippet from: https://supermarket.chef.io/cookbooks/ad
# This snippet checks for presence of a databag named "dcpromote" and for presence
# of an item in the databag named "main".  If that item is not present, then
# this snippet logs a fatal error.
begin
  dcpromote = Chef::DataBagItem.load('dcpromote', 'main')
rescue
  Chef::Log.fatal("Could not find the 'main' item in the 'dcpromote' data bag - Raising fatal error!!")
  raise
end




directory Chef::Config[:file_cache_path]
dcpromo_file = File.join(Chef::Config[:file_cache_path], 'dcpromo_unattend.txt')
#cert_script = File.join(Chef::Config[:file_cache_path], 'setupca.vbs')
# Available from e.g. http://blogs.technet.com/b/pki/archive/2009/09/18/automated-ca-installs-using-vb-script-on-windows-server-2008-and-2008r2.aspx

template dcpromo_file do
  source "dcpromo_unattend.txt.erb"
  variables(
    :admin_password => dcpromote['admin_password'],
    :domain_name => dcpromote['domain_name']
  )

end

powershell_script "run_dcpromo" do
  code "dcpromo /unattend:#{dcpromo_file}"
  #notifies :request, 'windows_reboot[60]'
  not_if { ServerHelper.feature_installed? 'DirectoryServices-DomainController' }
end

windows_feature 'DirectoryServices-DomainController' do
  action :install
  #notifies :request, 'windows_reboot[60]'
end

This cookbook/recipe is STILL not working with the databag.

To clarify: When I run it with the earlier code with the hard-coded setting of admin_password and domain_name, it works.

However, if I try the code that uses the databag it doesn't work. When I run it with the databag:

1) [This is strange]: If I look at the "unattended" txt file during the run, it looks like it is populated, but then at the end, the password item is set to nothing, i.e., the unattended text file changes during the run.

2) In the end when the Powershell is run, it looks like it gets an error 32.

Here's the console output:

PS C:\Users\Administrator> chef-client -o dcpromote_usedatabag
Starting Chef Client, version 12.3.0
[2015-06-14T07:24:47-07:00] INFO: *** Chef 12.3.0 ***
[2015-06-14T07:24:47-07:00] INFO: Chef-client pid: 260
[2015-06-14T07:25:04-07:00] WARN: Run List override has been provided.
[2015-06-14T07:25:04-07:00] WARN: Original Run List: []
[2015-06-14T07:25:04-07:00] WARN: Overridden Run List: [recipe[dcpromote_usedatabag]]
[2015-06-14T07:25:04-07:00] INFO: Run List is [recipe[dcpromote_usedatabag]]
[2015-06-14T07:25:04-07:00] INFO: Run List expands to [dcpromote_usedatabag]
[2015-06-14T07:25:04-07:00] INFO: Starting Chef Run for node8
[2015-06-14T07:25:04-07:00] INFO: Running start handlers
[2015-06-14T07:25:04-07:00] INFO: Start handlers complete.
[2015-06-14T07:25:04-07:00] INFO: HTTP Request Returned 404 Not Found:
resolving cookbooks for run list: ["dcpromote_usedatabag"]
[2015-06-14T07:25:04-07:00] INFO: Loading cookbooks [[email protected], [email protected], che
[2015-06-14T07:25:04-07:00] INFO: Skipping removal of obsoleted cookbooks from the cache
Synchronizing Cookbooks:
[2015-06-14T07:25:04-07:00] INFO: Storing updated cookbooks/dcpromote_usedatabag/recipes/default.rb
[2015-06-14T07:25:04-07:00] INFO: Storing updated cookbooks/dcpromote_usedatabag/templates/default/d
erb in the cache.
[2015-06-14T07:25:04-07:00] INFO: Storing updated cookbooks/dcpromote_usedatabag/Berksfile in the ca
[2015-06-14T07:25:04-07:00] INFO: Storing updated cookbooks/dcpromote_usedatabag/.kitchen.yml in the
  - windows
  - chef_handler
[2015-06-14T07:25:04-07:00] INFO: Storing updated cookbooks/dcpromote_usedatabag/chefignore in the c
[2015-06-14T07:25:04-07:00] INFO: Storing updated cookbooks/dcpromote_usedatabag/metadata.rb in the
[2015-06-14T07:25:04-07:00] INFO: Storing updated cookbooks/dcpromote_usedatabag/README.md in the ca
  - dcpromote_usedatabag
Compiling Cookbooks...
[2015-06-14T07:25:04-07:00] INFO: +++++++++++++++++++++++++++  HI ++++++++++++++++++++++++++++
[2015-06-14T07:25:04-07:00] INFO: +++++++++++++++++++++++++++  HI ++++++++++++++++++++++++++++
[2015-06-14T07:25:04-07:00] INFO: +++++++++++++++++++++++++++  In template +++++++++++++++++++++++++
[2015-06-14T07:25:04-07:00] INFO: +++++++++++++++++++++++++++  In template +++++++++++++++++++++++++
[2015-06-14T07:25:04-07:00] INFO: ++++ xoutput = [123]
Converging 5 resources
Recipe: dcpromote_usedatabag::default
  * windows_reboot[60] action nothing[2015-06-14T07:25:04-07:00] INFO: Processing windows_reboot[60]
romote_usedatabag::default line 28)
 (skipped due to action :nothing)
  * directory[c:/chef/cache] action create[2015-06-14T07:25:04-07:00] INFO: Processing directory[c:/
reate (dcpromote_usedatabag::default line 47)
 (up to date)
  * template[c:/chef/cache/dcpromo_unattend.txt] action create[2015-06-14T07:25:04-07:00] INFO: Proc
hef/cache/dcpromo_unattend.txt] action create (dcpromote_usedatabag::default line 52)
[2015-06-14T07:25:04-07:00] INFO: template[c:/chef/cache/dcpromo_unattend.txt] created file c:/chef/
nd.txt

    - create new file c:/chef/cache/dcpromo_unattend.txt[2015-06-14T07:25:04-07:00] INFO: template[c
_unattend.txt] updated file contents c:/chef/cache/dcpromo_unattend.txt

    - update content in file c:/chef/cache/dcpromo_unattend.txt from none to 798057
    --- c:/chef/cache/dcpromo_unattend.txt      2015-06-14 07:25:04.000000000 -0700
    +++ C:/Users/ADMINI~1/AppData/Local/Temp/chef-rendered-template20150614-260-1cvaiw  2015-06-14 0
700
    @@ -1 +1,10 @@
    +[DCINSTALL]
    +SafeModeAdminPassword=P@ssw0rd$123
    +RebootOnCompletion=Yes
    +ReplicaOrNewDomain=domain
    +NewDomain=forest
    +NewDomainDNSName=whateverisforever123.com
    +ForestLevel=4
    +DomainLevel=4
    +InstallDNS=yes
  * powershell_script[run_dcpromo] action run[2015-06-14T07:25:04-07:00] INFO: Processing powershell
 action run (dcpromote_usedatabag::default line 68)


    ================================================================================
    Error executing action `run` on resource 'powershell_script[run_dcpromo]'
    ================================================================================

    Mixlib::ShellOut::ShellCommandFailed
    ------------------------------------
    Expected process to exit with [0], but received '32'
    ---- Begin output of "powershell.exe" -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrest
None -File "C:/Users/ADMINI~1/AppData/Local/Temp/chef-script20150614-260-dfo5yi.ps1" ----
    STDOUT:
    STDERR:
    ---- End output of "powershell.exe" -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestri
ne -File "C:/Users/ADMINI~1/AppData/Local/Temp/chef-script20150614-260-dfo5yi.ps1" ----
    Ran "powershell.exe" -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestricted -InputForm
ers/ADMINI~1/AppData/Local/Temp/chef-script20150614-260-dfo5yi.ps1" returned 32

    Resource Declaration:
    ---------------------
    # In c:/chef/cache/cookbooks/dcpromote_usedatabag/recipes/default.rb

     68: powershell_script "run_dcpromo" do
     69:   code "dcpromo /unattend:#{dcpromo_file}"
     70:   #notifies :request, 'windows_reboot[60]'
     71:   not_if { ServerHelper.feature_installed? 'DirectoryServices-DomainController' }
     72: end
     73:

    Compiled Resource:
    ------------------
    # Declared in c:/chef/cache/cookbooks/dcpromote_usedatabag/recipes/default.rb:68:in `from_file'

    powershell_script("run_dcpromo") do
      action "run"
      retries 0
      retry_delay 2
      default_guard_interpreter :powershell_script
      command "run_dcpromo"
      backup 5
      returns 0
      code "dcpromo /unattend:c:/chef/cache/dcpromo_unattend.txt"
      interpreter "powershell.exe"
      declared_type :powershell_script
      cookbook_name "dcpromote_usedatabag"
      recipe_name "default"
      not_if { #code block }
    end

[2015-06-14T07:26:22-07:00] INFO: Running queued delayed notifications before re-raising exception

Running handlers:
[2015-06-14T07:26:22-07:00] ERROR: Running exception handlers
Running handlers complete
[2015-06-14T07:26:22-07:00] ERROR: Exception handlers complete
[2015-06-14T07:26:22-07:00] FATAL: Stacktrace dumped to c:/chef/cache/chef-stacktrace.out
Chef Client failed. 1 resources updated in 98.15625 seconds
[2015-06-14T07:26:22-07:00] FATAL: Mixlib::ShellOut::ShellCommandFailed: powershell_script[run_dcpro
tabag::default line 68) had an error: Mixlib::ShellOut::ShellCommandFailed: Expected process to exit
ved '32'
---- Begin output of "powershell.exe" -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestrict
 -File "C:/Users/ADMINI~1/AppData/Local/Temp/chef-script20150614-260-dfo5yi.ps1" ----
STDOUT:
STDERR:
---- End output of "powershell.exe" -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestricted
File "C:/Users/ADMINI~1/AppData/Local/Temp/chef-script20150614-260-dfo5yi.ps1" ----
Ran "powershell.exe" -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestricted -InputFormat N
ADMINI~1/AppData/Local/Temp/chef-script20150614-260-dfo5yi.ps1" returned 32
PS C:\Users\Administrator>

And here's the unattended txt file at the end:

[DCINSTALL]
SafeModeAdminPassword=
RebootOnCompletion=Yes
ReplicaOrNewDomain=domain
NewDomain=forest
NewDomainDNSName=whateverisforever123.com
ForestLevel=4
DomainLevel=4
InstallDNS=yes

Why is the unattended txt file changing twice during the run (and why is the password value disappearing)?

Thanks, Jim

EDIT 3:

For the record, I was able to get this working by adding an additional parameter to the template file for setting the netbios name:

[DCINSTALL]
RebootOnCompletion=Yes
ReplicaOrNewDomain=domain
NewDomain=forest
SafeModeAdminPassword=<%= @admin_password %>
NewDomainDNSName=<%= @domain_name %>
ForestLevel=4
DomainLevel=4
InstallDNS=yes
DomainNetbiosName=<%= @domain_netbios_name %>

and then modified the default.rb to set that parameter:

template dcpromo_file do
  source "dcpromo_unattend.txt.erb"
  variables(
    :admin_password => dcpromote['admin_password'],
    :domain_netbios_name => dcpromote['domain_netbios_name'],
    :domain_name => dcpromote['domain_name']
  )

Jim

1

There are 1 answers

6
Draco Ater On BEST ANSWER

Let's start with the template file itself.

[DCINSTALL]
SafeModeAdminPassword=<%= @admin_password %>
RebootOnCompletion=Yes
ReplicaOrNewDomain=domain
NewDomain=forest
NewDomainDNSName=<%= @domain_name %>
ForestLevel=3
DomainLevel=3
InstallDNS=yes

The code inside <% %> is ruby code. Things that start with @ inside the <% %> are variables. The = is a shorthand for printing the value. So the template uses 2 variables to set the values, by just printing them out.

Where do the variables come from? Exactly from this code in recipe ({ and } are not necessary here):

 variables(
    :admin_password => '',
    :domain_name => ''
 )

Currently they are initialized by empty strings, but if you put something else there in the recipe, it will be changed in template too. It will not break, if you pass some variables that are not used in the template, it will just be the redundant code.

For now you can put your password and domain name there like that and get this working (producing the right configuration file on target machine)

variables(
    :admin_password => 'my_pass',
    :domain_name => 'localhost'
)

Now we want to move the values to the data bag. Create a 'dcpromote' databag with 'main' data bag item.

knife data bag create dcpromote main

and edit the json file. In the end you should have something like that:

{
  "id": "main", # <= data bag item name
  "admin_password": "my_pass",
  "domain_name": "localhost"
}

Then in the recipe you read the data bag into the variable (Try using strings, not symbols, as data bag and item name):

begin
  dcpromote = Chef::DataBagItem.load( 'dcpromote', 'main' )
rescue
  Chef::Log.fatal("Could not find the 'main' item in the 'dcpromote' data bag - Raising fatal error!!")
  raise
end

and use it when creating configuration file:

variables(
  :admin_password => dcpromote['admin_password'],
  :domain_name => dcpromote['domain_name']
)