Hiera - break results into line items

71 Views Asked by At

I'm using puppet 3.6.2 and trying to use data out of hiera for a windows patching solution. I'm on a disconnect network and cannot use WSUS.

I have a file patches.yml, which looks like this:

windows::patches:
  - Firefox-Setup-119.0b9.exe
  - KB5007255-x64.msu
  - KB5008285-x64.msu
  - KB5008897-x64.msu
  - KB5009595-x64.msu
  - KB5010395-x64.msu
  - KB5011560-x64.msu
  - KB5012144-x64.msu

I have my windowspatches.pp file that looks like this:

class profile::windowspatches (
  $filename = hiera("windows::patches")
)
{

  include stdlib

notify{ "$filename": 
  message => $filename
}


#$downloadUrl = "http://server1/Microsoft/"

#package {$patches:
#   ensure => "installed",
#   source => "$downloadUrl+$filename"
#   
#   }

}

I'm working through my idea and have the other actions commented out.

The results currently look like this:

Firefox-Setup-119.0b9.exeKB5007255-x64. msuKB5008285-x64.msuKB5008897-x64.msuKB5009595-x64.msuKB5010395-x64.msuKB5011560 -x64.msuKB5012144-x64.msuKB5012670-x64.msuKB5013616-x64.msuKB5014011-x64.msuKB50 14746-x64.msuKB5015877-x64.msuKB5016683-x64.msuKB5017365-x64.msuKB5018476-x64.ms uKB5020010-x64.msuKB5020608-x64.msuKB5020902-x64.msuKB5021296-x64.msuKB5022346-x 64.msuKB5022508-x64.msuKB5022894-x64.msuKB5023764-x64.msuKB5025288-x64.msuKB5026 409-x64.msuKB5027109-x64.msuKB5027282-x64.msuKB5028223-x64.msuKB5028974-x64.msuK B5029304-x64.msuKB5029942-x64.msuKB5030287-x64.msuKB5031407-x64.msu (noop)

I have tired $filename = hiera("windows::patches"), which gives me the same results.

I'm not sure what I'm missing. I know I have an old version of puppet and I can't upgrade.

2

There are 2 best solutions below

3
On

I'm using puppet 3.6.2

Don't, if you can possibly avoid it. Puppet 3 is obsolete, many years past its end of life. As I write this, the current release version is Puppet 8.3.1.

Although Perforce seems to try to project a different impression on its main web pages, current Puppet is still available as an open-source, no-charge option. This has all the core features and can be used indefinitely on as many machines as you like.


Even if you feel constrained to stick with Puppet 3, this is a bit absurd:

class profile::windowspatches (
  $filename = hiera("windows::patches")
)
{

Perhaps the most significant advance of Puppet 3 over Puppet 2.7 was automatic data binding, which you could and should leverage to associate external data with class parameters. There are two possibilities here:

  1. You have a class somewhere whose fully-qualified name is windows and which has a class parameter named patches:

    class windows($patches) {
      # ...
    }
    

    In this case, your patch filenames from your current data will be bound automatically to variable $windows::patches, which you can ensure is available to you by includeing that class":

    class profile::windowspatches {
      include stdlib
      include windows
      # ... use $windows::patches ...
    }
    
  2. In the more likely case that you do not have such a windows class, you would be better off leveraging automatic data binding directly in your profile::windowspatches class. This would require changing the Hiera key to which the data are bound:

    profile::windowspatches::filename:
      - Firefox-Setup-119.0b9.exe
      - KB5007255-x64.msu
      - KB5008285-x64.msu
      - KB5008897-x64.msu
      - KB5009595-x64.msu
      - KB5010395-x64.msu
      - KB5011560-x64.msu
      - KB5012144-x64.msu
    

    Then, you would just let the data binding do its job:

    class profile::windowspatches($filename) {
      include stdlib
      # ... use $filename ...
    }
    

But the main question seems to be about iterating over those data once loaded. Later versions of the Puppet language offer built-in means to iterate over items, and the stdlib module has long had some help there, but the basic means to do iteration in Puppet 3 was to use the fact that when an array is used as a resource title, multiple resources are declared, one for each element. At its most basic, that might be something like this:

class profile::windowspatches (
  $filename
) {

  # Note: do not quote $filename if you want it to be interpreted as an
  # array of resource titles:
  notify { $filename:
    message => ''
  }
}

There are lots of other ways to leverage that, many of them involving using defined types to wrap up the logic to be applied iteratively. You quickly get to that point if you want the iterative body to do different things for different elements. For example:

define profile::windowspatches::notify_one() {
  notify{ "$title": 
    # different message per resource title:
    message => "$title"
  }
}

class profile::windowspatches (
  $filename
) {
  # Note: do not quote $filename if you want it to be interpreted as an
  # array of resource titles:
  profile::windowspatches::notify_one { $filename: }
}
5
On

You are supplying an array, and you need to iterate trough this array or maybe change the data structure.

Solution for OLD puppet servers

You may try changing your data structure in Hiera as follows:

---
download_url: 'http://server1/Microsoft'
windows::patches:
  apache2:
    source: "%{hiera('download_url')}/apache2"
  fping:
    source: "%{hiera('download_url')}/fping"

Please ensure that %{hiera('download_url')} works with your version of puppet and hiera. If it errors, you shuold change it as follows:

---
windows::patches:
  apache2:
    source: "http://server1/Microsoft/apache2"
  fping:
    source: "http://server1/Microsoft/fping"

and your puppet manifest will look like this (according to the Puppet language style guide, you should not use lookup in the class parameters):

class profile::windowspatches {
  $defaults = { 'ensure' => 'installed', }
  $filename = hiera('windows::patches')

  create_resources(package, $filename, $defaults)
}

Solution for RECENT puppet servers

If you did not have such an old version of puppet, you could stick with your data structure in hiera, and you could have done as following (with 2 bonus tips):

class profile::windowspatches {
  $filename = lookup("windows::patches", Array, 'first', [])  # hiera command is deprecated
  $download_url = "http://server1/Microsoft/"  # no CamelCase with puppet

  $filename.each | $patch | {
    package { $patch:
      ensure => installed,
      source => "${download_url}${patch}";
    }
  }
}

p.s.: I'd also recommend to not use the double colon separator in the key names in hiera, unless you're using the autolookup feature. It will help distinguishing between auto-lookup and normal lookup.

Let's use windows_patches instead of windows::patches