<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Puppet on Puppeteers Oy</title>
		<link>https://www.puppeteers.net/tags/puppet/</link>
		<description>Recent content in Puppet on Puppeteers Oy</description>
		<generator>Hugo</generator>
		<language>en-US</language>
		
		
		
		
			<lastBuildDate>Thu, 22 Dec 2022 00:00:00 +0000</lastBuildDate>
		
			<atom:link href="https://www.puppeteers.net/tags/puppet/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>Convert a hash into JSON in Puppet</title>
				<link>https://www.puppeteers.net/blog/convert-a-hash-into-json-in-puppet/</link>
				<pubDate>Thu, 22 Dec 2022 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/convert-a-hash-into-json-in-puppet/</guid>
				<description>&lt;p&gt;&lt;img src=&#34;https://puppeteers-public.hel1.your-objectstorage.com/pexels-jess-loiterton-9259842.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;This article shows you how to convert a hash into JSON in Puppet using a simple &lt;a href=&#34;https://www.puppet.com/docs/puppet/7/lang_template_erb.html&#34;&gt;ERB template&lt;/a&gt; that gets its data from &lt;a href=&#34;https://www.puppet.com/docs/puppet/7/hiera_intro.html&#34;&gt;Hiera&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Suppose you have this data in Hiera:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;myhash:&#xA;  mykey:&#xA;    foo: one&#xA;    bar: two&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;Converting a hash into a JSON file on the target node is surprisingly easy. First look up the data:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$myhash = lookup(&amp;#39;myhash&amp;#39;, Hash)&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;Then create a simple ERB template (here: &lt;em&gt;mymodule/templates/myhash.erb&lt;/em&gt;)&lt;/p&gt;</description>
			</item>
			<item>
				<title>Using .sync.yml in Puppet Development Kit (PDK)</title>
				<link>https://www.puppeteers.net/blog/using-sync-yml-in-puppet-development-kit-pdk/</link>
				<pubDate>Tue, 09 Aug 2022 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/using-sync-yml-in-puppet-development-kit-pdk/</guid>
				<description>&lt;p&gt;&lt;a href=&#34;https://puppet.com/try-puppet/puppet-development-kit/&#34;&gt;Puppet Development Kit&lt;/a&gt; is probably the best thing since sliced bread if you work a lot with Puppet. It makes adding basic validation and unit tests trivial with help from &lt;a href=&#34;https://rspec-puppet.com/&#34;&gt;rspec-puppet&lt;/a&gt;. It also makes it very easy to build module packages for the &lt;a href=&#34;https://forge.puppet.com/&#34;&gt;Puppet Forge&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;That said, there is a minor annoyance with it: whenever you run &amp;ldquo;pdk update&amp;rdquo; to update PDK to the latest version, all your local changes to files such as .gitignore get destroyed, because PDK creates those files from &lt;a href=&#34;https://github.com/puppetlabs/pdk-templates/&#34;&gt;PDK templates&lt;/a&gt; and any local changes are mercilessly wiped. Fortunately there is a way to &lt;em&gt;sync&lt;/em&gt; your local changes to the files generated from the templates with &lt;em&gt;.sync.yml&lt;/em&gt;. While .sync.yml is is &lt;a href=&#34;https://github.com/puppetlabs/pdk-templates/blob/main/README.md&#34;&gt;pretty well documented&lt;/a&gt;, documentation is lots and lots of words with very few examples. So, here&amp;rsquo;s a fairly trivial .sync.yml example that ensures local changes to &lt;em&gt;.pdkignore&lt;/em&gt; and &lt;em&gt;.gitignore&lt;/em&gt; persist across PDK updates:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Use content of a file as a variable with Puppet</title>
				<link>https://www.puppeteers.net/blog/load-content-of-a-file-on-an-agent-node-to-a-variable-with-puppet/</link>
				<pubDate>Sat, 16 Jul 2022 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/load-content-of-a-file-on-an-agent-node-to-a-variable-with-puppet/</guid>
				<description>&lt;p&gt;Every now and then a need to use the content of a file as a variable &lt;strong&gt;on an agent node&lt;/strong&gt; arises. Here&amp;rsquo;s one way to do it with the help of a &lt;a href=&#34;https://puppet.com/docs/puppet/7/custom_facts.html&#34;&gt;custom fact&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;First create a custom fact &lt;strong&gt;on the puppet serve&lt;/strong&gt;r:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Facter.add(:my_file_content) do&#xA;  setcode do&#xA;    path = &amp;#34;/path/to/my_file&amp;#34;&#xA;    File.read(path) if File.exist?(path)&#xA;   end&#xA;end&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;You can &lt;strong&gt;confine&lt;/strong&gt; this to restrict it to be available only on selected nodes, for example:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Debugging Puppet Bolt inventory plugins</title>
				<link>https://www.puppeteers.net/blog/debugging-puppet-bolt-inventory-plugins/</link>
				<pubDate>Mon, 14 Mar 2022 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/debugging-puppet-bolt-inventory-plugins/</guid>
				<description>&lt;p&gt;Puppet Bolt handles &lt;a href=&#34;https://puppet.com/docs/bolt/latest/inventory_files.html&#34;&gt;inventories&lt;/a&gt; in a very flexible and powerful manner: you can combine static target definitions and different targets into a single inventory. For example, you can have an inventory which defines some static node names combined with the &lt;a href=&#34;https://forge.puppet.com/modules/puppetlabs/aws_inventory&#34;&gt;AWS inventory&lt;/a&gt;, or one that combines static nodes with the &lt;a href=&#34;https://forge.puppet.com/modules/dylanratcliffe/bolt_vagrant/readme&#34;&gt;Vagrant inventory&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Puppet Bolt inventory plugins do their magic and then output what is essentially a static inventory generated dynamically. When dynamic inventory plugins do not work (as &lt;a href=&#34;https://github.com/dylanratcliffe/bolt_vagrant/pull/5&#34;&gt;in my case&lt;/a&gt;) it can be challenging to figure out what is wrong, let alone how to fix it. However, there&amp;rsquo;s is a very handy way to print out a static version of the entire inventory, including the dynamically created content:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Hiera lookups in rspec-puppet</title>
				<link>https://www.puppeteers.net/blog/hiera-lookups-in-rspec-puppet/</link>
				<pubDate>Wed, 09 Feb 2022 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/hiera-lookups-in-rspec-puppet/</guid>
				<description>&lt;p&gt;While &lt;a href=&#34;https://rspec-puppet.com/&#34;&gt;rspec-puppet documentation&lt;/a&gt; is quite decent, it does not really explain how to test classes that get their parameters via Hiera lookups, such as profiles in the &lt;a href=&#34;https://www.puppeteers.net/blog/fattening-the-workflow-part-4-roles-and-profiles/&#34;&gt;roles and profiles pattern&lt;/a&gt;. Several parameters related to Hiera are listed in the &lt;a href=&#34;https://rspec-puppet.com/documentation/configuration/&#34;&gt;rspec-puppet configuration reference&lt;/a&gt;, but that&amp;rsquo;s all. The other documentation you find on the Internet is generally outdated and not applicable for modules converted with &lt;a href=&#34;https://puppet.com/try-puppet/puppet-development-kit/&#34;&gt;PDK&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;In this article we show how to test an &amp;ldquo;ipa_client&amp;rdquo; class that uses lookups to get its parameter from Hiera. It used to be a profile that was later separated from the &lt;a href=&#34;https://www.puppeteers.net/blog/fattening-the-puppet-workflow-part-1-the-control-repository/&#34;&gt;control-repo&lt;/a&gt; into a separate module, which means that rspec-puppet does not have access to the control-repo&amp;rsquo;s Hiera data.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Serverless Puppet with control repo, Hiera, roles and profiles and Puppet Bolt</title>
				<link>https://www.puppeteers.net/blog/serverless-puppet-with-control-repo-hiera-roles-and-profiles-and-puppet-bolt/</link>
				<pubDate>Tue, 28 Dec 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/serverless-puppet-with-control-repo-hiera-roles-and-profiles-and-puppet-bolt/</guid>
				<description>&lt;p&gt;The traditional way of managing systems with &lt;a href=&#34;https://www.puppeteers.net/learn/what-is-puppet-and-how-does-it-work/&#34;&gt;Puppet&lt;/a&gt; is to install Puppet agent on the nodes being managed and point those agents to a Puppet server (more details &lt;a href=&#34;https://www.puppeteers.net/learn/what-is-puppet-and-how-does-it-work/&#34;&gt;here&lt;/a&gt;). This approach works well for environments with tens or hundreds of nodes, but is an overkill for small environments with just a handful of nodes. Fortunately nowadays &lt;a href=&#34;https://puppet.com/docs/bolt/latest/bolt.html&#34;&gt;Puppet Bolt&lt;/a&gt; enables you to &lt;a href=&#34;https://www.puppeteers.net/learn/understanding-infrastructure-as-code-iac/&#34;&gt;push&lt;/a&gt; configurations to the managed nodes via SSH and WinRM. When using Bolt the &lt;a href=&#34;https://www.puppeteers.net/learn/what-is-puppet-and-how-does-it-work/&#34;&gt;catalog&lt;/a&gt; (desired state) is created on the computer running Puppet Bolt using facts from managed nodes and then sent to the managed nodes to be applied.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Creating Puppet Bolt groups based on AWS tags</title>
				<link>https://www.puppeteers.net/blog/creating-puppet-bolt-groups-based-on-aws-tags/</link>
				<pubDate>Thu, 01 Jul 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/creating-puppet-bolt-groups-based-on-aws-tags/</guid>
				<description>&lt;p&gt;The &lt;a href=&#34;https://www.puppeteers.net/blog/using-tags-in-puppet-bolt-aws_inventory-target_mapping/&#34;&gt;Using tags in Puppet Bolt aws_inventory target_mapping&lt;/a&gt; showed how to use AWS &amp;ldquo;Name&amp;rdquo; tag as the target name for Puppet Bolt. Use of tags can be extended to creating Bolt target groups:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;groups:&#xA;  - name: webservers&#xA;    targets:&#xA;     - _plugin: aws_inventory&#xA;       region:&#xA;         _plugin: env_var&#xA;         var: AWS_DEFAULT_REGION&#xA;       filters:&#xA;         - name: tag:Type&#xA;           values: [webserver]&#xA;       target_mapping:&#xA;         name: tags.Name&#xA;         uri: public_ip_address&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;All you need to do is add a &amp;ldquo;filter&amp;rdquo; section with one filter. The &amp;ldquo;name&amp;rdquo; parameter tells Bolt that the filter is based on tags and what tag to look for. The &amp;ldquo;Type&amp;rdquo; that follows &amp;ldquo;tag&amp;rdquo; is the name of the tag you want to filter by. The &amp;ldquo;values&amp;rdquo; array lists acceptable values for that tag. Any nodes that don&amp;rsquo;t have the tag (here: &amp;ldquo;Type&amp;rdquo;) or where tag value is not acceptable (e.g. &amp;ldquo;db&amp;rdquo;) will not be shown in the group.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Testing Puppet feature environments with Puppet Bolt</title>
				<link>https://www.puppeteers.net/blog/testing-puppet-feature-environments-with-puppet-bolt/</link>
				<pubDate>Fri, 18 Jun 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/testing-puppet-feature-environments-with-puppet-bolt/</guid>
				<description>&lt;p&gt;&lt;a href=&#34;https://www.puppeteers.net/blog/fattening-the-workflow-part-2-r10k/&#34;&gt;Puppet feature environments&lt;/a&gt; are an excellent way to test code before deploying it, typically to production. They allow testing Puppet runs on no-operation mode across the whole node population managed by Puppet. There are sometimes cases where your code changes potentially impact many nodes and you&amp;rsquo;re not exactly sure of their scope or effect. In that case it helps to test your code on all Puppet-managed nodes. The challenge is that Puppet open source does not have any built-in mechanism to do that. This is where we can make good use of Puppet Bolt.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Using tags in Puppet Bolt aws_inventory target_mapping</title>
				<link>https://www.puppeteers.net/blog/using-tags-in-puppet-bolt-aws_inventory-target_mapping/</link>
				<pubDate>Sat, 05 Jun 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/using-tags-in-puppet-bolt-aws_inventory-target_mapping/</guid>
				<description>&lt;p&gt;We&amp;rsquo;re migrating away from Ansible to Puppet Bolt and the fact that Ansible updates broke the old &lt;a href=&#34;https://github.com/ansible/ansible/blob/stable-2.9/contrib/inventory/ec2.py&#34;&gt;ec2.py inventory script&lt;/a&gt; expedited that process. While that inventory script was quite rudimentary, it was able to automatically add human-readable names to the EC2 instance names. So, for example, you could target a node using &amp;ldquo;server_example_org&amp;rdquo; if the Name tag was set to &amp;ldquo;server.example.org&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Puppet Bolt&amp;rsquo;s &lt;a href=&#34;https://github.com/puppetlabs/puppetlabs-aws_inventory&#34;&gt;aws_inventory&lt;/a&gt; &lt;em&gt;seemed&lt;/em&gt; to lack that feature, but after taking a good look at its code this was just a case of missing documentation, which I fixed in &lt;a href=&#34;https://github.com/puppetlabs/puppetlabs-aws_inventory/pull/19&#34;&gt;this PR&lt;/a&gt;. Here&amp;rsquo;s an example of &lt;em&gt;inventory.yaml&lt;/em&gt; for Bolt that maps the value of the &amp;ldquo;Name&amp;rdquo; tag to the name of the Bolt target:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Puppet Bolt: taking action based on exit codes</title>
				<link>https://www.puppeteers.net/blog/puppet-bolt-taking-action-based-on-exit-codes/</link>
				<pubDate>Tue, 13 Apr 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/puppet-bolt-taking-action-based-on-exit-codes/</guid>
				<description>&lt;p&gt;Puppet resources like Package, File or Service are idempotent. In other words, you define the state of the resource and Puppet enforces it. The resources can be used in Bolt plans as well, which frees you from having to do state checks yourself. However, if the thing you&amp;rsquo;re managing is not a Puppet resource you have to check its state manually and use that information to drive login in your plan. Here&amp;rsquo;s a crude but functional PoC for checking if a node&amp;rsquo;s certificate is present on the Puppetmaster and acting accordingly:&lt;/p&gt;</description>
			</item>
			<item>
				<title>The four ways to install Puppet modules</title>
				<link>https://www.puppeteers.net/blog/the-four-ways-to-install-puppet-modules/</link>
				<pubDate>Tue, 13 Apr 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/the-four-ways-to-install-puppet-modules/</guid>
				<description>&lt;p&gt;When working with Puppet and Puppet modules in particular you quickly notice that there are several ways to manage module dependencies. Today I learned about a &lt;em&gt;new&lt;/em&gt; way introduced in Puppet Bolt 3.x, so I thought I&amp;rsquo;d compile a list that celebrates this diversity:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://puppet.com/docs/puppet/7.5/modules_metadata.html&#34;&gt;metadata.json&lt;/a&gt;: used primarily to dependency metadata to &lt;a href=&#34;https://forge.puppet.com/&#34;&gt;Puppet Forge&lt;/a&gt;. Some tools like &lt;a href=&#34;https://github.com/voxpupuli/librarian-puppet&#34;&gt;librarian-puppet&lt;/a&gt; and &lt;a href=&#34;https://www.puppeteers.net/blog/tag/puppet-litmus/&#34;&gt;puppet-litmus&lt;/a&gt; use / can use this file to download dependency modules. The downside is that you can&amp;rsquo;t define the location of the modules, so you&amp;rsquo;re limited to Forge modules.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Notes about puppet-litmus testing</title>
				<link>https://www.puppeteers.net/blog/notes-about-puppet-litmus-testing/</link>
				<pubDate>Sun, 07 Mar 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/notes-about-puppet-litmus-testing/</guid>
				<description>&lt;p&gt;&lt;a href=&#34;https://puppetlabs.github.io/litmus/&#34;&gt;Puppet Litmus&lt;/a&gt; is a Puppet acceptance test harness that &lt;a href=&#34;https://puppetlabs.github.io/litmus/litmus-concepts.html&#34;&gt;leverages&lt;/a&gt; on many existing, proven technologies. In the long run Litmus aims to replace &lt;a href=&#34;https://github.com/voxpupuli/beaker&#34;&gt;Beaker&lt;/a&gt;. However, the latter is currently still the only way to run multi-machine (e.g. cluster) acceptance tests in a standardized way. For example we use Beaker for our Keycloak domain mode cluster tests &lt;a href=&#34;https://github.com/treydock/puppet-module-keycloak/blob/master/spec/acceptance/1_domain_mode_cluster_spec.rb&#34;&gt;in puppet-module-keycloak&lt;/a&gt;. On a high-level the test process is the same as with Beaker:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Provision the SUT (&amp;ldquo;System Under Test&amp;rdquo;)&lt;/p&gt;</description>
			</item>
			<item>
				<title>Passing Vagrant parameters to Puppet Litmus</title>
				<link>https://www.puppeteers.net/blog/passing-vagrant-parameters-to-puppet-litmus/</link>
				<pubDate>Sun, 07 Mar 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/passing-vagrant-parameters-to-puppet-litmus/</guid>
				<description>&lt;p&gt;Puppet Litmus allows creating SUTs (&amp;ldquo;System Under Test&amp;rdquo;) with Vagrant using Virtualbox or Hyper-V as the virtualization backend. However, the default virtual machine settings like 1024MB of memory are only suitable for very simple modules and applications. For example, Litmus testing our &lt;a href=&#34;https://github.com/Puppet-Finland/puppet-puppetmaster&#34;&gt;puppetmaster-installer&lt;/a&gt; will fail with the default settings.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://puppetlabs.github.io/litmus/Litmus-core-commands.html&#34;&gt;Litmus documentation&lt;/a&gt; tells us that you can define parameters in provision.yaml which get then converted to environment variables. What it does not tell what to use these environment variables for. Looking at the Vagrant code in Litmus you can see that those environment variables can be used to drive the Virtualbox settings, among other things:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Limiting the scope of puppet-rspec tests</title>
				<link>https://www.puppeteers.net/blog/limiting-the-scope-of-puppet-rspec-tests/</link>
				<pubDate>Fri, 22 Jan 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/limiting-the-scope-of-puppet-rspec-tests/</guid>
				<description>&lt;p&gt;Some Puppet modules like &lt;a href=&#34;https://github.com/treydock/puppet-module-keycloak/&#34;&gt;puppet-module-keycloak&lt;/a&gt; have hundreds of unit tests. That is good for test coverage, but waiting for test results hurts your productivity when you&amp;rsquo;re developing tests for your new code. There are at least two ways to (temporarily) limit the scope of the tests that you run. First method is baked in into the &lt;a href=&#34;https://puppet.com/try-puppet/puppet-development-kit/&#34;&gt;Puppet Development Kit&lt;/a&gt; command-line:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pdk test unit --tests=spec/classes/init_spec.rb&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;This runs only the tests in the specified file.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Querying DNS in Puppet code</title>
				<link>https://www.puppeteers.net/blog/querying-dns-in-puppet-code/</link>
				<pubDate>Wed, 06 Jan 2021 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/querying-dns-in-puppet-code/</guid>
				<description>&lt;p&gt;I recently had to add access control rules to OpenVPN Access Server to permit certain groups to access certain IP addresses. As we manage our Access Server configurations with Puppet using &lt;a href=&#34;https://github.com/OpenVPN/puppet-openvpnas&#34;&gt;puppet-openvpnas&lt;/a&gt; (by the author), we needed a way to parameterize the allowed IP addresses while keeping the Puppet code readable.&lt;/p&gt;&#xA;&lt;p&gt;This is where &lt;a href=&#34;https://forge.puppet.com/modules/dalen/dnsquery&#34;&gt;puppet-dnsquery&lt;/a&gt; came really handy. It allows using data that&amp;rsquo;s already available in DNS and using it in Puppet code. A simplistic example:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Migrating from Puppet 5 to 6 with Puppet Bolt</title>
				<link>https://www.puppeteers.net/blog/migrating-from-puppet-5-to-6-with-puppet-bolt/</link>
				<pubDate>Thu, 31 Dec 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/migrating-from-puppet-5-to-6-with-puppet-bolt/</guid>
				<description>&lt;p&gt;The main annoyance with agent-based configuration management is the management of the agents themselves. They usually don&amp;rsquo;t require any handholding after the initial install, &lt;em&gt;except&lt;/em&gt; when you need to migrate them to a new environment. For example, migrate from a Puppet 5-based server to Puppet 6.&lt;/p&gt;&#xA;&lt;p&gt;The migration process is not overly difficult, but in order to scale to more than a handful of nodes you will need to orchestrate the process. Historically we would have used Ansible for this task, but nowadays we prefer Puppet Bolt, which is more powerful and flexible in our use-cases. The Bolt plans typically end up being ugly, but based on my experience with Ansible Playbooks that tends to be the case anyways. I guess this is the natural result of mixing state-based configuration management, ad hoc commands and orchestration together.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Creating custom facts programmatically</title>
				<link>https://www.puppeteers.net/blog/creating-custom-facts-programmatically/</link>
				<pubDate>Fri, 18 Dec 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/creating-custom-facts-programmatically/</guid>
				<description>&lt;p&gt;Writing static facts is fairly easy even with a low level of Ruby skills. Below is an example of a fact that returns true or false depending on whether the node has a /boot partition:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Facter.add(:has_bootfs) do&#xA;  setcode do&#xA;    if Facter.value(:mountpoints)[&amp;#39;/boot&amp;#39;].nil?&#xA;      false&#xA;    else&#xA;      true&#xA;    end&#xA;  end&#xA;end&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;As you can see above the required information was already inside the &amp;ldquo;mountpoints&amp;rdquo; fact of Facter.&lt;/p&gt;&#xA;&lt;p&gt;Using the fact is easy:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Fattening the workflow, part 5: Hiera and content encryption</title>
				<link>https://www.puppeteers.net/blog/fattening-the-workflow-part-5-hiera-and-content-encryption/</link>
				<pubDate>Thu, 03 Dec 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/fattening-the-workflow-part-5-hiera-and-content-encryption/</guid>
				<description>&lt;p&gt;Separating data from code in Puppet modules is advisable as it improves reusability of code. The separation can be accomplished with Hiera by having separate levels based on facts, organizational units, locations, etc.&lt;/p&gt;&#xA;&lt;p&gt;Hiera can also be used for storing private data that needs to be protected and must not be readable by outsiders. Typically the data, like all other code, is stored in a version control system that may be located outside the organisation. In such a case Hiera encryption is a must.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Fattening the workflow, part 4: Roles and profiles</title>
				<link>https://www.puppeteers.net/blog/fattening-the-workflow-part-4-roles-and-profiles/</link>
				<pubDate>Wed, 02 Dec 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/fattening-the-workflow-part-4-roles-and-profiles/</guid>
				<description>&lt;p&gt;If the workflow that includes the &lt;a href=&#34;https://www.puppeteers.net/blog/fattening-the-puppet-workflow-part-1-the-control-repository/&#34;&gt;control repository&lt;/a&gt;, &lt;a href=&#34;https://www.puppeteers.net/blog/fattening-the-workflow-part-2-r10k/&#34;&gt;r10k&lt;/a&gt; and &lt;a href=&#34;https://www.puppeteers.net/blog/fattening-the-workflow-part-3-gitlab-and-similar/&#34;&gt;GitLab&lt;/a&gt; still feels too light, there are yet more ways to make the workflow heavier by adding more abstraction to the Puppet modules themselves. This is done by adopting the &amp;ldquo;Roles and Profiles pattern&amp;rdquo; that was &lt;a href=&#34;http://craigdunn.org/2012/05/239/&#34;&gt;developed by Craig Dunn&lt;/a&gt; and popularized in the blog posts by &lt;a href=&#34;http://garylarizza.com/&#34;&gt;Gary Larizza&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Roles and profiles are just Puppet classes. Profiles are usually used for creating technology stacks. A good example is a web application whose installation requires a web server (nginx, apache), a database server (postgresql, mysql) and several other components. The configurations for such an application could of course be done with a &amp;ldquo;regular&amp;rdquo; Puppet module but the reusability of the module would be rather limited for two reasons:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Fattening the workflow, part 2: r10k</title>
				<link>https://www.puppeteers.net/blog/fattening-the-workflow-part-2-r10k/</link>
				<pubDate>Tue, 01 Dec 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/fattening-the-workflow-part-2-r10k/</guid>
				<description>&lt;p&gt;In the previous post I discussed the &lt;a href=&#34;https://www.puppeteers.net/blog/fattening-the-puppet-workflow-part-1-the-control-repository/&#34;&gt;control repository structure&lt;/a&gt;. In this post I&amp;rsquo;ll talk about &lt;a href=&#34;https://github.com/puppetlabs/r10k&#34;&gt;r10k&lt;/a&gt;. That tool is used for deploying control repository branches into matching Puppet environments on the Puppet server. Installing r10k is very straightforward with the bundled gem:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ /opt/puppetlabs/puppet/bin/gem install r10k&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;After this you&amp;rsquo;ll need a settings file for r10k, &lt;em&gt;/etc/puppetlabs/r10k/r10k.yaml:&lt;/em&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;--- &#xA; :cachedir: /opt/puppetlabs/puppet/cache/r10k &#xA; :sources: &#xA; control: &#xA; basedir: /etc/puppetlabs/code/environments &#xA; prefix: false &#xA; remote: git@gitlab.com:myorg/control-repo.git&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;Several requirements must be met before the configuration above will work:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Fattening the Puppet workflow, part 1: The control repository</title>
				<link>https://www.puppeteers.net/blog/fattening-the-puppet-workflow-part-1-the-control-repository/</link>
				<pubDate>Thu, 26 Nov 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/fattening-the-puppet-workflow-part-1-the-control-repository/</guid>
				<description>&lt;p&gt;If editing Puppet code and Hiera directly with puppetmaster feels too easy, you can complicate the Puppet workflow as much you like by adding more components to the palette. Adding the so called &lt;a href=&#34;https://puppet.com/docs/pe/2017.3/code_management/control_repo.html&#34;&gt;control repository&lt;/a&gt; and &lt;a href=&#34;https://puppet.com/docs/puppet/5.3/environments_about.html&#34;&gt;r10k&lt;/a&gt; to Puppet environment maintenance instantly adds several phases to your initially simple workflow. There are also benefits that I&amp;rsquo;ll discuss later on.&lt;/p&gt;&#xA;&lt;p&gt;The control repository (control repo for short) is a Git repository that is used to create dynamic Puppet environments on the Puppet master. Each Git branch can be turned into a matching Puppet environment. The name of the default branch, production, is the same as the environment the Puppet agents use by default. The content of the control repo branches look like this:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Using Puppet Bolt to apply roles to nodes, part 1</title>
				<link>https://www.puppeteers.net/blog/using-puppet-bolt-to-apply-roles-to-nodes-part-1/</link>
				<pubDate>Sat, 17 Oct 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/using-puppet-bolt-to-apply-roles-to-nodes-part-1/</guid>
				<description>&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; this article is somewhat outdated. Please refer to &lt;a href=&#34;https://www.puppeteers.net/blog/serverless-puppet-with-control-repo-hiera-roles-and-profiles-and-puppet-bolt/&#34;&gt;Serverless Puppet with control repo, Hiera, roles and profiles and Puppet Bolt&lt;/a&gt; instead.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://puppet.com/docs/bolt/latest/bolt.html&#34;&gt;Puppet Bolt&lt;/a&gt; is a designed to be an orchestration tool, but it can be used for configuration management as well. For example you may have a small environment of handful of nodes where having a dedicated Puppet server would be an overkill, or you have some nodes in the infrastructure where you can&amp;rsquo;t or don&amp;rsquo;t want to install a Puppet Agent.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Improving Vim&#39;s Puppet integration</title>
				<link>https://www.puppeteers.net/blog/improving-vims-puppet-integration/</link>
				<pubDate>Thu, 15 Oct 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/improving-vims-puppet-integration/</guid>
				<description>&lt;p&gt;A recent package upgrade seemed to break Puppet syntax highlighting horribly on my Fedora 32 laptop. Not only did syntax highlighting not work, it was actually completely wrong and confusing. It seemed as if the Puppet file type detection was missing, so vim was probably using some a syntax highlighter for a wrong type. To see which file types are enabled I used &lt;code&gt;:set filetype&lt;/code&gt; followed by a couple of tabs (more details on &lt;a href=&#34;https://vi.stackexchange.com/questions/5780/list-known-filetypes&#34;&gt;StackExchange&lt;/a&gt;).&lt;/p&gt;</description>
			</item>
			<item>
				<title>Integrating Puppet Bolt with PuppetDB</title>
				<link>https://www.puppeteers.net/blog/integrating-puppet-bolt-with-puppetdb/</link>
				<pubDate>Fri, 24 Apr 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/integrating-puppet-bolt-with-puppetdb/</guid>
				<description>&lt;p&gt;In the &lt;a href=&#34;https://www.puppeteers.net/blog/integrating-puppet-bolt-with-puppetdb/&#34;&gt;Puppet Bolt with a Puppet control repository&lt;/a&gt; blog post we showed how to integrate Puppet Bolt with an existing control repository. We used a static inventory file, which is ok for testing or when you have a few target nodes. However, if you have a Puppetserver running and are not using Bolt to manage the &lt;em&gt;configurations&lt;/em&gt; of your nodes, then the chances are that you have a fairly large node population and maintaining a static inventory would be both tedious and error-prone. One option &lt;a href=&#34;https://puppet.com/docs/bolt/latest/using_plugins.html&#34;&gt;among many&lt;/a&gt; is to use the PuppetDB plugin to generate a dynamic inventory and that is what we describe here.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Puppet environment leakage</title>
				<link>https://www.puppeteers.net/blog/puppet-environment-leakage/</link>
				<pubDate>Wed, 01 Apr 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/puppet-environment-leakage/</guid>
				<description>&lt;p&gt;Occasionally when working with feature branch and updating types and providers you may run into &lt;a href=&#34;https://puppet.com/docs/puppet/5.5/environment_isolation.html&#34;&gt;environment leakage issues&lt;/a&gt;. They manifest themselves as issues that make no particular sense and are caused by the wrong version of a type/provider such as puppetlabs-apt (see this &lt;a href=&#34;https://tickets.puppetlabs.com/browse/MODULES-8019&#34;&gt;issue&lt;/a&gt;) leaking into your feature environment. This can happen when you update external modules in your Puppetfile. The solution (or band-aid) is fortunately fairly easy. On the Puppetmaster run this command:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Puppet Bolt with a Puppet control repository</title>
				<link>https://www.puppeteers.net/blog/puppet-bolt-with-a-puppet-control-repository/</link>
				<pubDate>Mon, 30 Mar 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/puppet-bolt-with-a-puppet-control-repository/</guid>
				<description>&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; this article is somewhat outdated. Please refer to &lt;a href=&#34;https://www.puppeteers.net/blog/serverless-puppet-with-control-repo-hiera-roles-and-profiles-and-puppet-bolt/&#34;&gt;Serverless Puppet with control repo, Hiera, roles and profiles and Puppet Bolt&lt;/a&gt; instead.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://puppet.com/docs/bolt/latest/bolt.html&#34;&gt;Puppet Bolt&lt;/a&gt; is an orchestration tool similar to &lt;a href=&#34;https://www.ansible.com/&#34;&gt;Ansible&lt;/a&gt;. It is suitable for multiple use-cases:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Running ad hoc commands on target nodes. We use this approach for testing Puppet feature branches before approving and merging them.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;One-off configurations written in the Puppet language&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Masterless Puppet environments (look &lt;a href=&#34;https://puppet.com/blog/introducing-masterless-puppet-bolt/&#34;&gt;here&lt;/a&gt;). You may not always have enough privileges, trust or will to manage a complete Puppet infrastructure including Puppetserver, PuppetDB and Puppet Agents.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Simple orchestration with Puppet Bolt</title>
				<link>https://www.puppeteers.net/blog/simple-orchestration-with-puppet-bolt/</link>
				<pubDate>Mon, 30 Mar 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/simple-orchestration-with-puppet-bolt/</guid>
				<description>&lt;p&gt;As discussed in the introductory post &lt;a href=&#34;https://www.puppeteers.net/blog/puppet-bolt-with-a-puppet-control-repository/&#34;&gt;one of the use-cases for Puppet Bolt is orchestration&lt;/a&gt; - running actions on multiple targets in a certain order, possibly using results of actions or data from some targets on other targets. Orchestration with Bolt plans is quite easy: you just create a plan with multiple TargetSpec parameters, each representing a target or a type of target. A classic example is &lt;a href=&#34;https://puppet.com/blog/introducing-masterless-puppet-bolt/&#34;&gt;two or more web servers behind a load balancer&lt;/a&gt;. In this case we do something even simpler and just take a fact from a node called &amp;ldquo;first&amp;rdquo; and create a file on node &amp;ldquo;second&amp;rdquo; that uses the value of that fact in a file.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Puppet types and providers development part 2: creating a resource</title>
				<link>https://www.puppeteers.net/blog/puppet-types-and-providers-development-part-2-creating-a-resource/</link>
				<pubDate>Sat, 07 Mar 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/puppet-types-and-providers-development-part-2-creating-a-resource/</guid>
				<description>&lt;p&gt;This blog post is a part of this blog post series:&lt;/p&gt;&#xA;&lt;p&gt;In the &lt;a href=&#34;https://www.puppeteers.net/blog/puppet-types-and-providers-development-part-1-creating-the-type/&#34;&gt;previous blog post&lt;/a&gt; we created the Puppet type &lt;em&gt;librenms_service&lt;/em&gt; and created a dummy skeleton for the provider implementation. We were able to use the type, but it did not yet do anything. In this blog post we move to implementing the &lt;em&gt;create&lt;/em&gt; method, which in this case was the low-hanging fruit. At this point we do not need to worry about any existence checks (the &lt;em&gt;exists?&lt;/em&gt; method) or trying to minimizing the number of API calls with self.prefetch or something similar.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Puppet types and providers development part 1: creating the type</title>
				<link>https://www.puppeteers.net/blog/puppet-types-and-providers-development-part-1-creating-the-type/</link>
				<pubDate>Tue, 03 Mar 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/puppet-types-and-providers-development-part-1-creating-the-type/</guid>
				<description>&lt;p&gt;This article is a part of this blog post series:&lt;/p&gt;&#xA;&lt;p&gt;Writing Puppet types and providers looks like black magic even to those who have lots of experience in the Puppet language itself. While the official &lt;a href=&#34;https://puppet.com/docs/puppet/latest/custom_types.html&#34;&gt;types development documentation&lt;/a&gt; is quite ok, &lt;a href=&#34;https://puppet.com/docs/puppet/latest/provider_development.html&#34;&gt;provider documentation&lt;/a&gt; is definitely lacking. Not only are there no practical examples from real life, the important features such as prefetching, resource methods and flushing are described only on a very high level. The usual answer to lack of examples is &amp;ldquo;have a look at the built-in resources&amp;rdquo;. But when you look at any built-in resource, you realize that the code is very complex because&lt;/p&gt;</description>
			</item>
			<item>
				<title>Giving special treatment to the last entry when iterating over a Puppet hash</title>
				<link>https://www.puppeteers.net/blog/giving-special-treatment-to-the-last-entry-when-iterating-over-a-puppet-hash/</link>
				<pubDate>Thu, 20 Feb 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/giving-special-treatment-to-the-last-entry-when-iterating-over-a-puppet-hash/</guid>
				<description>&lt;p&gt;When you&amp;rsquo;re iterating over a hash you may end up having to treat the last hash entry differently. For example, if you&amp;rsquo;re creating entries to a JSON file you should have &amp;ldquo;},&amp;rdquo; in between the entries &lt;em&gt;until&lt;/em&gt; you reach the last entry, where you &lt;em&gt;must&lt;/em&gt; have just &amp;ldquo;}&amp;rdquo; or JSON data will be invalid.&lt;/p&gt;&#xA;&lt;p&gt;Now, hashes do not have indexes, nor does Puppet support variable reassignment along the lines of $x = $x +1, so you&amp;rsquo;re out of luck trying to get the length and comparing that to a counter. So, you need some other way to detect when iteration has reached the last entry in the hash.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Debugging rspec-puppet</title>
				<link>https://www.puppeteers.net/blog/debugging-rspec-puppet/</link>
				<pubDate>Fri, 07 Feb 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/debugging-rspec-puppet/</guid>
				<description>&lt;p&gt;Usually writing rspec-puppet tests is straightforward. Then you run into some problem that does not make any sense. In my case I ran into a couple of duplicate resource declaration issues when adding rspec tests to our &lt;a href=&#34;https://github.com/Puppet-Finland/puppet-puppetmaster&#34;&gt;puppetmaster-installer&lt;/a&gt;. The error message was not that useful:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ pdk test unit --tests=spec/classes/puppetboard_spec.rb&#xA;--- snip ---&#xA; Failures:&#xA; 1) puppetmaster::puppetboard minimal settings on ubuntu-18.04-x86_64 is expected&#xA;    to compile into a catalogue without dependency cycles&#xA;    Failure/Error: it { is_expected.to compile }&#xA;      error during compilation: Evaluation Error: Error while evaluating a&#xA;      Resource Statement, Duplicate declaration: Class[Apache] is already&#xA;      declared; cannot redeclare (file: /home/samuli/opt/puppeteers/puppet-&#xA;      puppetmaster/spec/fixtures/modules/puppetmaster/manifests/puppetboard.pp,&#xA;      line: 156) (file: /home/samuli/opt/puppeteers/puppet-puppetmaster&#xA;      /spec/fixtures/modules/puppetmaster/manifests/puppetboard.pp, line: 156,&#xA;      column: 3) on node puppet.example.org&#xA;--- snip ---&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;That particular block of code in &lt;a href=&#34;https://github.com/Puppet-Finland/puppet-puppetmaster/blob/master/manifests/puppetboard.pp&#34;&gt;puppetboard.pp&lt;/a&gt; did include a parameterized call to the apache class. But nothing else (afaics) included that class. Also, in Vagrant the same code worked just fine, which made things seem even more bizarre. To add insult to the injury the error message did not tell where &lt;em&gt;else&lt;/em&gt; the apache class was included. That would probably have been way too helpful.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Managing Jenkins with Puppet, part 1: Basic installation</title>
				<link>https://www.puppeteers.net/blog/managing-jenkins-with-puppet-part-1/</link>
				<pubDate>Fri, 31 Jan 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/managing-jenkins-with-puppet-part-1/</guid>
				<description>&lt;p&gt;It seems every other organization is using Jenkins these days. Jenkins is a continuous integration and continuous delivery server that can be used to automate building, testing, and delivering or deploying software. &lt;/p&gt;&#xA;&lt;p&gt;Many organizations also use Puppet for their configuration management needs. Puppet is, if not the de facto configuration management solution, at least one of the most mature and field tested ones. It consists of several server components, agents, a declarative programming language, amongst others. Puppet can be used to install, set up and manage large number of all kinds of resources.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Modifying XML files with Puppet&#39;s Augeas resource</title>
				<link>https://www.puppeteers.net/blog/modifying-xml-files-with-puppets-augeas-resource/</link>
				<pubDate>Fri, 31 Jan 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/modifying-xml-files-with-puppets-augeas-resource/</guid>
				<description>&lt;p&gt;This article is based on an &lt;a href=&#34;https://www.puppeteers.net/blog/xml-tiedostojen-muokkaus-puppetin-augeas-resurssilla/&#34;&gt;earlier article&lt;/a&gt; written in Finnish.&lt;/p&gt;&#xA;&lt;p&gt;Here I describe how to make changes to an XML file with Augeas - a useful approach when managing the whole file as a template is out of the question. The configuration file used as an example, web.xml, belong to &lt;a href=&#34;https://github.com/pwm-project/pwm&#34;&gt;Pwm&lt;/a&gt;, and we intend to set the webapp context parameter.&lt;/p&gt;&#xA;&lt;p&gt;As discussed before it is important to get an idea of how Augeas sees the file before attempting to modify it with Puppet. First we tell which Augeas lens to use (here: Xml) and which file(s) to associate with the Xml lens. This can be done with in one step:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Using Augeas to manage resources in Puppet</title>
				<link>https://www.puppeteers.net/blog/using-augeas-to-manage-resources-in-puppet/</link>
				<pubDate>Fri, 31 Jan 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/using-augeas-to-manage-resources-in-puppet/</guid>
				<description>&lt;p&gt;This translated from the original Finnish article that is available &lt;a href=&#34;https://www.puppeteers.net/blog/augeas-resurssien-luonnista-ja-debuggaamisesta/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The Augeas resource in Puppet allows one to manage parts of a complex configuration file instead of managing the whole file using a template or a static file. For trivial cases stdlib&amp;rsquo;s &lt;a href=&#34;https://github.com/puppetlabs/puppetlabs-stdlib/blob/master/REFERENCE.md#file_line&#34;&gt;file_line resource&lt;/a&gt; may be sufficient. While use of templates or static files is in general more reliable and easier than use of Augeas, the latter is quite useful in many cases. As using Puppet Augeas resource directly is quite challenging, a useful practice is to first make the changes manually with &amp;ldquo;augtool&amp;rdquo; and only then convert the results into Puppet Augeas resources. We use PostgreSQL&amp;rsquo;s pg_hba.conf file here as an example.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Allowing changes to a Puppet-managed resource outside of Puppet</title>
				<link>https://www.puppeteers.net/blog/allowing-changes-to-a-puppet-managed-resource-outside-of-puppet/</link>
				<pubDate>Wed, 29 Jan 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/allowing-changes-to-a-puppet-managed-resource-outside-of-puppet/</guid>
				<description>&lt;p&gt;When a resource is managed by Puppet it is typically managed fully, or not at all. Exceptions to this rule, such as the &amp;ldquo;replace&amp;rdquo; parameter in the &lt;a href=&#34;https://puppet.com/docs/puppet/5.5/type.html#file-attribute-replace&#34;&gt;File resource&lt;/a&gt;, are rare. However, sometimes you end up having to change the properties of a resource outside of Puppet &lt;em&gt;without&lt;/em&gt; making Puppet overwrite those changes on the next run. A good example is a set of firewall rules managed with &lt;a href=&#34;https://forge.puppet.com/puppetlabs/firewall&#34;&gt;puppetlabs-firewall&lt;/a&gt; - you generally want to manage the whole ruleset with Puppet so that you can be sure of the state of the ruleset. For example, if you have libvirt installed, a bunch of virbr0-related rules may appear without your consent. On RedHat-based distros you may also see several rules getting removed by puppetlabs-firewall. Or a fellow sysadmin may have added some random iptables rules for debugging purposes. These are good reasons to have&lt;/p&gt;</description>
			</item>
			<item>
				<title>Getting the interface that matches an IP in Puppet</title>
				<link>https://www.puppeteers.net/blog/getting-the-interface-that-matches-an-ip-in-puppet/</link>
				<pubDate>Tue, 14 Jan 2020 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/getting-the-interface-that-matches-an-ip-in-puppet/</guid>
				<description>&lt;p&gt;I was recently in a situation where I needed to figure out which interface name matched a particular, static IPv4 address. On a single or a handful of nodes this could have been hardcoded in Hiera, for example, but there were dozens of nodes. The way I solved this was by creating a custom fact that uses the IPv4 address and loops through the interfaces list to find the matching interface name:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Why modules in Puppetfile should be sorted</title>
				<link>https://www.puppeteers.net/blog/why-modules-in-puppetfile-should-be-sorted/</link>
				<pubDate>Mon, 30 Dec 2019 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/why-modules-in-puppetfile-should-be-sorted/</guid>
				<description>&lt;p&gt;This is probably one of the shortest blog postings in the history of this blog. But as this topic does not seem to be covered in more generic articles about avoiding Git merge conflicts (e.g. &lt;a href=&#34;https://geshan.com.np/blog/2016/04/3-simple-rules-for-less-or-no-git-conflicts/&#34;&gt;this&lt;/a&gt;, &lt;a href=&#34;https://medium.com/front-end-weekly/avoid-80-of-merge-conflicts-with-git-rebase-b5d755a082a6&#34;&gt;this&lt;/a&gt; and &lt;a href=&#34;https://team-coder.com/avoid-merge-conflicts/&#34;&gt;this&lt;/a&gt;), here it goes.&lt;/p&gt;&#xA;&lt;p&gt;Adding modules to the Puppetfile is a quite common task. When you or your team has multiple feature branches at the same time many of those branches may add new Puppet modules to the Puppetfile. This is fine, unless the new modules are added to the bottom of the Puppetfile: Git will get confused because it sees that multiple commits have changed the exact same place in the Puppetfile.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Puppet rspec testing: understanding describe and context</title>
				<link>https://www.puppeteers.net/blog/puppet-rspec-testing-understanding-describe-and-context/</link>
				<pubDate>Fri, 06 Dec 2019 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/puppet-rspec-testing-understanding-describe-and-context/</guid>
				<description>&lt;p&gt;In the &lt;a href=&#34;https://www.puppeteers.net/blog/puppet-module-rspec-testing-with-pdk/&#34;&gt;Puppet module rspec testing with PDK&lt;/a&gt; blog post I covered the procedure of creating and running puppet-rspec tests with PDK. This blog post will go more in-depth to that topic, in particular &lt;em&gt;describe&lt;/em&gt; and &lt;em&gt;context&lt;/em&gt; which you see in all puppet-rspec tests.&lt;/p&gt;&#xA;&lt;p&gt;When looking at the puppet-rspec test syntax the first time it looks very &amp;ldquo;rubyesque&amp;rdquo;. Somebody used to staring at Python code, for example, could call the syntax &amp;ldquo;odd&amp;rdquo; or &amp;ldquo;1337&amp;rdquo;. Fortunately reading the &lt;a href=&#34;https://relishapp.com/rspec&#34;&gt;Ruby rspec documentation&lt;/a&gt; helps understand what is going on under the hood and thus making the spec files look less magical. In my case rspec documentation helped understand how contexts work. In the &lt;a href=&#34;https://www.puppeteers.net/blog/puppet-module-rspec-testing-with-pdk/&#34;&gt;previous blog post&lt;/a&gt; a context was created for every supported operating system listed in metadata, and rspec tests were run for each of them by using facts from facterdb.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Testing Puppet feature branches in Foreman</title>
				<link>https://www.puppeteers.net/blog/testing-puppet-feature-branches-in-foreman/</link>
				<pubDate>Tue, 26 Nov 2019 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/testing-puppet-feature-branches-in-foreman/</guid>
				<description>&lt;p&gt;With default settings &lt;a href=&#34;https://theforeman.org/&#34;&gt;Foreman&lt;/a&gt; does not allow Puppet Agents to specify their own environment when connecting to the Puppetserver. This makes it impossible to use r10k and feature branch workflow like you can with a normal Puppetserver. This behavior can fortunately be reverted easily by modifying Foreman configuration file, &lt;em&gt;/etc/foreman/settings.yaml&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Allow testing feature branches on nodes&#xA;:enc_environment: false&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;After modifying settings restart the webserver, e.g.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Explicit high-level dependencies in Puppet</title>
				<link>https://www.puppeteers.net/blog/overusing-the-anchor-pattern/</link>
				<pubDate>Wed, 09 Oct 2019 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/overusing-the-anchor-pattern/</guid>
				<description>&lt;p&gt;I&amp;rsquo;ve noticed that some people - I won&amp;rsquo;t reveal their names here to protect them - love using contain and/or ordering arrows in Puppet. A typical use-case is for role classes:&lt;/p&gt;&#xA;&lt;p&gt;class role::webserver {&#xA;contain ::packetfilter::endpoint&#xA;contain ::profile::monit&#xA;contain ::profile::unixbase&#xA;contain ::profile::ipa_client&#xA;contain ::profile::webserver&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;Class[::packetfilter::endpoint]&lt;/code&gt;&#xA;&lt;code&gt;-&amp;gt; Class[::profile::monit]&lt;/code&gt;&#xA;&lt;code&gt;-&amp;gt; Class[::profile::unixbase]&lt;/code&gt;&#xA;&lt;code&gt;-&amp;gt; Class[::profile::ipa_client]&lt;/code&gt;&#xA;&lt;code&gt;-&amp;gt; Class[::profile::webserver]&lt;/code&gt;&#xA;}&lt;/p&gt;&#xA;&lt;p&gt;The above code nicely ensures that all the classes get applied in the exact order defined above. What happens under the hood is that Puppet generates dependencies between the classes and applies them in order.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Reprovisioning bare metal Linux servers with Foreman and IPMI</title>
				<link>https://www.puppeteers.net/blog/reprovisioning-bare-metal-linux-servers-with-foreman-and-ipmi/</link>
				<pubDate>Wed, 25 Sep 2019 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/reprovisioning-bare-metal-linux-servers-with-foreman-and-ipmi/</guid>
				<description>&lt;p&gt;Recently we had to reprovision a bare metal server that was in Foreman and had IPMI support. The goal was to check if our automated &amp;ldquo;force-join to FreeIPA domain after reprovisioning&amp;rdquo; logic worked as intented. We did not have access to any graphical IPMI console, nor Foreman&amp;rsquo;s GUI, so we had to do everything from the command-line. The host we reprovisioned was a CentOS 7 instance. It is assumed that &lt;a href=&#34;https://community.pivotal.io/s/article/How-to-work-on-IPMI-and-IPMITOOL&#34;&gt;IPMI tools are installed&lt;/a&gt;. This post was inspired by the &lt;a href=&#34;https://hobo.house/2016/08/10/automate-supermicro-server-provisioning-or-die-trying/&#34;&gt;Automate SuperMicro Server Provisioning or Die Trying&lt;/a&gt; blog post.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Enabling PHP 7.4 on CentOS 7 with Puppet</title>
				<link>https://www.puppeteers.net/blog/enabling-php-7-4-on-centos-7-with-puppet/</link>
				<pubDate>Tue, 17 Sep 2019 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/enabling-php-7-4-on-centos-7-with-puppet/</guid>
				<description>&lt;p&gt;The PHP version comes in default CentOS 7 repositories is rather horribly outdated. This is problematic when running Wordpress, which has to be upgraded constantly, and really old PHP versions may not be accepted, security-patched or not. The solution is to get updated PHP from the &lt;a href=&#34;https://rpms.remirepo.net/&#34;&gt;remi yum repositories.&lt;/a&gt; There are two sets of PHP packages &lt;a href=&#34;https://blog.remirepo.net/pages/English-FAQ#scl&#34;&gt;as described here&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Install updated packages that replace the old PHP 5 packages (i.e. names are the same)&lt;/p&gt;</description>
			</item>
			<item>
				<title>Puppet module rspec testing with PDK</title>
				<link>https://www.puppeteers.net/blog/puppet-module-rspec-testing-with-pdk/</link>
				<pubDate>Thu, 05 Sep 2019 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/puppet-module-rspec-testing-with-pdk/</guid>
				<description>&lt;p&gt;If you&amp;rsquo;ve searched for instructions on how to write unit tests for Puppet code you&amp;rsquo;ve probably stumbled upon tutorials such as &lt;a href=&#34;https://puppet.com/blog/next-generation-of-puppet-module-testing&#34;&gt;this&lt;/a&gt; that are of good quality, but suggest installing and using tools such as puppetlabs_rspec_helper directly. Or you may encounter references to &lt;a href=&#34;https://github.com/ruby/rake&#34;&gt;rake&lt;/a&gt;, &lt;a href=&#34;https://bundler.io/&#34;&gt;bundler&lt;/a&gt;, &lt;a href=&#34;https://rvm.io/&#34;&gt;rvm&lt;/a&gt; and &lt;a href=&#34;https://rspec.info/&#34;&gt;rspec&lt;/a&gt;. Then you start experimenting these tools, some of which (rvm and bundler) are aimed at managing the dependency hell that arises when you, or your team, works simultaneously on multiple projects with different dependency requirements. In the end you are maybe able to run spec tests on your Puppet module, but after all installations of gems and experiments you&amp;rsquo;re unsure what you did to make everything work. You also don&amp;rsquo;t want to become a hard-core Ruby developer who actually understands all these tools well - you just want to test your Puppet code.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Moving Jenkins user home directory</title>
				<link>https://www.puppeteers.net/blog/moving-jenkins-user-home-directory/</link>
				<pubDate>Thu, 29 Aug 2019 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/moving-jenkins-user-home-directory/</guid>
				<description>&lt;p&gt;&lt;a href=&#34;https://jenkins.io/&#34;&gt;Jenkins&lt;/a&gt; is a continuous integration (CI) / continuous delivery (CD) platform consisting of a Jenkins master - essentially a controller - which launches jobs on Jenkins slaves. Often slaves are controlled via SSH.&lt;/p&gt;&#xA;&lt;p&gt;Recently I ran into an issue with a legacy Jenkins slave where the &amp;ldquo;jenkins&amp;rdquo; where Puppet tried to enforce the home directory of &amp;ldquo;jenkins&amp;rdquo; to be /home/jenkins instead of /var/lib/jenkins:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Error: Could not set home on user[jenkins]: Execution of &amp;#39;/usr/sbin/usermod -d /home/jenkins jenkins&amp;#39; returned 8: usermod: user jenkins is currently used by p rocess 802  &lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;The usermod command failed because several Jenkins processes (ssh-agent, Java) locked the user from modification. The solution was relatively straightforward:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Lisää läskiä työnkulkuun, osa 4: Roolit ja profiilit</title>
				<link>https://www.puppeteers.net/blog/lisaa-laskia-tyonkulkuun-osa-4-roolit-ja-profiilit/</link>
				<pubDate>Wed, 31 Jan 2018 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/lisaa-laskia-tyonkulkuun-osa-4-roolit-ja-profiilit/</guid>
				<description>&lt;p&gt;Mikäli &lt;a href=&#34;https://www.puppeteers.net/blog/lisaa-laskia-tyonkulkuun-osa-1-hallintarepo/&#34;&gt;hallintarepon&lt;/a&gt;, &lt;a href=&#34;https://www.puppeteers.net/blog/lisaa-laskia-tyonkulkuun-osa-2-r10k/&#34;&gt;r10k:n&lt;/a&gt; ja &lt;a href=&#34;https://www.puppeteers.net/blog/lisaa-laskia-tyonkulkuun-osa-3-gitlab-ja-vastaavat/&#34;&gt;GitLab:in&lt;/a&gt; sisältävä työnkulku tuntuu vielä liian kevyeltä, on läskiä ja abstraktiotasoja lisättävissä onneksi toisaalla, nimittäin itse Puppet-moduuleissa. Pelastuksen tarjoaa ns. &amp;ldquo;Roles and Profiles pattern&amp;rdquo;, jonka &lt;a href=&#34;http://www.craigdunn.org/2012/05/239/&#34;&gt;kehitti alunperin&lt;/a&gt; Craig Dunn, ja joka löi läpi todennäköisesti Gary Larizzan &lt;a href=&#34;http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2/&#34;&gt;blogikirjoitusten&lt;/a&gt; myötä. Alla käytämme englanninkielisen termin sijaan lähes suomenkielistä termiä &amp;ldquo;roolit ja profiilit&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;Roolit ja profiilit ovat aivan tavallisia Puppet-luokkia. Ne eivät ole kuitenkaan ole &lt;a href=&#34;http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-1/&#34;&gt;Gary Larizzan termiä&lt;/a&gt; käyttäen ns. komponenttimoduuleja, jotka on suunniteltu jonkin yksittäisen asian tekoon, ja jotka olisivat siten (ainakin periaatteessa) jaettavissa muiden käyttäjien kesken esim. Puppet Forgessa.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Vagrant, Powershell ja parametrien välittäminen</title>
				<link>https://www.puppeteers.net/blog/vagrant-powershell-ja-parametrit/</link>
				<pubDate>Mon, 13 Nov 2017 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/vagrant-powershell-ja-parametrit/</guid>
				<description>&lt;p&gt;Vagrant on näppärä väline myös Windows-virtuaalikoneiden luontiin esim. Puppet-resurssien testauksessa. Windows-virtuaalikoneille on luontevaa käyttää Powershell-provisiointia, minkä Vagrant tekee melkoisen yksinkertaiseksi. Aika nopeasti ilmenee tarve välittää Powershell-skriptille parametrejä Vagrantin kautta. Tästä on hieman hankalaa löytää esimerkkejä, joten tässä yksi.&lt;/p&gt;&#xA;&lt;p&gt;Oletetaan että meillä on Powershell-skripti nimeltä bootstrap_windows.ps1 Vagrant-projektin alihakemistossa &amp;lsquo;provision&amp;rsquo;. Skripti asentaa ja konfiguroi Puppet-agentin Windows-virtuaalikoneelle. Skriptille pitää välittää kaksi parametria: Puppetserverin FQDN (serverName) ja mukavuusbonuksena virtuaalikoneelle haluttu FQDN (certName). (Käytän itse Vagrantille yaml-muotoista ns. data-driven vagrant-tiedostoa, joten syntaksi on hieman erilainen vakiomuotoiseen Vagrant-tiedostoon verrattuna).&lt;/p&gt;</description>
			</item>
			<item>
				<title>Puppet-moduulien laadunvarmistus, osa 1: automaattiset tarkistustyökalut</title>
				<link>https://www.puppeteers.net/blog/puppet-moduulien-laadunvarmistus-osa-1-automaattiset-tarkistustyokalut/</link>
				<pubDate>Mon, 27 Apr 2015 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/puppet-moduulien-laadunvarmistus-osa-1-automaattiset-tarkistustyokalut/</guid>
				<description>&lt;p&gt;Puppet-moduulien laatu pysyy korkeana, kun syntaksi- ja tyylivirheitä päästetä edes versionhallintaan asti. Puppet-koodin syntaksivirheet löytyvät helposti:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ puppet parser validate tiedosto&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;ERB-templateiden virheet saa selvitettyä seuraavalla tavalla:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ erb -P -x -T &amp;#39;-&amp;#39; tiedosto|ruby -c&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;JSON-tiedostojen (metadata.json) virheet etsiminen vaatii hieman kikkailua:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ cat tiedosto|json_pp -f json -t null&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;Tyylivirheitä voi etsiä puppet-lint -työkalulla:&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ puppet-lint tiedosto&#xA; $ puppet-lint .&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;Puppet-lint tunnistaa myös puppet-koodin, joka on eteenpäin tai taaksepäin epäyhteensopivaa. Onkin hyvin järkevää ajaa puppet-koodi aina puppet-lintin läpi ennen suuria Puppet-ympäristön päivityksiä.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Määriteltyjen resurssien luonti suoraan Puppet-luokista</title>
				<link>https://www.puppeteers.net/blog/maariteltyjen-resurssien-luonti-suoraan-puppet-luokista/</link>
				<pubDate>Mon, 02 Feb 2015 00:00:00 +0000</pubDate>
				<guid>https://www.puppeteers.net/blog/maariteltyjen-resurssien-luonti-suoraan-puppet-luokista/</guid>
				<description>&lt;p&gt;Jotkin Puppet ENC:t (&lt;a href=&#34;https://docs.puppetlabs.com/guides/external_nodes.html&#34;&gt;External Node Classifier&lt;/a&gt;), hyvänä esimerkkinä &lt;a href=&#34;http://theforeman.org/&#34;&gt;Foreman&lt;/a&gt;, eivät tue määriteltyjen resurssien (&lt;a href=&#34;https://docs.puppetlabs.com/puppet/latest/reference/lang_defined_types.html&#34;&gt;Defined Resource Types&lt;/a&gt;) käyttöä suoraan, vaan kaikki konfiguraatiot pitää sisällyttää &lt;a href=&#34;https://docs.puppetlabs.com/puppet/latest/reference/lang_classes.html&#34;&gt;Puppet-luokkiin&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Myös &lt;a href=&#34;https://docs.puppetlabs.com/hiera/1/index.html&#34;&gt;Hieran&lt;/a&gt; kanssa määritellyt resurssit ovat hieman ongelmallisia, koska ne pitää erikseen luoda create_resources funktiolla. Eräs ratkaisu on määrittää kaikki create_resources -funktiot site.pp-tiedostossa (sovellettu &lt;a href=&#34;http://drewblessing.com/blog/-/blogs/puppet-hiera-implement-defined-resource-types-in-hiera&#34;&gt;täältä&lt;/a&gt;):&lt;/p&gt;&#xA;&lt;div class=&#34;code-block&#34;&gt;&#xA;    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;&#xA;      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;&#xA;    &lt;/button&gt;&#xA;    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;create_resources(&amp;#39;mysqldump::backup&amp;#39;,hiera(&amp;#39;mysqldump_backups&amp;#39;, {}))&lt;/code&gt;&lt;/pre&gt;&#xA;  &lt;/div&gt;&lt;p&gt;Tämän lähestymistavan ongelma on se, että uudet määritellyt resurssit pitää muistaa lisätä site.pp-tiedostoon.&lt;/p&gt;&#xA;&lt;p&gt;Yksinkertaisempi ja myös Foremanin kanssa toimiva ratkaisu on luoda määritellyt resurssit suoraan Puppet-moduulin pääluokasta (sovellettu &lt;a href=&#34;https://github.com/arnoudj/puppet-sudo&#34;&gt;täältä&lt;/a&gt;):&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
