rubychef-infradatabags

Chef delay attribute assignment via data bag


So i have a bit of a pickle. I have an encrypted data bag to store LDAP passwords. In my node run list, one of my recipes installs the secret key onto my client machine. In my problematic cookbook, i have a helper (in /libraries) that pulls data from AD (using LDAP). Problem is, i can't find a way to delay the assignment of my node attribute after initial compile phase.

Take this line of code as example :

node.override['yp_chefserver']['osAdminUser'] = node['yp_chefserver']['osAdminUser'] + get_sam("#{data_bag_item('yp_chefserver', 'ldap', IO.read('/etc/chef/secret/yp_chefserver'))['ldap_password']}")

Im trying to override an attribute by adding an array returned by my helper function "get_sam" which returns an array, but it needs to run AFTER the compile phase since the file "/etc/chef/secret/yp_chefserver" doesnt exist before the convergence of my runlist.

So my question : Is there a way to assign node attributes via data_bag_items during the execution phase?

Some things i've tried :

ruby_block 'attribution' do
  only_if { File.exist?('/etc/chef/secret/yp_chefserver')}
  block do
    node.override['yp_chefserver']['osAdminUser'] = node['yp_chefserver']['osAdminUser'] + get_sam("#{data_bag_item('yp_chefserver', 'ldap', IO.read('/etc/chef/secret/yp_chefserver'))['ldap_password']}")
    Chef::Log.warn("content of osAdminUser : #{node['yp_chefserver']['osAdminUser']}")
  end
end

This doesn't work because the custom resource ruby_block doesn't have the method "data_bag_item". I've tried using lazy attributes in my "chef_server" custom resource, but same problem.

I also tried having the attribution done directly in my helper module, but since the helper module compiles before the exec phase, the file doesn't exist when it assigns the variable.

Here is the helper function in question should anyone wonder, it pulls the SamAccountName from LDAP to assign admin users to my chef server. :

module YpChefserver
  module LDAP

    require 'net-ldap'
    @ldap

    def get_ldap(ldap_password)
      if @ldap.nil?
        @ldap = Net::LDAP.new :host => "ADSERVER",
        :port => 389,
        :auth => {
              :method => :simple,
              :username => "CN=USERNAME,OU=East Service Accounts,OU=System Accounts,DC=ad,DC=ypg,DC=com",
              :password => "#{ldap_password}"
        }
      end
      @ldap
    end

    def get_ldap_users(ldap_password)
      filter = Net::LDAP::Filter.eq("cn", "DevOps")
      treebase = "dc=ad, dc=ypg, dc=com"
      get_ldap(ldap_password).search(:base => treebase, :filter => filter) do |entry|
       #puts "DN: #{entry.dn}"
       entry.each do |attribute, values|
            return values if attribute == :member
       end
      end
    end

    def get_sam(ldap_password)
      samacc = Array.new
      get_ldap_users(ldap_password).entries.each{ |elem|
        y = elem.to_s.split(/[,=]/)
        filter = Net::LDAP::Filter.eq("cn", y[1])
        treebase = "OU=Support Users and Groups,OU=CGI Support,DC=ad,DC=ypg,DC=com"
        get_ldap(ldap_password).search(:base => treebase, :filter => filter, :attributes => "SamAccountName") do |entry|
          samacc << entry.samaccountname
        end
      }
      return samacc
    end

  end
end

Solution

  • Turns out you can actually call it inside a ruby block, just by using the actual Chef call instead of the resource name, as follow :

    ruby_block 'attributes' do
      only_if {File.exist?('/etc/chef/secret/yp_chefserver')}
      block do
        dtbg = Chef::EncryptedDataBagItem.load('yp_chefserver','ldap',"IO.read('/etc/chef/secret/yp_chefserver')")
      end
    end
    

    Leaving this here for those who might need it

    EDIT : Here is final function using the code mentionned above to pull accounts from AD, using encrypted data bags to provide the password and to then pass those results to my node attributes, all during the execution phase :

    ruby_block 'attributes' do
      extend YpChefserver::LDAP
      only_if {File.exist?('/etc/chef/secret/yp_chefserver')}
      block do
        # Chef::Config[:encrypted_data_bag_secret] = '/etc/chef/secret/yp_chefserver'
        dtbg = Chef::EncryptedDataBagItem.load('yp_chefserver','ldap')
        node.override['yp_chefserver']['ldap_pw'] = dtbg['ldap_password']
        userarray = Array.new
        userarray.push("#{node['yp_chefserver']['osAdminUser']}")
        get_sam("#{node['yp_chefserver']['ldap_pw']}").each { |i| userarray.push(i[0]) }
        node.override['yp_chefserver']['authorized_users'] = userarray
        node.override['yp_chefserver']['local_admin_pw'] = dtbg['local_admin_pw']
      end
    end