Chef helpers / Custom modules

I found myself in need of a custom guard for a Chef recipe using the registry_key resource. I was able to create a module to eliminate code repetition. The following is a simplified example.

To get started, generate a new cookbook chef generate cookbook example_helper.

There isn’t a chef generate for libraries. Manually create the libraries directory and a new ruby file. In this example, I have created a file named my_helper.rb.

Within the my_helper.rb file I created a new method called in_file?. The in_file? method will search the text file 'c:\temp\my_helper.txt' for a specific word and return true if found. Additionally, the method will need to be included in the recipe, resource, etc. To prevent clobbering any existing method with the same name include it only where needed.

example_helper > libraries > my_helper.rb


# libraries/my_helper.rb

module My
  module Helper
    def in_file?(word)
      # example code not for actual use
      file = 'c:\temp\my_helper.txt'
      if File.exist?(file)
        File.readlines(file).grep(/#{word}/).any? # return truth
      end
    end
  end
end

# Send the helper to the required resource
Chef::Resource::RegistryKey.send(:include, My::Helper)

Attribute file

Create a new attribute file chef generate attribute default. I populated the attribute file with some values to loop over.

example_helper > attributes > default.rb


# attribute/default.rb

default['example_helper']['regkey']['key1'] = 'value1'
default['example_helper']['regkey']['key2'] = 'value2'
default['example_helper']['regkey']['key3'] = 'value3'
default['example_helper']['regkey']['key4'] = 'value4'

Cookbook recipe

The recipe iterates through the cookbook attributes and creates a new registry key. The guard uses the in_file? method created above. If the text file contains the attribute name the registry_key resource will be skipped for that iteration.

example_helper > recipes > default.rb


# recipe/default.rb

return unless platform_family?('windows')

node['example_helper']['regkey']&.each do |name, val|
  registry_key "HKLM\\System\\CurrentControlSet\\Control\\DoesNotExist\\#{name}" do
    action :create
    recursive true
    values [{ name: 'Enabled', type: :dword, data: val }]
    not_if { in_file?(name) }
  end
end

Example word list file

Add the words we want to trigger the in_file? method. In this case, we will add two of the four attribute values.

c:\temp\my_helper.txt

key2
key4

Run the Chef Client

Run the Chef cookbook chef-client -z -o example_helper. Using the helper along with the guard registry keys key2 and key4 will be skipped.

➜  chef-client -z -o example_helper

Recipe: example_helper::default
  * registry_key[HKLM\System\CurrentControlSet\Control\DoesNotExist\key1] action create
    - create key HKLM\System\CurrentControlSet\Control\DoesNotExist\key1
    - set value {:name=>"Enabled", :type=>:dword, :data=>"value1"}
  * registry_key[HKLM\System\CurrentControlSet\Control\DoesNotExist\key2] action create (skipped due to not_if)
  * registry_key[HKLM\System\CurrentControlSet\Control\DoesNotExist\key3] action create
    - create key HKLM\System\CurrentControlSet\Control\DoesNotExist\key3
    - set value {:name=>"Enabled", :type=>:dword, :data=>"value3"}
  * registry_key[HKLM\System\CurrentControlSet\Control\DoesNotExist\key4] action create (skipped due to not_if)

Running handlers:
Running handlers complete
Chef Infra Client finished, 2/4 resources updated in 03 seconds