Tuesday, October 4, 2011

Windows Registry-based Idempotence Check with Chef

Problem

You are using Chef with Windows (that is a problem in itself!) and are using powershell to install programs using a specific user account with winrm.

How do you ensure idempotence?

Solution

There are many possibilities, like using the windows cookbook from Opscode, but that doesn't handle installing and package as a specific user.

Here is a hack that will use Ruby to check the Windows registry for an installed product and if found, prevent the powershell resource block from executing.

Add this registry_helper.rb to your cookbook's libraries directory:


require 'win32/registry'


module RegistryHelper
  INSTALLER_PRODUCTS = 'Installer\Products'
  def RegistryHelper.is_in_classes_root?(path, product_name)
    found = false
    Win32::Registry::HKEY_CLASSES_ROOT.open(path) do |reg|
      reg.each_key do |key|
        k = reg.open(key)
        if k['ProductName'] =~ /#{Regexp.quote(product_name)}/
          found = true
          break
        end
      end
    end
    return found
  end
end


In your recipe, include this library, and add a not_if block to the powershell resource statement:


class Chef::Recipe
  include RegistryHelper
end



# The name to search for in the registry for idempotence
registry_name = 'Java(TM) SE Development Kit 6 Update 26'

powershell "Install-jdk_6u26" do
  ...
  # ensure idempotence
  not_if do
    RegistryHelper.is_in_classes_root?(RegistryHelper::INSTALLER_PRODUCTS, registry_name)
    end
end