Puppet - build array from hash titles to check contents in exec

puppet hash
puppet append to array
puppet array
puppet substring
puppet notify
puppet check if array is empty
puppet string interpolation
puppet concatenate strings

I am trying to apply the thoward-windows_firewall module to Windows 10 and hit a snag whereby most of the built in rules can be purged with the module's purge function, but some others (such as cortana) cannot. This problem didn't occur in earlier Windows builds. The workaround to this seems to be to run an exec with 'netsh advfirewall reset' which purges them and stops them reappearing.

However, I only want to apply the 'netsh advfirewall reset' exec if firewall rules other than what I have specified in Hiera exist.

My approach is to build an array (or list) of only the titles in hiera and then iterate over them in a PowerShell 'only if' or 'unless'.

Code so far (which is not working) is:

Hiera snippet:

harden::firewall:
   'Remote Desktop - User Mode (UDP-In)':
      description:      'Inbound rule for the Remote Desktop service to allow RDP traffic. [UDP 3389]'
      application_name: 'C:\Windows\system32\svchost.exe'
      service_name:     'termservice'
      protocol:         17
      local_ports:      '3389'
      remote_ports:     '*'
      local_addresses:  '%{facts.networking.ip}'
      remote_addresses: '*'
      direction:        1
      interface_types:  'All'
      enabled:          true
      grouping:         '@FirewallAPI.dll,-28752'
      profiles:         3
      action:           1

   'File and Printer Sharing (Echo Request - ICMPv4-In) 1':
      description:          'Echo Request messages are sent as ping requests to other nodes.'
      protocol:             1
      local_addresses:      '%{facts.networking.ip}'
      remote_addresses:     'LocalSubnet'
      icmp_types_and_codes: '8:*'
      direction:            1
      interface_types:      'All'
      enabled:              true
      grouping:             '@FirewallAPI.dll,-28502'
      profiles:             6
      action:               1

Manifest (extract):

class harden (
Hash     $firewall_rule_names = lookup('harden::firewall'),
){

# reset firewall rules
  exec { 'reset_firewall':
    command  => 'netsh advfirewall reset',
    onlyif   => 'if (Get-NetFirewallRule | where {\$_.DisplayName -notmatch $firewall_rule_names}) { exit 0 } else { exit 1 }',
    provider => powershell,
  }

  Class { 'windows_firewall':
    profile_state => 'on',
    in_policy     => 'BlockInbound',
    out_policy    => 'BlockOutbound',
    rule_key      => 'harden::firewall',
    purge_rules   => true,
  }

I know I need to have a .each look in there somewhere, and also tidy up the powershell 'only if' so that it looks at just the titles in the hash (perhaps re-written to an array of just hash titles) and runs the exec if there are rules on the host that aren't in Hiera, but am getting a bit lost.

Any help sincerely appreciated.

You wrote:

My approach is to build an array (or list) of only the titles in hiera and then iterate over them in a PowerShell 'only if' or 'unless'.

Apparently, you attempt to build that array in class parameter $firewall_rule_names, with this declaration:

Hash     $firewall_rule_names = lookup('harden::firewall'),

and then you attempt to use that list in the onlyif parameter of your Exec:

    onlyif   => 'if (Get-NetFirewallRule | where {\$_.DisplayName -notmatch $firewall_rule_names}) { exit 0 } else { exit 1 }',

There are multiple problems with that.

First, if you want to interpolate a puppet variable into a string, then that string needs to be quoted with double quotes ("); quoting with single quotes (') suppresses interpolation (and also treats \$ as two literal characters, not an escape sequence).

Second, and where you seem to be at loss, as how to extract the keys of your $firewall_rule_names hash and format them appropriately. I'm uncertain exactly what Powershell requires here, but some of the best tools to use to get it would be the keys() and join() functions provided by the puppetlabs/stdlib module or by Puppet itself if you're using version 5.5 or later. For example, if all you need is a comma-delimited list of the names then something like this would do it:

$really_the_rule_names = join(keys($firewall_rule_names), ', ')

I'm suspicious, though, that you may need to quote the keys. You could get most of that by being clever with the delimiter you specify to join, but you might also want to consider processing the key array with the built-in regsubst() function before joining results together into a string.

Puppet - build array from hash titles to check contents in exec, However, I only want to apply the 'netsh advfirewall reset' exec if firewall rules other than what I have specified in Hiera exist. My approach is to build an array (​or  0 Puppet - build array from hash titles to check contents in exec Sep 29 '18 0 Creating nested Puppet fact (Ruby) by iterating over gem query output Jan 4 '19 0 Hammer command in Puppet exec fails (foreman 1.20.1) Feb 6 '19

I can see a problem in the Only IF statement. $_DisplayName should be $_.DisplayName , you need to use Dot operator to cherry pick the properties of an Object and \ is also not required.

List of built-in functions, An array, hash, or other iterable object that the function will iterate over. Check that all values are a multiple of 10 and keys start with 'abc' $data = {abc_123 In addition to names in string form, you may also directly use Class and Resource For the hash $data, run a lambda using each item's key and value to create a  how to check if a hash/array is empty or not in puppet .pp/module (not in template)? Showing 1-8 of 8 messages.

@John-Bollinger - Puppet and PowerShell = PuppetHell! :)

I finally managed to solve this problem but feel that my code blocks are way to long with too many substitutions. I've tried shorter methods to no avail, so if anyone can suggest working shorter code that would be appreciated.

In short, I ended up having to

  1. get the hiera hash keys (titles) into suitable array syntax for Powershell comparison by:

    a. adding single quotes and commas as separators

    b. adding a single quote and double quote at the beginning and end of array

    c. removing parenthesis from anywhere in the array as PowerShell spat it on them

    $firewall_hiera = regsubst(regsubst(regsubst(join(keys($firewall_rules), "', '"), '^', '"\''), '$', '\'"'), '[\(\)]', '', 'G')
    

produces

    "'Core Networking - DNS UDP-Out', 'Core Networking - Dynamic Host Configuration Protocol DHCP-Out', 'File and Printer Sharing Echo Request - ICMPv4-Out', 'Internet Browsing HTTP-Out', 'Internet Browsing HTTPS-Out'"

  1. Use a Powershell exec to clean out the Firewall if undefined in Puppet hiera by:

    a. using an exec command to purge the host firewall rules with:

    command   => 'netsh advfirewall firewall delete rule name=all; reg delete "HKLM\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\FirewallRules" /f',
    

    b. ensuring idempotency with a very long unless statement:

    unless => "if (@(Compare-Object((Get-NetFirewallRule | foreach {\"'\"+\$_.DisplayName+\"'\"}) -join ', ' -replace '[()]') $firewall_hiera).length -eq 0) {exit 0} else {exit 1}",
    

which produces for the following to compare-object with the above created $firewall_hiera

    'Core Networking - DNS UDP-Out', 'Core Networking - Dynamic Host Configuration Protocol DHCP-Out', 'File and Printer Sharing Echo Request - ICMPv4-Out', 'Internet Browsing HTTP-Out', 'Internet Browsing HTTPS-Out'

Desired rules are then recreated (if a purge occurs) using the thoward-windows_firewall module which iterates over the same source hiera. This module is HEAPS better than the rather cr4ppy puppetlabs-windows_firewall module, but can't handle Windows 10's horrible firewall additives after user logon.

Interestingly, only the puppet created array needs outer double quotes. As the puppetlabs-powershell module doesn't echo the .ps1's to disk, I had to echo them out manually to get an idea of what was actually being created and test them in Powershell ISE (the older Josh Cooper powershell module did create temporary .ps1's which was handy though less secure)

I was able to put some regsubst or replace characters in arrays, but ^ and $ do not go well in an array.

All this was to solve Windows 10's incessant creation of additional pointless firewall rules only after a user logs on. This continues intermitently for about 6 puppet runs and finally stops. Never had this problem with Server 2012 R2 for example.

Deleting the following registry key (with Puppetlabs registry module ensure => absent) provides some additional quietening but its not an end in itself:

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\AppIso\FirewallRules

Again, ANY assistance with shortening the above code, particularly with regards to the regsubst and -replace commands is appreciated. Keep in mind that the produced arrays need to be comparable using Powershell's 'compare-object' feature or similar.

Thanks to @John-Bollinger for getting me started with creating the array from a hiera hash using the keys and join functions.

Iteration and loops, of code to create a new value, or data structure, by combining values from a provided data structure. Hashes preserve the order in which their keys and values were written. For information about the syntax of function calls, see the functions By writing defined resource types and using arrays as resource titles you  I know I need to have a .each look in there somewhere, and also tidy up the powershell 'only if' so that it looks at just the titles in the hash (perhaps re-written to an array of just hash titles) and runs the exec if there are rules on the host that aren't in Hiera, but am getting a bit lost. Any help sincerely appreciated.

Hashes, An array, hash, or other iterable object that the function will iterate over. Check that all values are a multiple of 10 and keys start with 'abc' $data In addition to names in string form, you may also directly use Class and This function can be used to create defined resources and classes, as well as native resources. For more detailed control over the formatting (including indentations and line breaks, delimiters around arrays and hash entries, between key/values in hash entries, and individual formatting of values in the array) see the new function for String and its formatting options for Array and Hash.

Creating templates using Embedded Puppet, Hashes map keys to values, maintaining the order of the entries according to insertion order. Nested arrays and hashes can be accessed by chaining indexes: By default, Array matches arrays of any length, provided all values in the array match the abstract data type Data. You can use parameters to restrict which values Array matches. Parameters

Resources, Boolean $keys_enable, String $keys_file, Array $keys_trusted, String Execute an expression without inserting a value. The keys of the hash match the variable names you'll be using in the template, minus the leading $ sign. Before deploying a template, validate its syntax and render its output to make sure the  First, Puppet checks whether the control and array are the same length, then each corresponding element is compared using these same case matching rules. Hashes compare each key-value pair. To match, the control value and the case must have the same keys, and each corresponding value is compared using these same case matching rules.

Comments
  • Great, that has certainly got me 1 step closer. You are correct that I need to quote the output, which is doable with '", "' although it doesn't put in the first or last quotes. Do you know how to delimit with a newline? I have tried '\n' and "\n" but they come out literal or fail altogether. If I can't get the output to be item quoted, then I may try to compare the entire string as the result is really the same for this purpose anyway.
  • @xit, have you tried a literal newline, quoted? Puppet's quoted strings are not limited to a single physical line. But that's a guess -- I would have expected "\n" to work.
  • Thanks, you are correct -I missed the dot. However with Puppet I believe that when passing a $ variable to an exec it needs to be escaped or it will be treated as a Puppet variable, hence why the second $ isn't escaped.
  • Got it, was the missing dot made your script fail ?
  • no, it still doesn't apply in the way I need, i.e. I need the exec to only run if comparing existing rules on the host against those in hiera show additional rules on the host
  • Note that if I manually create a rule "example", the thoward-windows_firewall puppet module will purge it. The problem is with some of the newer Windows 10 rules for services such as Cortana which don't get purged correctly but can be overcome with the exec 'netsh advfirewall reset' first.
  • This should have been a comment on the OP's question or self-answer, not an answer in its own right.
  • I managed to slithly shorten the array processing and have editing my code lines above to refect this to:
  • Powershell does not need a replacement string if it is nul, and the string can be pre and postpended with <string> + or + <string>. Regsubst does require a blank declaration for nul replacement, and I cannot seem to pre and postpend the array to be outer double-quoted using the regex "^(.*)$", "$1"