<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Martin Alfke</title>
    <description>The latest articles on DEV Community by Martin Alfke (@tuxmea).</description>
    <link>https://dev.to/tuxmea</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F412233%2F4f6397d3-ddc0-4798-b33d-dcce1ff5e41c.png</url>
      <title>DEV Community: Martin Alfke</title>
      <link>https://dev.to/tuxmea</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tuxmea"/>
    <language>en</language>
    <item>
      <title>Das neue Puppet Open Source Projekt heißt "OpenVox"</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Tue, 28 Jan 2025 15:00:38 +0000</pubDate>
      <link>https://dev.to/betadots/das-neue-puppet-open-source-projekt-heisst-openvox-2330</link>
      <guid>https://dev.to/betadots/das-neue-puppet-open-source-projekt-heisst-openvox-2330</guid>
      <description>&lt;p&gt;&lt;a href="https://puppet.com" rel="noopener noreferrer"&gt;Puppet by Perforce&lt;/a&gt; hat &lt;a href="https://www.puppet.com/blog/open-source-puppet-updates-2025" rel="noopener noreferrer"&gt;angekündigt&lt;/a&gt;,&lt;br&gt;
dass die Open Source Pakete ab 2025 nur noch nach Zustimmung einer nicht weiter definierten EULA verfügbar gemacht werden sollen. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;In early 2025, Puppet will begin to ship any new binaries and packages developed by our team to a private, hardened, and controlled location. Community contributors will have free access to this private repo under the terms of an End-User License Agreement (EULA) for development use.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Dabei muss berücksichtigt werden, dass die kostenfreie Variante nur für maximal 25 Nodes gilt.&lt;br&gt;
Wer mehr Systeme mit Puppet Open Source verwalten will, benötigt eine &lt;code&gt;Puppet Labs Support Commercial License&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Die Open Source Community hat das Gespräch mit dem Puppet Management gesucht.&lt;/p&gt;

&lt;p&gt;Hier das Resultat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Das neue Puppet Open Source Projekt heißt &lt;code&gt;OpenVox&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Vorbereitung&lt;/li&gt;
&lt;li&gt;Aus Puppet wird OpenVox und Puppet Open Source&lt;/li&gt;
&lt;li&gt;Source Code, Pakete und Container&lt;/li&gt;
&lt;li&gt;Ich nutze Puppet Open Source. Was muss ich tun?&lt;/li&gt;
&lt;li&gt;Puppet Open Source&lt;/li&gt;
&lt;li&gt;OpenVox&lt;/li&gt;
&lt;li&gt;Die weitere Entwicklung&lt;/li&gt;
&lt;li&gt;Wie kann man helfen?&lt;/li&gt;
&lt;li&gt;Professioneller Support&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Vorbereitung
&lt;/h2&gt;

&lt;p&gt;Vor dem ersten Gespräch mit Puppet hat sich die Community im Vorfeld besprochen und analysiert, ob man als Open Source Projekt in der Lage sein kann, die weitere Entwicklung zu stemmen.&lt;br&gt;
Das Feedback aus der Open Source Community und von Open Source Usern war sehr positiv.&lt;/p&gt;

&lt;p&gt;Einige Nutzer haben sogar direkt angekündigt, dass man Entwickler für dedizierte Aufgaben zur Verfügung stellen möchte.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aus Puppet wird OpenVox und Puppet Open Source
&lt;/h2&gt;

&lt;p&gt;Die Open Source Community hat sich mit Puppet by Perforce darauf geeinigt, dass die weitere Entwicklung von Puppet in einem Fork unter der Verwaltung der Open Source Community erfolgen soll.&lt;/p&gt;

&lt;p&gt;Nach einer &lt;a href="https://github.com/OpenVoxProject/planning/discussions/9" rel="noopener noreferrer"&gt;öffentlichen Abstimmung Ende 2024&lt;/a&gt; innerhalb der Community, hat man sich für den Namen &lt;code&gt;OpenVox&lt;/code&gt; entschieden und beschlossen, dass die Entwicklung innerhalb der bereits existierenden &lt;a href="https://voxpupuli.org" rel="noopener noreferrer"&gt;&lt;code&gt;Vox Pupuli&lt;/code&gt; Puppet Open Source Community&lt;/a&gt; auf &lt;a href="https://github.com/voxpupuli" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; stattfinden soll.&lt;/p&gt;

&lt;p&gt;Da Perforce die Namensrechte an &lt;code&gt;Puppet&lt;/code&gt; besitzt, hat man gemeinsam mit Perforce entschieden, dass die Pakete, die durch das OpenVox Projekt erstellt werden, nicht mehr &lt;code&gt;Puppet&lt;/code&gt; im Namen haben sollen.&lt;br&gt;
Dies ist auch in Hinsicht auf Differenzierbarkeit der Herkunft der Pakete sinnvoll.&lt;br&gt;
Die Open Source Pakete von Perforce werden weiterhin &lt;code&gt;puppet-agent&lt;/code&gt;, &lt;code&gt;puppetdb&lt;/code&gt; und &lt;code&gt;puppetserver&lt;/code&gt; heißen.&lt;/p&gt;

&lt;p&gt;Gemeinsam mit Perforce wurde vereinbart, dass man sowohl die Kommandozeile, wie auch Namen der Konfigurationsdateien und die Schnittstellen zur Erweiterung von Puppet ( &lt;code&gt;/lib/puppet&lt;/code&gt; in Modulen) zu existierenden Installation und Code kompatibel halten möchte.&lt;/p&gt;

&lt;p&gt;Die weitere Kompatibilität soll durch ein &lt;code&gt;Language Steering Committee&lt;/code&gt; sichergestellt werden, in dem alle Beteiligten gemeinsam über Änderungen an der Puppet DSL oder Inkompatibilitäten entscheiden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code, Pakete und Container
&lt;/h2&gt;

&lt;p&gt;Der frei verfügbare &lt;a href="https://github.com/puppetlabs" rel="noopener noreferrer"&gt;Puppet Open Source Code&lt;/a&gt; wurde in das &lt;a href="https://github.com/openvoxproject" rel="noopener noreferrer"&gt;OpenVox GitHub Projekt&lt;/a&gt; geforkt.&lt;/p&gt;

&lt;p&gt;Im ersten Schritt hat das Re-branding stattgefunden.&lt;br&gt;
&lt;strong&gt;Für Anwender ist es wichtig zu wissen, dass Kommandozeilen Tools, Bibliotheken und Puppet Erweiterungen auch unter OpenVox vollständig kompatibel zu Puppet Open Source sind.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Aktuell werden die Pipelines zur Erstellung der RedHat und Debian Pakete auf öffentliche GitHub Actions umgestellt und sollen sehr bald zur Verfügung gestellt werden.&lt;br&gt;
Erste reproduzierbare Build wurden bereits getetstet.&lt;br&gt;
Pakete für Windows und MacOS sind ebenfalls geplant.&lt;/p&gt;

&lt;p&gt;Die Puppet Container (&lt;a href="https://github.com/voxpupuli/container-puppetserver" rel="noopener noreferrer"&gt;puppetserver&lt;/a&gt; und &lt;a href="https://github.com/voxpupuli/container-puppetdb" rel="noopener noreferrer"&gt;puppetdb&lt;/a&gt;) werden nicht mehr weiter gepflegt!&lt;/p&gt;

&lt;p&gt;Statt dessen werden OpenVox Container zur Verfügung gestellt. Aktuell wird an &lt;a href="https://github.com/OpenVoxProject/container-openvoxserver" rel="noopener noreferrer"&gt;OpenVox Server&lt;/a&gt; und &lt;a href="https://github.com/OpenVoxProject/container-openvoxdb" rel="noopener noreferrer"&gt;OpenVoxDB&lt;/a&gt; gearbeitet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ich nutze Puppet Open Source. Was muss ich tun?
&lt;/h2&gt;

&lt;p&gt;Es gibt 2 Möglichkeiten:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Puppet Open Source&lt;/li&gt;
&lt;li&gt;OpenVox&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Puppet Open Source
&lt;/h3&gt;

&lt;p&gt;Man muss bei Puppet die EULA akzeptieren und sich ab 25 Nodes um eine Lizenz kümmern.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenVox
&lt;/h3&gt;

&lt;p&gt;Die Installations-Anleitung für die Migration findet man auf der&lt;br&gt;
&lt;a href="https://voxpupuli.org/openvox/install/" rel="noopener noreferrer"&gt;OpenVox Projekt Seite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Da die Konfigurationspfade und -dateien, sowie die Kommandos alle identisch sind, muss keine weitere Anpassung oder Umstellung vorgenommen werden.&lt;br&gt;
Auch der existierende Puppet Code funktioniert ohne Anpassungen.&lt;/p&gt;

&lt;p&gt;Nutzer von OpenVox werden gebeten die aktuellen Pakete und Container in einer Testumgebung zu validieren.&lt;/p&gt;

&lt;h2&gt;
  
  
  Die weitere Entwicklung
&lt;/h2&gt;

&lt;p&gt;Es gibt eine ganze Reihe von Wünschen aus der Community, die bisher leider nicht umgesetzt wurden.&lt;br&gt;
Zuerst möchte man aktuelle Betriebssystemversionen unterstützen. Hierbei hatte die Community oft nach Paketen für Debian stable und testing gefragt.&lt;/p&gt;

&lt;p&gt;Im nächsten Schritt soll der Code von alten Artefakten befreit werden.&lt;br&gt;
Nicht mehr gepflegte Ruby Erweiterungen sollen durch moderne Implementierungen ersetzt werden.&lt;br&gt;
Der Code Zoo (Jruby, Clojure, Java) soll aufgeräumt werden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wie kann man helfen?
&lt;/h2&gt;

&lt;p&gt;Die Puppet Community möchte sich als Professionelles Open Source Projekt etablieren.&lt;br&gt;
Dazu sucht man die finanzielle Unterstützung von Usern, Firmen und öffentlichen Einrichtungen für Open Source Arbeit.&lt;/p&gt;

&lt;p&gt;Auch individuelle Zuarbeit ist gerne gesehen. Es muss Dokumentation geschrieben werden, die Pakete müssen getestet werden.&lt;br&gt;
Die Community braucht die Unterstützung der Anwender, sei es durch Zuarbeit, Code Reviews oder Bug Reports.&lt;/p&gt;

&lt;p&gt;Im nächsten Schritt benötigt man Spiegel Server, Bandbreite und Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Professioneller Support
&lt;/h2&gt;

&lt;p&gt;Open Source Support ist für viele Kunden ein wichtiger Aspekt bei der Wahl der Mittel.&lt;br&gt;
Für OpenVox gibt es bereits jetzt schon 2 Firmen, die Support anbieten:&lt;/p&gt;

&lt;p&gt;Wir von der &lt;a href="https://www.betadots.de" rel="noopener noreferrer"&gt;betadots GmbH&lt;/a&gt; unterstützen Kunden bei Planung, Installation, Updates von OpenVox, Puppet Open Source und Puppet Enterprise und kümmern uns um die Weiterbildung der Mitarbeiter im Umfeld von Git und GitLab, Foreman/Katello, Puppet/OpenVox und Linux HA.&lt;/p&gt;

&lt;p&gt;In Amerikanischem Raum wird die Firma &lt;a href="https://overlookinfratech.com/" rel="noopener noreferrer"&gt;overlookinfratech&lt;/a&gt; Ansprechpartner für OpenVox Support sein.&lt;/p&gt;

&lt;p&gt;Weitere Firmen werden auf der &lt;a href="https://voxpupuli.org/openvox/support/" rel="noopener noreferrer"&gt;Support Übersicht von OpenVox&lt;/a&gt; gelistet werden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;OpenVox ist ein drop-in Ersatz für Puppet Open Source.&lt;/p&gt;

&lt;p&gt;Die Community hat gezeigt, dass sie den Wunsch nach Weiterentwicklung hat und in der Lage ist diese Arbeiten durchzuführen.&lt;/p&gt;

&lt;p&gt;Professioneller Support und Trainings stehen in den USA und in Europa zur Verfügung.&lt;/p&gt;

&lt;p&gt;Open Source ist die Basis für den Erfolg vieler Unternehmen. Deshalb: Unterstützt Eure Community.&lt;/p&gt;

&lt;p&gt;Für weitere Informationen stehen &lt;a href="//mailto:info@betadots.de"&gt;wir&lt;/a&gt; gerne zur Verfügung.&lt;/p&gt;

&lt;p&gt;Datacenters need automation&lt;/p&gt;

&lt;p&gt;betadots GmbH&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>opensource</category>
      <category>openvox</category>
      <category>cfgmgmt</category>
    </item>
    <item>
      <title>The Ruby side of Puppet - Part 3 - Custom Types and Providers</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Mon, 20 Jan 2025 18:50:29 +0000</pubDate>
      <link>https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma</link>
      <guid>https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma</guid>
      <description>&lt;p&gt;This is the final post in a three-part serie, covering the concepts and best practices for extending &lt;a href="https://www.puppet.com" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt; using &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;custom facts&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_functions_ruby" rel="noopener noreferrer"&gt;custom functions&lt;/a&gt; and &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types" rel="noopener noreferrer"&gt;custom types&lt;/a&gt; and &lt;a href="https://www.puppet.com/docs/puppet/latest/provider_development" rel="noopener noreferrer"&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7"&gt;Part 1&lt;/a&gt; explores how to create custom facts, which allow nodes to send information to the Puppet Server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7"&gt;Part 2&lt;/a&gt; discusses building custom functions to process data or execute specific tasks.&lt;/p&gt;

&lt;p&gt;Part 3 (this post) focuses on custom types and providers, allowing you to extend Puppet's DSL and control system resources.&lt;/p&gt;




&lt;p&gt;Types are at the core of Puppet’s declarative DSL. Types describe the desired state of the system, targeting specific, configurable parts (sometimes OS-specific). Puppet provides a set of &lt;a href="https://www.puppet.com/docs/puppet/latest/type.html" rel="noopener noreferrer"&gt;core types&lt;/a&gt;, available with any Puppet agent installation.&lt;/p&gt;

&lt;p&gt;Some of the core types include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file&lt;/li&gt;
&lt;li&gt;user&lt;/li&gt;
&lt;li&gt;group&lt;/li&gt;
&lt;li&gt;package&lt;/li&gt;
&lt;li&gt;service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Create Custom Types and Providers&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;There are several reasons to develop custom types and providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid relying on the often unreliable or hard-to-maintain &lt;code&gt;exec&lt;/code&gt; resources&lt;/li&gt;
&lt;li&gt;Manage an application that requires CLI commands for configuration.&lt;/li&gt;
&lt;li&gt;Handle configurations with highly specific syntax that existing Puppet types cannot manage (like &lt;code&gt;file&lt;/code&gt; or &lt;code&gt;concat&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;General concepts&lt;/li&gt;
&lt;li&gt;Types and providers in modules&lt;/li&gt;
&lt;li&gt;Type description&lt;/li&gt;
&lt;li&gt;Provider implementation&lt;/li&gt;
&lt;li&gt;Using custom types in Puppet DSL&lt;/li&gt;
&lt;li&gt;Resource API&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  General concepts
&lt;/h2&gt;

&lt;p&gt;A custom type consists of two main parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type definiton&lt;/strong&gt;: This defines how the type will be used in the Puppet DSL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider implementation(s)&lt;/strong&gt;: One or more providers per type define how to interact with the system to manage resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Type definition
&lt;/h3&gt;

&lt;p&gt;The type describes how Puppet resources are declared in the DSL. For example, to manage an application’s configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;   &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;property&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;param&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;app_args&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;auth_method&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the type definition, you typically have a &lt;strong&gt;namevar&lt;/strong&gt; (a key identifier) and mutiple &lt;strong&gt;parameters&lt;/strong&gt; and &lt;strong&gt;properties&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namevar&lt;/strong&gt;: The key identifying the resource instance in the type declaration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties&lt;/strong&gt;: These represent something measurable on the target system, such as a user’s UID or GID, or an application’s config value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameters&lt;/strong&gt;: Parameters influence how Puppet manages a resource but don’t directly map to something measurable on the system. For example, &lt;code&gt;manage_home&lt;/code&gt; in the &lt;code&gt;user&lt;/code&gt; type is a parameter that affects Puppet’s behavior but isn’t a property of the user account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference between parameters and properties is nicely described on the &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types#custom_types" rel="noopener noreferrer"&gt;Puppet Type/Provider development page&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Properties:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Properties correspond to something measurable on the target system. For example, the UID and GID of a user account are properties, because their current state can be queried or changed. In practical terms, setting a value for a property causes a method to be called on the provider."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Parameters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Parameters change how Puppet manages a resource, but do not necessarily map directly to something measurable. For example, the user type’s managehome attribute is a parameter — its value affects what Puppet does, but the question of whether Puppet is managing a home directory isn’t an innate property of the user account."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our example, the property is the value of the config setting, whereas the parameter specifies application attributes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider implementations
&lt;/h3&gt;

&lt;p&gt;Providers define the mechanisms for managing the state of the resources described by types. They handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Determining whether the resource already exists.&lt;/li&gt;
&lt;li&gt;Creating or removing resources.&lt;/li&gt;
&lt;li&gt;Modifying resources.&lt;/li&gt;
&lt;li&gt;Optionally, listing all existing resources of the type.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Types and providers in modules
&lt;/h2&gt;

&lt;p&gt;Custom types and providers are placed within the &lt;code&gt;lib/puppet&lt;/code&gt; directory of a module.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The type file is located in &lt;code&gt;lib/puppet/type&lt;/code&gt; and named after the resource type (&lt;code&gt;app_config.rb&lt;/code&gt; in this case).&lt;/li&gt;
&lt;li&gt;Provider implementations go into the &lt;code&gt;lib/puppet/provider&lt;/code&gt; directory, inside a subdirectory named after the resource type. Each provider is named according to its provider implementation (e.g., &lt;code&gt;ruby.rb&lt;/code&gt;, &lt;code&gt;cli.rb&lt;/code&gt;, &lt;code&gt;cert.rb&lt;/code&gt;, &lt;code&gt;token.rb&lt;/code&gt;, &lt;code&gt;user.rb&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Type: app_config&lt;/li&gt;
&lt;li&gt;Providers:

&lt;ul&gt;
&lt;li&gt;cert&lt;/li&gt;
&lt;li&gt;token&lt;/li&gt;
&lt;li&gt;user
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# &amp;lt;modulepath&amp;gt;/&amp;lt;modulename&amp;gt;
modules/application/
    \- lib/
        \- puppet/
            |- type/
            |    \- app_config.rb
            \- provider/
                \- app_config/
                    |- cert.rb
                    |- token.rb
                    \- user.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Type description
&lt;/h2&gt;

&lt;p&gt;New custom types can be crated using two different API versions.&lt;br&gt;
APIv1 is the old, classic way using getters and setters within the providers.&lt;br&gt;
APIv2 is a new implementation which is integrated into PDK - this implementation is also called &lt;code&gt;Resource API&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the next section we introduce the APIv1 implementation. The Resource API implementation is handled later in this document.&lt;/p&gt;
&lt;h3&gt;
  
  
  APIv1
&lt;/h3&gt;

&lt;p&gt;The traditional method uses &lt;code&gt;Puppet::Type.newtype&lt;/code&gt;, which defines the type.&lt;br&gt;
It is recommended to add the type documentation into the code.&lt;br&gt;
This allows users to run &lt;code&gt;puppet describe &amp;lt;type&amp;gt;&lt;/code&gt; on their system to display the documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="c1"&gt;# ... the code ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Management
&lt;/h3&gt;

&lt;p&gt;The most important feature of a type is to add or remove something from the system.&lt;br&gt;
This is usually handled with the &lt;code&gt;ensure&lt;/code&gt; property.&lt;br&gt;
To enable &lt;code&gt;ensure&lt;/code&gt;, a single line is needed: &lt;code&gt;ensurable&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Namevar
&lt;/h3&gt;

&lt;p&gt;When declaring the type, one must specify a title - one could refer to this as a type instance identifier.&lt;br&gt;
Usually this reflects for something the type is managing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'betadots'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c"&gt;# &amp;lt;- title
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most simple implementation is to add a parameter with the name &lt;code&gt;:name&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app config setting to manage. see app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing the &lt;code&gt;namevar: true&lt;/code&gt; key to the parameter is another way to identify a namevar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;namevar: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'The app config setting key to manage. see app_cli conf --help'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Properties
&lt;/h3&gt;

&lt;p&gt;Next we add the other property. Each property can be validated by its content.&lt;br&gt;
In our demo case we expect a string value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app config setting to manage. see app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The config value to set."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a valid value"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides this one can provide specific valid values which are validated automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:enable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;newvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;newvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that arrays as property values are validated in a different way:&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types#tandp_properties_and_parameters" rel="noopener noreferrer"&gt;Puppet custom types&lt;/a&gt; website:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, if a property is assigned multiple values in an array:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is considered in sync if any of those values matches the current value.&lt;/li&gt;
&lt;li&gt;If none of those values match, the first one is used when syncing the property.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;If all array values should be matched, the property needs the&lt;br&gt;
&lt;code&gt;array_matching&lt;/code&gt;to be set to &lt;code&gt;:all&lt;/code&gt;. The default value is &lt;code&gt;:first&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing values can be done in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;should&lt;/code&gt; for properties, &lt;code&gt;value&lt;/code&gt; for parameters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; for both, properties and parameters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We prefer to use the explizit methods as this makes it more clear, whether we deal with a property or a parameter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameters
&lt;/h3&gt;

&lt;p&gt;Parameters are defined in a similar way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        cli_args =&amp;gt; ['-p'], # persistency
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app config setting to manage. see app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The config value to set."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a valid value"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"CLI options to use during command execution."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a an array"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;defaultto&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-p'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Boolean parameters
&lt;/h3&gt;

&lt;p&gt;Parameter which get a bool value should be handled in a different way to avoid code repetition&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/parameter/boolean'&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:force&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Parameter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatic relationships
&lt;/h3&gt;

&lt;p&gt;Within the type we can specify soft dependencies between different types.&lt;/p&gt;

&lt;p&gt;E.g. the app_cli should use a user which must be available on the system&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;autorequire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From now on the new custom type can already be used in Puppet DSL, the compiler will create a catalog but the agent will produce an error, as there are no functional providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent side prerun evaluation
&lt;/h3&gt;

&lt;p&gt;It is possible to have the agent first check some things prior the catalog will be applied.&lt;br&gt;
This is what the &lt;code&gt;:pre_run_check&lt;/code&gt; method can be used for&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pre_run_check&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app.exe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"App not installed"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;When using multiple providers (similar to the package resource) we want to be sure that the provider has all required implementations (features).&lt;br&gt;
A type can require a feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        file     =&amp;gt; '/opt/app/etc/app.cfg',
        cli_args =&amp;gt; ['-p'], # persistency
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="c1"&gt;# global feature&lt;/span&gt;
  &lt;span class="c1"&gt;# feature :cli, "The cli feature requires some parameters", :methods =&amp;gt; [:cli]&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app config setting to manage. see app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The config value to set."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a valid value"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# The property config_file must be set when using the cli &lt;/span&gt;
  &lt;span class="c1"&gt;#   option to configure app.&lt;/span&gt;
  &lt;span class="c1"&gt;# The cli provider will check for a feature.&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:required_features&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sx"&gt;%w{cli}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The config file to use."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not an absolute path"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="ss"&gt;defaultto: &lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/etc/app.cfg'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"CLI options to use during command execution."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a an array"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="ss"&gt;defaultto: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-p'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provider implementation
&lt;/h2&gt;

&lt;p&gt;Once the type is defined, the provider must handle how the resource is managed. Providers typically implement methods to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if the resource exists (&lt;code&gt;exists?&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Create a new resource (&lt;code&gt;create&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Read existing resource properties (&lt;code&gt;prefetch&lt;/code&gt; or a getter)&lt;/li&gt;
&lt;li&gt;Modify a resource (&lt;code&gt;flush&lt;/code&gt; or a setter).&lt;/li&gt;
&lt;li&gt;Delete a resource (&lt;code&gt;destroy&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types cli provider."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also possible to re-use and extend existing providers classes.&lt;br&gt;
Common code can be placed inside a generic provider (&lt;code&gt;lib/puppet/provider/app_config.rb&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;App_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types cli provider reuses shared/common code from app_config.rb."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to add shared functionality to multiple providers, you can place your code into the PuppetX module directory: &lt;code&gt;lib/puppet_x/&amp;lt;company_name&amp;gt;/&amp;lt;unique class name&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../../puppet_x/betadots/app_api.rb'&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types cli provider reuses shared/common code from app_config.rb."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new provider can be created by inheriting from and extending an existing provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/token.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types token provideris an extension to the api provider."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally one can reuse any provider from any type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/file.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ini_setting&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ruby&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types token provideris an extension to the api provider."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Selection of provider
&lt;/h3&gt;

&lt;p&gt;The Puppet Agent must know which provider to use, if multiple providers are present.&lt;br&gt;
You can use of the &lt;code&gt;provider&lt;/code&gt; meta parameterr or let providers perform checks to determine if they are valid for the system. The options include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;commands&lt;/td&gt;
&lt;td&gt;&lt;code&gt;commands :app =&amp;gt; "/opt/app/bin/app.exe"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the command &lt;code&gt;/opt/app/bin/app.exe&lt;/code&gt; exists. The command can be later used using &lt;code&gt;:app&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - exists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :exists =&amp;gt; "/opt/app/etc/app.conf"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the file exists.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :true =&amp;gt; /^10\./.match(%x{/opt/app/bin/app.exec version})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if Version is 10.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - Fact&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine 'os.family' =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the fact value matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confing - feature&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :feature =&amp;gt; :cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the provider has the feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;defaultfor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaultfor 'os.family' =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use this provider as default for Debian-based systems.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Reading and applying configuration
&lt;/h3&gt;

&lt;p&gt;The provider needs the ability to create, read, update and delete individual configuration states (CRUD API).&lt;/p&gt;

&lt;p&gt;Each configuration property needs a &lt;code&gt;getter&lt;/code&gt; (read) and a &lt;code&gt;setter&lt;/code&gt; (modify) method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="c1"&gt;#   app_config: { 'enable_logging':&lt;/span&gt;
&lt;span class="c1"&gt;#     ensure   =&amp;gt; present,&lt;/span&gt;
&lt;span class="c1"&gt;#     value    =&amp;gt; true,&lt;/span&gt;
&lt;span class="c1"&gt;#   }&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types cli provider."&lt;/span&gt;

  &lt;span class="c1"&gt;# confine to existing command&lt;/span&gt;
  &lt;span class="n"&gt;commands&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"/opt/app/bin/app.exe"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exists?&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_cli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'list'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;grep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{^&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ParserError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'found multiple config items found, please fix this'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'set'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'rm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# getter&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# setter&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'set'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is recommended to also create the &lt;code&gt;instances&lt;/code&gt; class method, which can collect all instances of a resource type into a hash.&lt;br&gt;
The namevar is the hash key, which has a hash of paramters and properties as value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'yaml'&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instances&lt;/span&gt;
  &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_cli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'list'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;attributes_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;key: &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ensure: :present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;instances&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes it is not possible to collect all instances, such as with the &lt;code&gt;file&lt;/code&gt; resource. In such cases no &lt;code&gt;instance&lt;/code&gt; method is defined.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;instance&lt;/code&gt; method is used indirectly be each type when calling &lt;code&gt;prefetch&lt;/code&gt; to get the acutal configuration and returns the&lt;code&gt;@property_hash&lt;/code&gt; instance variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_key&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the type getter and setter to read an manipulate the instance variable, instead of writing &lt;code&gt;getter&lt;/code&gt; and &lt;code&gt;setter&lt;/code&gt; methods for each type.&lt;br&gt;
This behavior is added by declaring the &lt;code&gt;mk_resource_methods&lt;/code&gt; class method.&lt;/p&gt;

&lt;p&gt;Once this is implemented you can run the command &lt;code&gt;puppet resource app_config&lt;/code&gt; to retrieve all existing configurations.&lt;/p&gt;
&lt;h3&gt;
  
  
  Refresh events
&lt;/h3&gt;

&lt;p&gt;In some cases it is required to &lt;code&gt;refresh&lt;/code&gt; a resource, for example, to restart a service, remount a mount, or rerun an exec resource. To allow a type/provider to react to a refresh event, it needs special handling.&lt;/p&gt;

&lt;p&gt;Within the type the &lt;code&gt;refreshable&lt;/code&gt; feature must be activated and a &lt;code&gt;refresh&lt;/code&gt; definition is added.&lt;/p&gt;

&lt;p&gt;The following examples are taken from puppet &lt;code&gt;service&lt;/code&gt; type and provider. The feature describes, which provider definition should be executed upon refresh event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/services.rb&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="ss"&gt;:refreshable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"The provider can restart the service."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:methods&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;refresh&lt;/span&gt;
    &lt;span class="c1"&gt;# Only restart if we're actually running&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:ensure&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;newattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ensure&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:running&lt;/span&gt;
      &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restart&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="s2"&gt;"Skipping restart; service is not running"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using custom types in Puppet DSL
&lt;/h2&gt;

&lt;p&gt;Once the custom type and provider are implemented, you can use them in your manifests as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# Type declaration
&lt;/span&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'enable_logging'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;value&lt;/span&gt;     &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kp"&gt;require&lt;/span&gt;   &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/etc/app.cfg'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="kp"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;|,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Type reference
&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kp"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;App_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'enable_logging'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Type declaration using lambda and splat operator
&lt;/span&gt;&lt;span class="nv"&gt;$config_hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Hash&lt;/span&gt; &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Virtual or exported resource
&lt;/span&gt;&lt;span class="err"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'app_mount'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;value&lt;/span&gt;  &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;{facts['networking']['fqdn']}/srv/app_mount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;tag&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'blog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Resource collector
&lt;/span&gt;&lt;span class="nc"&gt;App_config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'blog'&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resource API
&lt;/h2&gt;

&lt;p&gt;The modern resource API has one limitation: one can not refresh types!&lt;br&gt;
Besides this it also consists of types and providers which must be placed at the same location as the existing implementation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Resource API Type
&lt;/h3&gt;

&lt;p&gt;The resource API type uses an attributes hash to list all parameters and properties. Within the &lt;code&gt;type&lt;/code&gt; key one can use any of the built-in Puppet data types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/resource_api'&lt;/span&gt;

&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResourceApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'app_config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;docs: &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;
      @summary The type to manage app config settings
      @example
      app_config { 'key':
        ensure =&amp;gt; present,
        value  =&amp;gt; 'value',
      }

      This type provides Puppet with the capabilities to manage our application config
&lt;/span&gt;&lt;span class="no"&gt;    EOS&lt;/span&gt;
  &lt;span class="ss"&gt;features: &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="ss"&gt;attributes: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;ensure: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type:    &lt;/span&gt;&lt;span class="s1"&gt;'Enum[present, absent]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc:    &lt;/span&gt;&lt;span class="s1"&gt;'Whether the setting should be added or removed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="s1"&gt;'present'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type:     &lt;/span&gt;&lt;span class="s1"&gt;'String'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc:     &lt;/span&gt;&lt;span class="s1"&gt;'The setting to manage'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;behavior: :namevar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'Variant[String, Integer, Array]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The value to set'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Respource API Provider
&lt;/h3&gt;

&lt;p&gt;The most simple solution is to make use of the &lt;code&gt;SimpleProvider&lt;/code&gt; class.&lt;br&gt;
This provider needs up to 4 defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get&lt;/li&gt;
&lt;li&gt;create&lt;/li&gt;
&lt;li&gt;update&lt;/li&gt;
&lt;li&gt;delete
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/resource_api/simple_provider'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'open3'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Puppet::Provider::AppConfig::Cli&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResourceApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SimpleProvider&lt;/span&gt;
  &lt;span class="vg"&gt;$app_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/opt/app/bin/app.exe'&lt;/span&gt;

  &lt;span class="c1"&gt;# Get: fetching all readable config settings into a hash&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Returning data from command'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app.exe list'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ParseError&lt;/span&gt; &lt;span class="s2"&gt;"Error running command: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
    &lt;span class="c1"&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class="vi"&gt;@command_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@command_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'---'&lt;/span&gt;
      &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
      &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;'ensure'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'present'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Create: add a new config setting with name and should value&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Creating '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' with &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Update: correct an existing setting with name and replace with should value&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Updating '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' with &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Delete: remove a config setting&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Deleting '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe rm &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Types and providers are not complex. They basically describe the CRUD behavior of Puppet.&lt;br&gt;
The type defines how to use the Puppet DSL, while the provider controls how to check and handle specific settings.&lt;/p&gt;

&lt;p&gt;Please stop using &lt;code&gt;exec&lt;/code&gt; for almost anything that is not super simple. In most cases, a simple type and provider will allow better analysis, error handling and control over what is happening. Besides this: &lt;strong&gt;custom types and providers execute faster&lt;/strong&gt; compared to &lt;code&gt;exec&lt;/code&gt; type usage - far more faster!&lt;/p&gt;

&lt;p&gt;The example application and the working types and providers are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app: &lt;a href="https://github.com/betadots/workshop-demo-app" rel="noopener noreferrer"&gt;workshop demo app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The Puppet module with types and providers for the app: &lt;a href="https://github.com/betadots/workshop-demo-module/tree/ruby_workshop" rel="noopener noreferrer"&gt;workshop demo module&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install APP:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-app /opt/app
/opt/app/bin/app.exe

&lt;span class="c"&gt;# Install Puppet module:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-module &lt;span class="nt"&gt;-b&lt;/span&gt; ruby_workshop modules/app
&lt;span class="c"&gt;# Type API V1&lt;/span&gt;
puppet resource app_config &lt;span class="nt"&gt;--modulepath&lt;/span&gt; modules
&lt;span class="c"&gt;# Type Resource API&lt;/span&gt;
puppet resource app_config2 &lt;span class="nt"&gt;--modulepath&lt;/span&gt; modules

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Die Ruby-Seite von Puppet - Teil 3 - Benutzerdefinierte Typen und Provider</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Mon, 20 Jan 2025 18:48:32 +0000</pubDate>
      <link>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp</link>
      <guid>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp</guid>
      <description>&lt;p&gt;Dies ist der letzte Beitrag in einer dreiteiligen Serie, die die Konzepte und Best Practices für die Erweiterung von &lt;a href="https://www.puppet.com" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt; mithilfe von &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;benutzerdefinierten Fakten&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_functions_ruby" rel="noopener noreferrer"&gt;benutzerdefinierten Funktionen&lt;/a&gt; und &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types" rel="noopener noreferrer"&gt;benutzerdefinierten Typen&lt;/a&gt; sowie &lt;a href="https://www.puppet.com/docs/puppet/latest/provider_development" rel="noopener noreferrer"&gt;Providern&lt;/a&gt; behandelt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj"&gt;Teil 1&lt;/a&gt; untersucht, wie man benutzerdefinierte Fakten erstellt, die es Knoten ermöglichen, Informationen an den Puppet-Server zu senden.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3"&gt;Teil 2&lt;/a&gt; behandelt den Aufbau benutzerdefinierter Funktionen zur Verarbeitung von Daten oder zur Ausführung spezifischer Aufgaben.&lt;/p&gt;

&lt;p&gt;Teil 3 (dieser Beitrag) konzentriert sich auf benutzerdefinierte Typen und Provider, mit denen Puppets DSL erweitert und Systemressourcen verwaltet werden können.&lt;/p&gt;




&lt;p&gt;Typen stehen im Zentrum der deklarativen DSL von Puppet. Typen beschreiben den gewünschten Zustand des Systems und zielen auf spezifische, konfigurierbare Teile ab (manchmal betriebssystemspezifisch). Puppet bietet eine Reihe von &lt;a href="https://www.puppet.com/docs/puppet/latest/type.html" rel="noopener noreferrer"&gt;Kern-Typen&lt;/a&gt;, die mit jeder Puppet-Agent-Installation verfügbar sind.&lt;/p&gt;

&lt;p&gt;Einige der Kern-Typen umfassen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file&lt;/li&gt;
&lt;li&gt;user&lt;/li&gt;
&lt;li&gt;group&lt;/li&gt;
&lt;li&gt;package&lt;/li&gt;
&lt;li&gt;service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Warum benutzerdefinierte Typen und Provider erstellen&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Es gibt mehrere Gründe, benutzerdefinierte Typen und Provider zu entwickeln:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vermeiden von den oft unzuverlässigen oder schwer zu wartenden &lt;code&gt;exec&lt;/code&gt;-Ressourcen.&lt;/li&gt;
&lt;li&gt;Verwalten einer Anwendung, die CLI-Befehle zur Konfiguration benötigt.&lt;/li&gt;
&lt;li&gt;Handhaben von Konfigurationen mit hochspezifischer Syntax, die bestehende Puppet-Typen nicht verwalten können (wie &lt;code&gt;file&lt;/code&gt; oder &lt;code&gt;concat&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inhalt&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Allgemeine Konzepte&lt;/li&gt;
&lt;li&gt;Typen und Provider in Modulen&lt;/li&gt;
&lt;li&gt;Typbeschreibung&lt;/li&gt;
&lt;li&gt;Provider-Implementierung&lt;/li&gt;
&lt;li&gt;Verwendung benutzerdefinierter Typen in der Puppet DSL&lt;/li&gt;
&lt;li&gt;Resourcen-API&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Allgemeine Konzepte
&lt;/h2&gt;

&lt;p&gt;Ein Typ besteht aus zwei Hauptteilen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Typdefinition&lt;/strong&gt;: Diese definiert, wie der Typ in der Puppet DSL verwendet wird.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider-Implementierung(en)&lt;/strong&gt;: Ein oder mehrere Provider pro Typ definieren, wie mit dem System interagiert wird, um Ressourcen zu verwalten.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Typdefinition
&lt;/h3&gt;

&lt;p&gt;Der Typ beschreibt, wie Puppet-Ressourcen in der DSL deklariert werden. Zum Beispiel, um die Konfiguration einer Anwendung zu verwalten:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;   &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;property&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;param&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;app_args&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;auth_method&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typdefinition haben einen &lt;strong&gt;Namevar&lt;/strong&gt; (ein Schlüsselbezeichner) und mehrere &lt;strong&gt;Parameter&lt;/strong&gt; und &lt;strong&gt;Properties (Eigenschaften)&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namevar&lt;/strong&gt;: Der Schlüssel, der die Ressourceninstanz in der Typdeklaration identifiziert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties (Eigenschaften)&lt;/strong&gt;: Diese repräsentieren etwas Messbares im Zielsystem, wie die UID oder GID eines Benutzers oder einen Konfigurationswert einer Anwendung.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter&lt;/strong&gt;: Parameter beeinflussen, wie Puppet eine Ressource verwaltet, spiegeln jedoch nicht direkt etwas Messbares im System wider. Zum Beispiel ist &lt;code&gt;manage_home&lt;/code&gt; im &lt;code&gt;user&lt;/code&gt;-Typ ein Parameter, der Puppets Verhalten beeinflusst, aber keine Eigenschaft des Benutzerkontos ist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Der Unterschied zwischen Parametern und Eigenschaften wird auf der &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types#custom_types" rel="noopener noreferrer"&gt;Puppet-Typ/Provider-Entwicklungsseite&lt;/a&gt; gut beschrieben:&lt;/p&gt;

&lt;p&gt;Properties:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Eigenschaften entsprechen etwas Messbarem im Zielsystem. Zum Beispiel sind die UID und GID eines Benutzerkontos Eigenschaften, da ihr aktueller Zustand abgefragt oder geändert werden kann. Praktisch bedeutet das, dass das Festlegen eines Wertes für eine Eigenschaft eine Methode im Provider aufruft."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Parameter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Parameter ändern, wie Puppet eine Ressource verwaltet, entsprechen jedoch nicht unbedingt direkt etwas Messbarem. Zum Beispiel ist das managehome-Attribut des Benutzertyps ein Parameter – sein Wert beeinflusst, was Puppet tut, aber die Frage, ob Puppet ein Home-Verzeichnis verwaltet, ist keine angeborene Eigenschaft des Benutzerkontos."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In unserem Beispiel ist die Eigenschaft der Wert des Konfigurationseinstellungen, während der Parameter die Anwendungsattribute angibt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider-Implementierungen
&lt;/h3&gt;

&lt;p&gt;Provider definieren die Mechanismen zur Verwaltung des Zustands der durch Typen beschriebenen Ressourcen. Sie behandeln:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bestimmung, ob die Ressource bereits existiert&lt;/li&gt;
&lt;li&gt;Erstellen oder Entfernen von Ressourcen.&lt;/li&gt;
&lt;li&gt;Ändern von Ressourcen.&lt;/li&gt;
&lt;li&gt;Optional das Auflisten aller vorhandenen Ressourcen des Typs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Typen und Provider in Modulen
&lt;/h2&gt;

&lt;p&gt;Benutzerdefinierte Typen und Provider werden im Verzeichnis &lt;code&gt;lib/puppet&lt;/code&gt; eines Moduls platziert.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Die Typdatei befindet sich in &lt;code&gt;lib/puppet/type&lt;/code&gt; und ist nach dem Ressourcentyp benannt (&lt;code&gt;app_config.rb&lt;/code&gt; in diesem Fall).&lt;/li&gt;
&lt;li&gt;Provider-Implementierungen gehen in das Verzeichnis &lt;code&gt;lib/puppet/provider&lt;/code&gt;, in ein Unterverzeichnis, das nach dem Ressourcentyp benannt ist. Jeder Provider ist nach seiner Provider-Implementierung benannt (z.B. &lt;code&gt;ruby.rb&lt;/code&gt;, &lt;code&gt;cli.rb&lt;/code&gt;, &lt;code&gt;cert.rb&lt;/code&gt;, &lt;code&gt;token.rb&lt;/code&gt;, &lt;code&gt;user.rb&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beispiel:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Typ: app_config&lt;/li&gt;
&lt;li&gt;Provider:

&lt;ul&gt;
&lt;li&gt;cert&lt;/li&gt;
&lt;li&gt;token&lt;/li&gt;
&lt;li&gt;user
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# &amp;lt;modulpfad&amp;gt;/&amp;lt;modulname&amp;gt;
modules/application/
    \- lib/
        \- puppet/
            |- type/
            |    \- app_config.rb
            \- provider/
                \- app_config/
                    |- cert.rb
                    |- token.rb
                    \- user.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Typbeschreibung
&lt;/h2&gt;

&lt;p&gt;Neue benutzerdefinierte Typen können mit zwei verschiedenen API-Versionen erstellt werden. APIv1 ist die alte, klassische Methode, die Getter und Setter innerhalb der Provider verwendet. APIv2 ist eine neue Implementierung, die in PDK integriert ist – diese Implementierung wird auch als &lt;code&gt;Resource-API&lt;/code&gt; bezeichnet.&lt;/p&gt;

&lt;p&gt;Im nächsten Abschnitt stellen wir die APIv1-Implementierung vor. Die Implementierung der Ressourcen-API wird später in diesem Dokument behandelt.&lt;/p&gt;

&lt;h3&gt;
  
  
  APIv1
&lt;/h3&gt;

&lt;p&gt;Die traditionelle Methode verwendet &lt;code&gt;Puppet::Type.newtype&lt;/code&gt;, die den Typ definiert. Es wird empfohlen, die Typdokumentation in den Code einzufügen. Dies ermöglicht es den Benutzern, &lt;code&gt;puppet describe &amp;lt;type&amp;gt;&lt;/code&gt; auf ihrem System auszuführen, um die Dokumentation anzuzeigen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten
    zur Authentifizierung: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="c1"&gt;# ... der Code ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verwaltung
&lt;/h3&gt;

&lt;p&gt;Die wichtigste Funktion eines Typs besteht darin, etwas zum System hinzuzufügen oder zu entfernen. Dies wird normalerweise mit der &lt;code&gt;ensure&lt;/code&gt;-Eigenschaft behandelt. Um &lt;code&gt;ensure&lt;/code&gt; zu aktivieren, ist eine einzige Zeile erforderlich: &lt;code&gt;ensurable&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten
    zur Authentifizierung: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Namevar
&lt;/h3&gt;

&lt;p&gt;Bei der Deklaration des Typs muss ein Titel angegeben werden – dies könnte man als Typinstanz-Identifikator bezeichnen. In der Regel spiegelt dies wider, wofür der Typ verantwortlich ist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'betadots'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c"&gt;# &amp;lt;- Titel
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Die einfachste Implementierung besteht darin, einen Parameter mit dem Namen &lt;code&gt;:name&lt;/code&gt; hinzuzufügen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das Übergeben des Schlüssels &lt;code&gt;namevar: true&lt;/code&gt; an den Parameter ist eine weitere Möglichkeit, einen Namevar zu identifizieren:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;namevar: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'Der Schlüssel des Anwendungsconfig-Elements, das verwaltet werden soll. Siehe app_cli conf --help'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Eigenschaften
&lt;/h3&gt;

&lt;p&gt;Als Nächstes fügen wir die andere Eigenschaft hinzu. Jede Eigenschaft kann durch ihren Inhalt validiert werden. In unserem Demo-Fall erwarten wir einen Stringwert.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der zu setzende Konfigurationswert."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s ist kein gültiger Wert"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Außerdem kann man spezifische gültige Werte angeben, die automatisch validiert werden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:enable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;newvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;newvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bitte beachten, dass Arrays als Eigenschaftswerte auf andere Weise validiert werden:&lt;/p&gt;

&lt;p&gt;Von der Website &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types#tandp_properties_and_parameters" rel="noopener noreferrer"&gt;Puppet benutzerdefinierte Typen&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Standardmäßig wird eine Eigenschaft, die mehrere Werte in einem Array zugewiesen bekommt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Als synchron betrachtet, wenn einer dieser Werte dem aktuellen Wert entspricht.&lt;/li&gt;
&lt;li&gt;Wenn keiner dieser Werte übereinstimmt, wird der erste Wert beim Synchronisieren der Eigenschaft verwendet.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wenn alle Array-Werte übereinstimmen sollen, muss die Eigenschaft &lt;code&gt;array_matching&lt;/code&gt; auf &lt;code&gt;:all&lt;/code&gt; gesetzt werden. Der Standardwert ist &lt;code&gt;:first&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Der Zugriff auf Werte kann auf zwei Arten erfolgen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;should&lt;/code&gt; für Eigenschaften, &lt;code&gt;value&lt;/code&gt; für Parameter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; für sowohl Eigenschaften als auch Parameter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wir bevorzugen die expliziten Methoden, da dies klarer macht, ob wir es mit einer Eigenschaft oder einem Parameter zu tun haben.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameter
&lt;/h3&gt;

&lt;p&gt;Parameter werden auf ähnliche Weise definiert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        cli_args =&amp;gt; ['-p'], # Persistenz
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der zu setzende Konfigurationswert."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s ist kein gültiger Wert"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"CLI-Optionen, die während der Befehlsausführung verwendet werden sollen."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s ist kein Array"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;defaultto&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-p'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Boolean-Parameter
&lt;/h3&gt;

&lt;p&gt;Parameter, die einen booleschen Wert erhalten, sollten auf andere Weise behandelt werden, um Codewiederholungen zu vermeiden.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/parameter/boolean'&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:force&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Parameter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatische Abhängigkeiten
&lt;/h3&gt;

&lt;p&gt;Innerhalb des Typs können wir weiche Abhängigkeiten zwischen verschiedenen Typen angeben.&lt;/p&gt;

&lt;p&gt;Beispiel: Das &lt;code&gt;app_cli&lt;/code&gt; sollte einen Benutzer verwenden, der im System verfügbar sein muss.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;autorequire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Von nun an kann der neue benutzerdefinierte Typ bereits in Puppet DSL verwendet werden, der Compiler wird ein Katalog erstellen, aber der Agent wird einen Fehler produzieren, da es keine funktionalen Provider gibt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vorabprüfung auf der Agentenseite
&lt;/h3&gt;

&lt;p&gt;Es ist möglich, dass der Agent zunächst einige Dinge überprüft, bevor der Katalog angewendet wird. Dafür kann die Methode &lt;code&gt;:pre_run_check&lt;/code&gt; verwendet werden.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pre_run_check&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app.exe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"App nicht installiert"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Merkmale
&lt;/h3&gt;

&lt;p&gt;Bei der Verwendung mehrerer Provider (ähnlich der Ressourcenpakete) wollen wir sicherstellen, dass der Provider alle erforderlichen Implementierungen (Merkmale - Features) hat. Ein Typ kann ein Merkmal erfordern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        file     =&amp;gt; '/opt/app/etc/app.cfg',
        cli_args =&amp;gt; ['-p'], # Persistenz
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="c1"&gt;# globales Merkmal&lt;/span&gt;
  &lt;span class="c1"&gt;# feature :cli, "Das CLI-Merkmal erfordert einige Parameter", :methods =&amp;gt; [:cli]&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der zu setzende Konfigurationswert."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s ist kein gültiger Wert"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# Die Eigenschaft config_file muss gesetzt werden, wenn die CLI&lt;/span&gt;
  &lt;span class="c1"&gt;#   Option zur Konfiguration von App verwendet wird.&lt;/span&gt;
  &lt;span class="c1"&gt;# Der CLI-Provider wird nach einem Merkmal suchen.&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:required_features&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sx"&gt;%w{cli}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Die zu verwendende Konfigurationsdatei."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s ist kein absoluter Pfad"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;defaultto&lt;/span&gt; &lt;span class="s1"&gt;'/opt/app/etc/app.cfg'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"CLI-Optionen, die während der Befehlsausführung verwendet werden sollen."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s ist kein Array"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;defaultto&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-p'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provider-Implementierung
&lt;/h2&gt;

&lt;p&gt;Sobald der Typ definiert ist, muss der Anbieter steuern, wie die Ressource verwaltet wird. Anbieter implementieren typischerweise Methoden, um:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Überprüfen, ob die Ressource existiert (&lt;code&gt;exists?&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Eine neue Ressource erstellen (&lt;code&gt;create&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Vorhandene Ressourcenattribute lesen (&lt;code&gt;prefetch&lt;/code&gt; oder ein Getter).&lt;/li&gt;
&lt;li&gt;Eine Ressource ändern (&lt;code&gt;flush&lt;/code&gt; oder ein Setter).&lt;/li&gt;
&lt;li&gt;Eine Ressource löschen (&lt;code&gt;destroy&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der CLI-Anbieter des app_config-Typs."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es ist auch möglich, bestehende Anbieterklassen wiederzuverwenden und zu erweitern. Gemeinsamer Code kann in einem generischen Anbieter (&lt;code&gt;lib/puppet/provider/app_config.rb&lt;/code&gt;) platziert werden.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;App_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der CLI-Anbieter des app_config-Typs verwendet gemeinsamen Code aus app_config.rb."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wenn eine gemeinsame Funktionalität für mehrere Anbieter hinzugefügt werden soll, kann man den Code im Puppet_X-Modulverzeichnis ablegen: &lt;code&gt;lib/puppet_x/&amp;lt;unternehmens_name&amp;gt;/&amp;lt;eindeutiger Klassenname&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../../puppet_x/betadots/app_api.rb'&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der CLI-Anbieter des app_config-Typs verwendet gemeinsamen Code aus app_config.rb."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ein neuer Anbieter kann erstellt werden, indem er von einem bestehenden Anbieter erbt und diesen erweitert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/token.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der Token-Anbieter des app_config-Typs ist eine Erweiterung des API-Anbieters."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zusätzlich kann man jeden Anbieter von jedem Typ wiederverwenden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/file.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ini_setting&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ruby&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der Datei-Anbieter des app_config-Typs ist eine Erweiterung des API-Anbieters."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auswahl des Anbieters
&lt;/h3&gt;

&lt;p&gt;Der Puppet-Agent muss wissen, welcher Anbieter zu verwenden ist, wenn mehrere Anbieter vorhanden sind. Man kann den &lt;code&gt;provider&lt;/code&gt;-Meta-Parameter verwenden oder die Anbieter Überprüfungen durchführen lassen, um festzustellen, ob sie für das System gültig sind. Die Optionen umfassen:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Überprüfung&lt;/th&gt;
&lt;th&gt;Beispiel&lt;/th&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Befehle&lt;/td&gt;
&lt;td&gt;&lt;code&gt;commands :app =&amp;gt; "/opt/app/bin/app.exe"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn der Befehl &lt;code&gt;/opt/app/bin/app.exe&lt;/code&gt; existiert. Der Befehl kann später mit &lt;code&gt;:app&lt;/code&gt; verwendet werden.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - existiert&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :exists =&amp;gt; "/opt/app/etc/app.conf"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn die Datei existiert.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :true =&amp;gt; /^10\./.match(%x{/opt/app/bin/app.exec version})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn die Version 10.x ist.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - Fakt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine 'os.family' =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn der Faktwert übereinstimmt.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - Funktion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :feature =&amp;gt; :cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn der Anbieter die Funktion hat.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;defaultfor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaultfor 'os.family' =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anbieter als Standard für Debian-basierte Systeme.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Lesen und Anwenden von Konfigurationen
&lt;/h3&gt;

&lt;p&gt;Der Anbieter benötigt die Fähigkeit, einzelne Konfigurationszustände zu erstellen, zu lesen, zu aktualisieren und zu löschen (CRUD-API).&lt;/p&gt;

&lt;p&gt;Jede Konfigurationseigenschaft benötigt eine &lt;code&gt;getter&lt;/code&gt;- (lesen) und eine &lt;code&gt;setter&lt;/code&gt;- (ändern) Methode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="c1"&gt;#   app_config: { 'enable_logging':&lt;/span&gt;
&lt;span class="c1"&gt;#     ensure   =&amp;gt; present,&lt;/span&gt;
&lt;span class="c1"&gt;#     value    =&amp;gt; true,&lt;/span&gt;
&lt;span class="c1"&gt;#   }&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Der CLI-Anbieter des app_config-Typs."&lt;/span&gt;

  &lt;span class="c1"&gt;# Einschränkung auf bestehenden Befehl&lt;/span&gt;
  &lt;span class="n"&gt;commands&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"/opt/app/bin/app.exe"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exists?&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_cli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'list'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;grep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{^&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ParserError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mehrere Konfigurationselemente gefunden, bitte beheben Sie dies'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'set'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'rm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# getter&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# setter&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'set'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es wird empfohlen, auch die &lt;code&gt;instances&lt;/code&gt;-Klassenmethode zu erstellen, die alle Instanzen eines Ressourcentyps in einem Hash sammeln kann. Der Namevar ist der Hash-Schlüssel, der einen Hash von Parametern und Eigenschaften als Wert hat.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'yaml'&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instances&lt;/span&gt;
  &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_cli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'list'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;attributes_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;key: &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ensure: :present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;instances&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Manchmal ist es nicht möglich, alle Instanzen zu sammeln, z. B. beim &lt;code&gt;file&lt;/code&gt;-Ressourcentyp. In solchen Fällen wird keine &lt;code&gt;instance&lt;/code&gt;-Methode definiert.&lt;/p&gt;

&lt;p&gt;Die &lt;code&gt;instance&lt;/code&gt;-Methode wird indirekt von jedem Typ verwendet, wenn &lt;code&gt;prefetch&lt;/code&gt; aufgerufen wird, um die aktuelle Konfiguration zu erhalten und die &lt;code&gt;@property_hash&lt;/code&gt;-Instanzvariable zurückzugeben.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_key&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dies ermöglicht es dem Typ, Getter und Setter zu verwenden, um die Instanzvariable zu lesen und zu manipulieren, anstatt für jeden Typ &lt;code&gt;getter&lt;/code&gt;- und &lt;code&gt;setter&lt;/code&gt;-Methoden zu schreiben. Dieses Verhalten wird durch die Deklaration der &lt;code&gt;mk_resource_methods&lt;/code&gt;-Klassenmethode hinzugefügt.&lt;/p&gt;

&lt;p&gt;Sobald dies implementiert ist, kann man den Befehl &lt;code&gt;puppet resource app_config&lt;/code&gt; ausführen, um alle vorhandenen Konfigurationen abzurufen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refresh-Ereignisse
&lt;/h3&gt;

&lt;p&gt;In einigen Fällen ist es erforderlich, eine Ressource zu &lt;code&gt;aktualisieren&lt;/code&gt; (refresh), beispielsweise um einen Dienst neu zu starten, ein Laufwerk erneut zu mounten oder eine Exec-Ressource erneut auszuführen. Damit ein Typ/Anbieter auf ein Aktualisierungsereignis reagieren kann, ist eine spezielle Behandlung erforderlich.&lt;/p&gt;

&lt;p&gt;Innerhalb des Typs muss die &lt;code&gt;refreshable&lt;/code&gt;-Funktion aktiviert werden, und eine &lt;code&gt;refresh&lt;/code&gt;-Definition wird hinzugefügt.&lt;/p&gt;

&lt;p&gt;Die folgenden Beispiele stammen vom Puppet &lt;code&gt;service&lt;/code&gt; Typ und Anbieter. Die Funktion beschreibt, welche Anbieterdefinition bei einem Aktualisierungsereignis ausgeführt werden soll.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/services.rb&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="ss"&gt;:refreshable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Der Anbieter kann den Dienst neu starten."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:methods&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;refresh&lt;/span&gt;
    &lt;span class="c1"&gt;# Nur neu starten, wenn wir tatsächlich laufen&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:ensure&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;newattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ensure&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:running&lt;/span&gt;
      &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restart&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="s2"&gt;"Neustart überspringen; Dienst läuft nicht"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verwendung benutzerdefinierter Typen in Puppet DSL
&lt;/h2&gt;

&lt;p&gt;Sobald der benutzerdefinierte Typ und Anbieter implementiert sind, kann man diesen in Puppet Manifesten wie folgt verwenden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# Typdeklaration
&lt;/span&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'enable_logging'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;value&lt;/span&gt;     &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kp"&gt;require&lt;/span&gt;   &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/etc/app.cfg'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="kp"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Typreferenz
&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kp"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;App_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'enable_logging'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Typdeklaration mit Lambda und Splat-Operator
&lt;/span&gt;&lt;span class="nv"&gt;$config_hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Hash&lt;/span&gt; &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Virtuelle oder exportierte Ressource
&lt;/span&gt;&lt;span class="err"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'app_mount'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;value&lt;/span&gt;  &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;{facts['networking']['fqdn']}/srv/app_mount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;tag&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'blog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Ressourcen-Sammler
&lt;/span&gt;&lt;span class="nc"&gt;App_config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'blog'&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resourcen-API
&lt;/h2&gt;

&lt;p&gt;Die moderne Ressourcen-API hat eine Einschränkung: Man kann Typen nicht aktualisieren! Außerdem besteht sie aus Typen und Anbietern, die am selben Ort wie die bestehende Implementierung platziert werden müssen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ressourcen-API-Typ
&lt;/h3&gt;

&lt;p&gt;Der Ressourcen-API-Typ verwendet einen Attributs-Hash, um alle Parameter und Eigenschaften aufzulisten. Innerhalb des &lt;code&gt;type&lt;/code&gt;-Schlüssels kann man jeden der integrierten Puppet-Datentypen verwenden.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/resource_api'&lt;/span&gt;

&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResourceApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'app_config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;docs: &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;
      @summary Der Typ zur Verwaltung von App-Konfigurationseinstellungen
      @example
      app_config { 'key':
        ensure =&amp;gt; present,
        value  =&amp;gt; 'value',
      }

      Dieser Typ bietet Puppet die Möglichkeit, unsere Anwendungsconfig zu verwalten.
&lt;/span&gt;&lt;span class="no"&gt;    EOS&lt;/span&gt;
  &lt;span class="ss"&gt;features: &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="ss"&gt;attributes: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;ensure: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type:    &lt;/span&gt;&lt;span class="s1"&gt;'Enum[present, absent]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc:    &lt;/span&gt;&lt;span class="s1"&gt;'Ob die Einstellung hinzugefügt oder entfernt werden soll'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="s1"&gt;'present'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type:     &lt;/span&gt;&lt;span class="s1"&gt;'String'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc:     &lt;/span&gt;&lt;span class="s1"&gt;'Die Einstellung, die verwaltet werden soll'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;behavior: :namevar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'Variant[String, Integer, Array]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'Der Wert, der gesetzt werden soll'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ressourcen-API-Anbieter
&lt;/h3&gt;

&lt;p&gt;Die einfachste Lösung ist die Verwendung der Klasse &lt;code&gt;SimpleProvider&lt;/code&gt;. Dieser Anbieter benötigt bis zu 4 Definitionen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get&lt;/li&gt;
&lt;li&gt;create&lt;/li&gt;
&lt;li&gt;update&lt;/li&gt;
&lt;li&gt;delete
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/resource_api/simple_provider'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'open3'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Puppet::Provider::AppConfig::Cli&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResourceApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SimpleProvider&lt;/span&gt;
  &lt;span class="vg"&gt;$app_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/opt/app/bin/app.exe'&lt;/span&gt;

  &lt;span class="c1"&gt;# Get: Alle lesbaren Konfigurationseinstellungen in einen Hash abrufen&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Gibt Daten vom Befehl zurück'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app.exe list'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ParseError&lt;/span&gt; &lt;span class="s2"&gt;"Fehler beim Ausführen des Befehls: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
    &lt;span class="c1"&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class="vi"&gt;@command_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@command_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'---'&lt;/span&gt;
      &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
      &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;'ensure'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'present'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Create: Eine neue Konfigurationseinstellung mit Name und Sollwert hinzufügen&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Erstelle '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' mit &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Update: Eine vorhandene Einstellung mit Namen korrigieren und durch den Sollwert ersetzen&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Aktualisiere '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' mit &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Delete: Eine Konfigurationseinstellung entfernen&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Lösche '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe rm &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;Typen und Anbieter sind nicht komplex. Sie beschreiben im Grunde das CRUD-Verhalten von Puppet. Der Typ definiert, wie man die Puppet DSL verwendet, während der Anbieter steuert, wie spezifische Einstellungen überprüft und behandelt werden.&lt;/p&gt;

&lt;p&gt;Eine exzessive Nutzung der &lt;code&gt;exec&lt;/code&gt; Resource ist zu vermweiden. In den meisten Fällen ermöglicht ein einfacher Typ und Anbieter eine bessere Analyse, Fehlerbehandlung und Kontrolle über das, was passiert. Darüber hinaus: &lt;strong&gt;benutzerdefinierte Typen und Anbieter führen schneller aus&lt;/strong&gt; im Vergleich zur Verwendung des &lt;code&gt;exec&lt;/code&gt;-Typs - viel schneller!&lt;/p&gt;

&lt;p&gt;Die Beispielanwendung und die funktionierenden Typen und Anbieter aus diesem Posting sind auf GitHub verfügbar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Die App: &lt;a href="https://github.com/betadots/workshop-demo-app" rel="noopener noreferrer"&gt;Workshop-Demo-App&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Das Puppet-Modul mit Typen und Anbietern für die App: &lt;a href="https://github.com/betadots/workshop-demo-module/tree/ruby_workshop" rel="noopener noreferrer"&gt;Workshop-Demo-Modul&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App-Nutzung:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# APP installieren:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-app /opt/app
/opt/app/bin/app.exe

&lt;span class="c"&gt;# Puppet-Modul installieren:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-module &lt;span class="nt"&gt;-b&lt;/span&gt; ruby_workshop modules/app
&lt;span class="c"&gt;# Type API V1&lt;/span&gt;
puppet resource app_config &lt;span class="nt"&gt;--modulepath&lt;/span&gt; modules
&lt;span class="c"&gt;# Type Resource API&lt;/span&gt;
puppet resource app_config2 &lt;span class="nt"&gt;--modulepath&lt;/span&gt; modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Viel Spaß beim Puppetisieren,&lt;br&gt;&lt;br&gt;
Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>ruby</category>
    </item>
    <item>
      <title>The Ruby side of Puppet - Part 2 - Custom Functions</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Tue, 14 Jan 2025 16:04:08 +0000</pubDate>
      <link>https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7</link>
      <guid>https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7</guid>
      <description>&lt;p&gt;This is the second post in a series of three, covering the concepts and best practices for extending &lt;a href="https://www.puppet.com" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt; using &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;custom facts&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_functions_ruby" rel="noopener noreferrer"&gt;custom functions&lt;/a&gt; and &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types" rel="noopener noreferrer"&gt;custom types&lt;/a&gt; and &lt;a href="https://www.puppet.com/docs/puppet/latest/provider_development" rel="noopener noreferrer"&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7"&gt;Part 1 covers Custom Facts&lt;/a&gt;, which explain how a node can provide information to the Puppet Server.&lt;/p&gt;

&lt;p&gt;Part 2 (this post) focuses on Custom Functions, demonstrating how to develop functions for data processing and execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma"&gt;Part 3&lt;/a&gt; covers Custom Types and Providers, showing how to extend Puppet's DSL functionality.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Custom Functions in Puppet&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Functions in Puppet are executed on the Puppet server, specifically inside the compiler. These functions have access to all facts and Puppet variables, as long as they exist within the function’s namespace.&lt;/p&gt;

&lt;p&gt;There are two main types of functions in Puppet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Statement functions&lt;/li&gt;
&lt;li&gt;Return value functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Statement functions&lt;/strong&gt; can interact with Puppet internals. The most common statement functions are &lt;code&gt;include&lt;/code&gt; or &lt;code&gt;contain&lt;/code&gt;, which add classes to the catalog, or &lt;code&gt;noop&lt;/code&gt; and &lt;code&gt;notice&lt;/code&gt;, which set all resources into simulation mode or print entries into the Puppet server log file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Return value functions&lt;/strong&gt; such as &lt;code&gt;lookup&lt;/code&gt; or &lt;code&gt;read_url&lt;/code&gt;, return values from Hiera or external sources like web servers.&lt;br&gt;
Some return value functions can also manipulate data (&lt;code&gt;each&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Create Custom Functions?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are some reasons you might develop custom functions in Puppet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing a custom Hiera lookup function&lt;/li&gt;
&lt;li&gt;Converting data structures for specific needs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Function development&lt;/li&gt;
&lt;li&gt;Functions in Modules&lt;/li&gt;
&lt;li&gt;General API&lt;/li&gt;
&lt;li&gt;Dispatcher and Define&lt;/li&gt;
&lt;li&gt;Using functions in Puppet DSL&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Function development
&lt;/h2&gt;

&lt;p&gt;Since functions are executed only at compile time, it is advisable to first focus on their core functionality before integrating them with the Function API. This allows you to test the logic with local data, avoiding issues on the Puppet server that could break compilation or even crash the server.&lt;/p&gt;

&lt;p&gt;For example, imagine you have a data structure that outlines various aspects of a system's configuration. Using a function, you can expose specific parts of this data structure as variables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Data Structure&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server_hash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;postfix'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;transport'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mta'&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssl'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="s"&gt;'apache'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vhost'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;server.domain.tld'&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;document_root'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/srv/www/html/server.domain.tld'&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssl'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="s"&gt;'proxy_pass'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8080'&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tomcat'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;crm'&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;frontend'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assume that this data structure comes from Hiera, and you want to allow other teams, who may write their own modules, to reuse this data without performing additional lookups. This would allow you to modify the data structure internally while still providing teams with the required information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Function&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test data&lt;/span&gt;
&lt;span class="n"&gt;server_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'postfix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'config'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'transport'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'mta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'ssl'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s1"&gt;'apache'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'config'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'vhost'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'server.domain.tld'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'document_root'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/srv/www/html/server.domain.tld'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'ssl'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'proxy_pass'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:8080'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s1"&gt;'tomcat'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'config'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'application'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'crm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'service'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'frontend'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# function definition&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_service_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# testing the function&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;read_service_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'tomcat'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Functions in Modules
&lt;/h2&gt;

&lt;p&gt;Puppet provides an API for creating custom functions. To ensure the Puppet agent can locate these functions, they must be placed in specific directories within a module.&lt;/p&gt;

&lt;p&gt;Files inside the module's &lt;code&gt;lib&lt;/code&gt; directory are synchronized across all agents. Note that it is not possible to limit which files are synchronized.&lt;/p&gt;

&lt;p&gt;Custom functions can be added to one of two directory structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lib/puppet/parser/functions&lt;/code&gt; - Functions API v1&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/puppet/functions&lt;/code&gt; - Functions API v2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The newer Functions API v2 supports subdirectories, allowing you to namespace your functions. The filename should match the function name.&lt;/p&gt;

&lt;p&gt;In this post, we’ll focus on the modern API v2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Directory Structure&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# &amp;lt;modulepath&amp;gt;/stdlib/&lt;/span&gt;
  &lt;span class="se"&gt;\-&lt;/span&gt; lib/
    &lt;span class="se"&gt;\-&lt;/span&gt; puppet/
      &lt;span class="se"&gt;\-&lt;/span&gt; functions/
        &lt;span class="se"&gt;\-&lt;/span&gt; stdlib/
          &lt;span class="se"&gt;\-&lt;/span&gt; to_yaml.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# stdlib/lib/puppet/functions/stdlib/to_yaml.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'stdlib::to_yaml'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We recommend prefixing custom functions with the module name to avoid naming conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  General API
&lt;/h2&gt;

&lt;p&gt;To create custom functions in Puppet, use the following structure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the namespace &lt;strong&gt;must&lt;/strong&gt; be identical to the module name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dispatcher and Define
&lt;/h2&gt;

&lt;p&gt;Within the &lt;code&gt;do ... end&lt;/code&gt; of the function, you need to add a dispatcher. The dispatcher validates the input data and links it to a named definition, which executes the corresponding Ruby code.&lt;/p&gt;

&lt;p&gt;The dispatcher can check data types using &lt;code&gt;param&lt;/code&gt; and map values to Ruby symbols. The definition uses the dispatcher’s name and the parameters provided in the dispatcher.&lt;/p&gt;

&lt;p&gt;The final command in the definition determines the return value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Dispatcher for an IPv4 Address&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:ip&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ipaddr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s a table of common parameter methods used in dispatchers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;param&lt;/code&gt; or &lt;code&gt;required_param&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A mandatory argument. May occur multiple times. &lt;strong&gt;Position&lt;/strong&gt;: All mandatory arguments must come first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optional_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An argument that can be omitted. You can use any number of these. When there are multiple optional arguments, users can only pass latter ones if they also provide values for the prior ones. This also applies to repeated arguments. &lt;strong&gt;Position&lt;/strong&gt;: Must come after any required arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;repeated_params&lt;/code&gt; or &lt;code&gt;optional_repeated_params&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A repeatable argument, which can receive zero or more values. A signature can only use one repeatable argument. &lt;strong&gt;Position&lt;/strong&gt;: Must come after any non-repeating arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required_repeated_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A repeatable argument, which must receive one or more values. A signature can only use one repeatable argument. &lt;strong&gt;Position&lt;/strong&gt;: Must come after any non-repeating arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;block_param&lt;/code&gt; or &lt;code&gt;required_block_param&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A mandatory lambda (block of Puppet code). A signature can only use one block. &lt;strong&gt;Position&lt;/strong&gt;: Must come after all other arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optional_block_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An optional lambda. A signature can only use one block. &lt;strong&gt;Position&lt;/strong&gt;: Must come after all other arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Adding a Definition&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Dispatcher&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dispatch :ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def ip&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;param ..., :ipaddr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def ip(ipaddr)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'resolv'&lt;/span&gt;
  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:ip&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ipaddr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Resolv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getname&lt;/span&gt; &lt;span class="n"&gt;ipaddr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now additional dispatchers and definitions can be added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;
  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:ip&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ipaddr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:dnsname&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[/.*/]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:dnsname&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:local_hostname&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[//]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:local_hostname&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Addrinfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'80'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getnameinfo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dnsname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dnsname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Addrinfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dnsname&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getnameinfo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;local_hostname&lt;/span&gt;
    &lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gethostname&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using functions in Puppet DSL
&lt;/h2&gt;

&lt;p&gt;Within Puppet the functions usually gets executed during compile time.&lt;br&gt;
A special case is the &lt;a href="https://www.puppet.com/docs/puppet/8/deferring_functions.html" rel="noopener noreferrer"&gt;deferred function&lt;/a&gt; which gets executed on the agent.&lt;/p&gt;

&lt;p&gt;Let's consider two use-cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;using resolv function on the compiler&lt;/li&gt;
&lt;li&gt;using resolv function on the agent&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Use case 1: compiler execution
&lt;/h3&gt;

&lt;p&gt;The function can be used anywhere in the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;betadots::resolver&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Stdlib&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Fqdn&lt;/span&gt; &lt;span class="nv"&gt;$local_hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;betadots::resolve&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$ip_from_google&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;betadots::resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'www.google.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use case 2: agent execution
&lt;/h3&gt;

&lt;p&gt;The function must be declared to run in deferred mode in Puppet. Avoid using the function within the class header, as this could potentially overwrite the function call from hiera data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;betadots::local_resolve&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$local_api_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api.int.domain.tld'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Puppet provides a stable API for creating custom functions.&lt;br&gt;
Before creating a new function, check whether a solutions already exists in &lt;a href="https://github.com/puppetlabs/puppetlabs-stdlib" rel="noopener noreferrer"&gt;Stdlib-&lt;/a&gt; or &lt;a href="https://github.com/voxpupuli/puppet-extlib" rel="noopener noreferrer"&gt;Extlib-Module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Remember to prefix your functions with your module name to avoid conflicts and help identify their origin.&lt;/p&gt;

&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Die Ruby-Seite von Puppet - Teil 2 - Benutzerdefinierte Funktionen</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Tue, 14 Jan 2025 16:01:03 +0000</pubDate>
      <link>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3</link>
      <guid>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3</guid>
      <description>&lt;p&gt;Dies ist der zweite Beitrag einer dreiteiligen Serie, in der die Konzepte und Best Practices zum Erweitern von &lt;a href="https://www.puppet.com" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt; mithilfe von &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;benutzerdefinierten Facts&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_functions_ruby" rel="noopener noreferrer"&gt;benutzerdefinierten Funktionen&lt;/a&gt; und &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types" rel="noopener noreferrer"&gt;benutzerdefinierten Typen&lt;/a&gt; und &lt;a href="https://www.puppet.com/docs/puppet/latest/provider_development" rel="noopener noreferrer"&gt;Providern&lt;/a&gt; behandelt werden.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj"&gt;Teil 1 behandelt Benutzerdefinierte Facts&lt;/a&gt;, die erklären, wie ein Knoten Informationen an den Puppet-Server übermitteln kann.&lt;/p&gt;

&lt;p&gt;Teil 2 (dieser Beitrag) konzentriert sich auf Benutzerdefinierte Funktionen und zeigt, wie man Funktionen für Datenverarbeitung und Ausführung entwickelt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp"&gt;Teil 3&lt;/a&gt; stellt dar, wie man die DSL-Funktionalität von Puppet mit neuen Typen und Provider erweitert.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Benutzerdefinierte Funktionen in Puppet&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Funktionen in Puppet werden auf dem Puppet-Server, insbesondere im Compiler, ausgeführt. Diese Funktionen haben Zugriff auf alle Facts und Puppet-Variablen, solange sie mit dem Namensraum angegeben werden.&lt;/p&gt;

&lt;p&gt;Es gibt zwei Haupttypen von Funktionen in Puppet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anweisungsfunktionen&lt;/li&gt;
&lt;li&gt;Rückgabefunktionen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Anweisungsfunktionen&lt;/strong&gt; können mit den internen Mechanismen von Puppet interagieren. Die gebräuchlichsten Anweisungsfunktionen sind &lt;code&gt;include&lt;/code&gt; und &lt;code&gt;contain&lt;/code&gt;, die Klassen zum Katalog hinzufügen, oder &lt;code&gt;noop&lt;/code&gt; und &lt;code&gt;notice&lt;/code&gt;, die alle Ressourcen in den Simulationsmodus versetzen oder Einträge in die Protokolldatei des Puppet-Servers schreiben.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rückgabefunktionen&lt;/strong&gt; wie &lt;code&gt;lookup&lt;/code&gt; oder &lt;code&gt;read_url&lt;/code&gt; geben Werte aus Hiera oder externen Quellen wie Webservern zurück. Einige Rückgabefunktionen können auch Daten manipulieren (&lt;code&gt;each&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warum benutzerdefinierte Funktionen erstellen?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hier sind einige Gründe, warum man benutzerdefinierte Funktionen in Puppet entwickeln könnte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Erstellen einer benutzerdefinierten Hiera-Suchfunktion&lt;/li&gt;
&lt;li&gt;Umwandeln von Datenstrukturen für spezifische Anforderungen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inhalt&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Funktion-Entwicklung&lt;/li&gt;
&lt;li&gt;Funktionen in Modulen&lt;/li&gt;
&lt;li&gt;Allgemeine API&lt;/li&gt;
&lt;li&gt;Dispatcher und Definition&lt;/li&gt;
&lt;li&gt;Verwendung von Funktionen in der Puppet-DSL&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Funktion-Entwicklung
&lt;/h2&gt;

&lt;p&gt;Da Funktionen nur zur Kompilierzeit ausgeführt werden, empfiehlt es sich, sich zunächst auf ihre Kernfunktionalität zu konzentrieren, bevor sie in die Funktions-API integriert werden. Dadurch kann die Logik mit lokalen Daten getestet werden, ohne dass Probleme auf dem Puppet-Server auftreten, die die Kompilierung unterbrechen oder den Server sogar zum Absturz bringen könnten.&lt;/p&gt;

&lt;p&gt;Als Beispiel wird eine Datenstruktur definiert, die verschiedene Aspekte der Konfiguration eines Systems darstellt. Mit einer Funktion kann man bestimmte Teile dieser Datenstruktur als Variablen verfügbar machen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel für eine Datenstruktur&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server_hash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;postfix'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;transport'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mta'&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssl'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="s"&gt;'apache'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vhost'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;server.domain.tld'&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;document_root'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/srv/www/html/server.domain.tld'&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssl'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="s"&gt;'proxy_pass'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8080'&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tomcat'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;crm'&lt;/span&gt;
      &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;frontend'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Angenommen, diese Datenstruktur stammt aus Hiera, und man möchte anderen Teams, die ihre eigenen Module schreiben, die Wiederverwendung dieser Daten ohne zusätzliche Abfragen ermöglichen. Dadurch kann man die interne Datenstruktur ändern und die Funktion anpassen, während die Teams weiterhin die benötigten Informationen zur Verfügung gestellt bekommen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel für eine Funktion&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Test Daten&lt;/span&gt;
&lt;span class="n"&gt;server_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'postfix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'config'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'transport'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'mta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'ssl'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s1"&gt;'apache'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'config'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'vhost'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'server.domain.tld'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'document_root'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/srv/www/html/server.domain.tld'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'ssl'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'proxy_pass'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:8080'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s1"&gt;'tomcat'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'config'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'application'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'crm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'service'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'frontend'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# Funktion definition&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_service_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# Testen der Funktion&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;read_service_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'tomcat'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Funktionen in Modulen
&lt;/h2&gt;

&lt;p&gt;Puppet stellt eine API zum Erstellen benutzerdefinierter Funktionen zur Verfügung. Damit der Puppet Compiler diese Funktionen finden kann, müssen sie in bestimmten Verzeichnissen innerhalb eines Moduls abgelegt werden.&lt;/p&gt;

&lt;p&gt;Dateien im &lt;code&gt;lib&lt;/code&gt;-Verzeichnis des Moduls werden auf alle Agenten synchronisiert. Es ist nicht möglich, zu steuern, welche Dateien synchronisiert werden.&lt;/p&gt;

&lt;p&gt;Benutzerdefinierte Funktionen können zu einer der beiden folgenden Verzeichnisstrukturen hinzugefügt werden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lib/puppet/parser/functions&lt;/code&gt; - Funktionen API v1&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/puppet/functions&lt;/code&gt; - Funktionen API v2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Die neuere Funktionen-API v2 unterstützt Unterverzeichnisse, sodass man Funktionen in Namespaces organisieren können. Der Dateiname sollte mit dem Funktionsnamen übereinstimmen.&lt;/p&gt;

&lt;p&gt;In diesem Beitrag konzentrieren wir uns auf die moderne API v2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel für eine Verzeichnisstruktur&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# &amp;lt;modulepath&amp;gt;/stdlib/&lt;/span&gt;
  &lt;span class="se"&gt;\-&lt;/span&gt; lib/
    &lt;span class="se"&gt;\-&lt;/span&gt; puppet/
      &lt;span class="se"&gt;\-&lt;/span&gt; functions/
        &lt;span class="se"&gt;\-&lt;/span&gt; stdlib/
          &lt;span class="se"&gt;\-&lt;/span&gt; to_yaml.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# stdlib/lib/puppet/functions/stdlib/to_yaml.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'stdlib::to_yaml'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wir empfehlen, benutzerdefinierte Funktionen mit dem Modulnamen zu präfixen, um Namenskonflikte zu vermeiden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Allgemeine API
&lt;/h2&gt;

&lt;p&gt;Um benutzerdefinierte Funktionen in Puppet zu erstellen, verwendet man die folgende Struktur:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bitte beachten, dass der Funktions Namespace identisch zum Modulnamen (betadots) sein &lt;strong&gt;muss&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dispatcher und Definition
&lt;/h2&gt;

&lt;p&gt;Innerhalb des &lt;code&gt;do ... end&lt;/code&gt; Blocks der Funktion muss ein Dispatcher und eine Definition hinzugefügt werden. Der Dispatcher validiert die Eingabedaten und verknüpft sie mit einer benannten Definition, die den entsprechenden Ruby-Code ausführt.&lt;/p&gt;

&lt;p&gt;Der Dispatcher kann Datentypen mithilfe von &lt;code&gt;param&lt;/code&gt; prüfen und Werte Ruby-Symbolen zuordnen. Die Definition verwendet den Namen des Dispatchers und die im Dispatcher bereitgestellten Parameter.&lt;/p&gt;

&lt;p&gt;Der letzte Befehl in der Definition bestimmt den Rückgabewert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel für einen Dispatcher für eine IPv4-Adresse&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:ip&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ipaddr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hier ist eine Tabelle mit gängigen Methoden für Parameter in Dispatchern:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Methode&lt;/th&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;param&lt;/code&gt; oder &lt;code&gt;required_param&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ein obligatorisches Argument. Kann mehrfach verwendet werden. &lt;strong&gt;Position&lt;/strong&gt;: Alle Pflichtargumente müssen zuerst erscheinen.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optional_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ein Argument, das ausgelassen werden kann.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;repeated_params&lt;/code&gt; oder &lt;code&gt;optional_repeated_params&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ein wiederholbares Argument, das null oder mehr Werte annehmen kann.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required_repeated_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ein wiederholbares Argument, das einen oder mehr Werte annehmen muss.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;block_param&lt;/code&gt; oder &lt;code&gt;required_block_param&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ein obligatorisches Lambda.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optional_block_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ein optionales Lambda.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Hinzufügen einer Definition&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;th&gt;Dispatcher&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dispatch :ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def ip&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;param ..., :ipaddr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def ip(ipaddr)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'resolv'&lt;/span&gt;
  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:ip&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ipaddr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Resolv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getname&lt;/span&gt; &lt;span class="n"&gt;ipaddr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nun können zusätzliche Dispatcher und Definitionen hinzugefügt werden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;
  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:ip&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ipaddr&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:dnsname&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[/.*/]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:dnsname&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="ss"&gt;:local_hostname&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s1"&gt;'Regexp[//]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:local_hostname&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Addrinfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'80'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getnameinfo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dnsname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dnsname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Addrinfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dnsname&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getnameinfo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;local_hostname&lt;/span&gt;
    &lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gethostname&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verwendung von Funktionen in der Puppet-DSL
&lt;/h2&gt;

&lt;p&gt;Innerhalb von Puppet werden Funktionen in der Regel während der Kompilierzeit ausgeführt.&lt;br&gt;
Eine besondere Ausnahme bildet die &lt;a href="https://www.puppet.com/docs/puppet/8/deferring_functions.html" rel="noopener noreferrer"&gt;deferred function&lt;/a&gt;, die auf dem Agenten ausgeführt wird.&lt;/p&gt;

&lt;p&gt;Im folgenden betrachten wir die zwei Anwendungsfälle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verwendung der &lt;code&gt;resolve&lt;/code&gt;-Funktion im Compiler&lt;/li&gt;
&lt;li&gt;Verwendung der &lt;code&gt;resolve&lt;/code&gt;-Funktion auf dem Agenten&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Anwendungsfall 1: Ausführung im Compiler
&lt;/h3&gt;

&lt;p&gt;Die Funktion kann an beliebiger Stelle im Code verwendet werden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;betadots::resolver&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Stdlib&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Fqdn&lt;/span&gt; &lt;span class="nv"&gt;$local_hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;betadots::resolve&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$ip_from_google&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;betadots::resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'www.google.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Anwendungsfall 2: Ausführung auf dem Agenten
&lt;/h3&gt;

&lt;p&gt;Die Funktion muss in deferred mode deklariert werden, um in Puppet auf dem Agenten ausgeführt zu werden. Die Funktion sollte nicht als Parameter im Header der Klasse verwendet werden, da dies möglicherweise den Funktionsaufruf mit hiera-Daten überschreiben könnte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;betadots::local_resolve&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$local_api_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Deferred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'betadots::resolve'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api.int.domain.tld'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;Puppet bietet eine stabile API zum Erstellen benutzerdefinierter Funktionen.&lt;br&gt;
Bevor eine neue Funktion erstellt wird, bitte prüfen, ob bereits eine Lösung im &lt;a href="https://github.com/puppetlabs/puppetlabs-stdlib" rel="noopener noreferrer"&gt;Stdlib-&lt;/a&gt; oder &lt;a href="https://github.com/voxpupuli/puppet-extlib" rel="noopener noreferrer"&gt;Extlib-Modul&lt;/a&gt; vorhanden ist.&lt;/p&gt;

&lt;p&gt;Funktionen sollten mit dem Modulnamen zu präfixen, um Konflikte zu vermeiden und deren Herkunft zu kennzeichnen.&lt;/p&gt;

&lt;p&gt;Viel Erfolg beim Puppetisieren,&lt;br&gt;
Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Die Ruby-Seite von Puppet - Teil 1 - Benutzerdefinierte Fakten</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Wed, 01 Jan 2025 17:26:54 +0000</pubDate>
      <link>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj</link>
      <guid>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj</guid>
      <description>&lt;p&gt;Dies ist der erste von drei Beiträgen, die die Konzepte und Best Practices zur Erweiterung von &lt;a href="https://www.puppet.com" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt; mit Hilfe von &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;benutzerdefinierten Fakten&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/8/custom_functions_ruby" rel="noopener noreferrer"&gt;benutzerdefinierten Funktionen&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types" rel="noopener noreferrer"&gt;benutzerdefinierten Typen&lt;/a&gt; und &lt;a href="https://www.puppet.com/docs/puppet/latest/provider_development" rel="noopener noreferrer"&gt;Providern&lt;/a&gt; behandeln.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teil 1&lt;/strong&gt; (dieser Beitrag) erklärt benutzerdefinierte Fakten und wie ein Puppet Agent dem Puppet-Server Informationen bereitstellen kann.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3"&gt;&lt;strong&gt;Teil 2&lt;/strong&gt;&lt;/a&gt; konzentriert sich auf benutzerdefinierte Funktione und erläutert detailliert, wie Datenverarbeitungs- oder Ausführungsfunktionen entwickelt werden können.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp"&gt;&lt;strong&gt;Teil 3&lt;/strong&gt;&lt;/a&gt; deckt benutzerdefinierte Typen und Provider ab, die die Funktionalität der Puppet-DSL erweitern.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Benutzerdefinierte Fakten&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Es gibt mehrere Gründe, benutzerdefinierte Fakten zu entwickeln:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wann immer ein VM-Namensschema verwendet wird, empfehlen wir, das Namensschema als benutzerdefinierten Fakt bereitzustellen.&lt;/li&gt;
&lt;li&gt;Es ist manchmal erforderlich, den Zustand bestimmter Konfigurationen zu bestimmen oder ob bestimmte Software installiert ist.&lt;/li&gt;
&lt;li&gt;Die Rechenzentrumsabteilung benötigt möglicherweise einen Überblick über die physische Hardware, Details zu den Hardwareanbietern und End-of-Life (EOL)-Supportdaten. Darüber hinaus kann die Finanzabteilung Informationen über die Anzahl der verwendeten kommerziellen Lizenzen und die Versionen der installierten Software benötigen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wann immer Informationen von einem Puppet Agenten benötigt werden, bietet Puppet die Möglichkeit, benutzerdefinierte Fakten zu implementieren.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inhalt&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Faktenerstellung&lt;/li&gt;
&lt;li&gt;Fakten in Modulen&lt;/li&gt;
&lt;li&gt;Allgemeine API&lt;/li&gt;
&lt;li&gt;Einschränkungen&lt;/li&gt;
&lt;li&gt;Hilfsfunktionen&lt;/li&gt;
&lt;li&gt;Zugriff auf andere Fakten&lt;/li&gt;
&lt;li&gt;Rückgabewerte&lt;/li&gt;
&lt;li&gt;Windows-Fakten&lt;/li&gt;
&lt;li&gt;Weitere Konzepte&lt;/li&gt;
&lt;li&gt;Codierungsstrategien und strukturierte Daten&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Faktenerstellung
&lt;/h2&gt;

&lt;p&gt;Benutzerdefinierte Fakten können lokal auf einem Arbeitsplatz oder auf dem System entwickelt werden, auf dem der Fakt benötigt wird. Bevor der ungetestete Fakt in den Puppet-Code aufgenommen wird, kann der Faktladepfad auf zwei Arten angegeben und unabhängig gestestet werden.&lt;/p&gt;

&lt;p&gt;Die folgende Verzeichnisstruktur wird verwendet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; ~/new_fact/
betadots_application_version.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Um den Fakt auszuführen, verwenden man einen der folgenden Befehle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;FACTERLIB=~/new_fact facter betadots_application_version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;facter --custom-dir ~/new_fact betadots_application_version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fakten in Modulen
&lt;/h2&gt;

&lt;p&gt;Puppet stellt eine API zur Entwicklung benutzerdefinierter Fakten bereit. Um sicherzustellen, dass der Puppet-Agent sie findet, müssen benutzerdefinierte Fakten in einem bestimmten Verzeichnis innerhalb eines Moduls abgelegt werden.&lt;/p&gt;

&lt;p&gt;Alle Dateien im &lt;code&gt;lib&lt;/code&gt;-Verzeichnis von Modulen werden auf alle Agenten synchronisiert. Es ist nicht möglich, einzuschränken, welche Dateien synchronisiert werden.&lt;/p&gt;

&lt;p&gt;Benutzerdefinierte Fakten sollten im Verzeichnis &lt;code&gt;lib/facter&lt;/code&gt; eines Moduls abgelegt werden. Obwohl man einen beliebigen Dateinamen wählen kann, wird empfohlen, den Namen des Fakts als Dateinamen zu verwenden, um eine einfache Identifikation zu gewährleisten. Der Dateiname muss mit &lt;code&gt;.rb&lt;/code&gt; enden.&lt;/p&gt;

&lt;p&gt;Wir empfehlen außerdem, dem Namen des benutzerdefinierten Fakts einen Firmen- oder Abteilungspräfix hinzuzufügen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Allgemeine API
&lt;/h2&gt;

&lt;p&gt;Facter bietet eine API zum Erstellen neuer benutzerdefinierter Fakten.&lt;/p&gt;

&lt;p&gt;Beispiel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Code hier&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Der Block &lt;code&gt;setcode do ... end&lt;/code&gt; enthält den gesamten Ruby-Code für den Fakt. Das Ergebnis des letzten Befehls oder der letzten Variablen wird als Rückgabewert des Fakts verwendet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Einschränkungen
&lt;/h2&gt;

&lt;p&gt;Da alle benutzerdefinierten Fakten an alle Puppet-Agenten verteilt werden, ist es wichtig sicherzustellen, dass betriebssystem- oder anwendungsspezifische Fakten nur dort ausgeführt werden, wo es erforderlich ist.&lt;/p&gt;

&lt;p&gt;Dies wird durch Einschränkungen (confining) erreicht.&lt;/p&gt;

&lt;p&gt;Einschränkungen können auf einfache oder strukturierte Fakten oder auf anderen Ruby-Code wie &lt;code&gt;File.exist?&lt;/code&gt; angewendet werden.&lt;/p&gt;

&lt;p&gt;Einfache Fakt-Einschränkung:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="ss"&gt;kernel: &lt;/span&gt;&lt;span class="s1"&gt;'Linux'&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Code hier&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Einschränkung unter Verwendung eines Ruby-Blocks und Fakten:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="s1"&gt;'os'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'family'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'RedHat'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Code hier&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Einschränkung unter Verwendung eines Ruby-Blocks und Ruby-Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Code hier&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hilfsfunktionen
&lt;/h2&gt;

&lt;p&gt;Facter bietet mehrere Hilfsmethoden, die verwendet werden können, ohne dass die Klasse &lt;code&gt;facter&lt;/code&gt; erforderlich ist:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hilfsfunktion&lt;/th&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter::Core::Execution::execute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Führt ein ausführbares Programm auf dem System mit einer Timeout-Option aus, um das Aufhängen von Facter-Läufen zu verhindern.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter::Core::Execution::which&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prüft, ob ein ausführbares Programm verfügbar ist. Funktioniert auf jedem unterstützten Betriebssystem und durchsucht die Standardpfade &lt;code&gt;ENV['PATH'] + ['/sbin', '/usr/sbin']&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter.value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bietet Zugriff auf den Wert eines anderen Fakts. Achtung! Loops vermeiden!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Weitere Informationen findet man in der &lt;a href="https://www.rubydoc.info/gems/facter" rel="noopener noreferrer"&gt;API-Dokumentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Bitte beachten, dass &lt;code&gt;Facter::Core::Execution::exec&lt;/code&gt; zugunsten von &lt;code&gt;Facter::Core::Execution::execute&lt;/code&gt; veraltet ist. Dies ist wichtig, wenn man von älteren Versionen von Facter migriert.&lt;/p&gt;

&lt;p&gt;Die &lt;code&gt;timeout&lt;/code&gt;-Options-Hash kann auf zwei Arten festgelegt werden:&lt;/p&gt;

&lt;p&gt;Gültig für den gesamten Fakt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pro Ausführungsmethode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zum Beispiel, wenn eine Anwendung ihre Konfiguration über &lt;code&gt;/opt/app/bin/app config&lt;/code&gt; zurückgibt, kann man ein Timeout von 5 Sekunden festlegen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das Ergebnis des letzten Befehls oder die Angabe einer Variable wird als Faktenergebnis verwendet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;config_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;config_list&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Zugriff auf andere Fakten
&lt;/h2&gt;

&lt;p&gt;Es ist möglich, dass ein benutzerdefinierter Fakt den Wert eines anderen Fakts verwendet, indem er &lt;code&gt;Facter.value&lt;/code&gt; verwendet. Dies sollte jedoch vorsichtig geschehen, um zyklische Abhängigkeiten zwischen Fakten zu vermeiden.&lt;/p&gt;

&lt;p&gt;In unserem Beispiel hat die Anwendung unterschiedliche Konfigurationspfade für verschiedene Betriebssysteme.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RedHat - /etc/sysconfig/application&lt;/li&gt;
&lt;li&gt;Debian - /etc/default/application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beispiel mit Zugriff auf einen anderen Fakt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;app_cfg_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'os'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'family'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'RedHat'&lt;/span&gt;
                     &lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;
                   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'Debian'&lt;/span&gt;
                     &lt;span class="s1"&gt;'/etc/default/application'&lt;/span&gt;
                   &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_cfg_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das Beispiel ruft den &lt;code&gt;os&lt;/code&gt;-Fakt ab und verwendet den &lt;code&gt;family&lt;/code&gt;-Schlüssel, um das Betriebssystem zu vergleichen, und wendet dann eine Logik an, um zu bestimmen, welchen Anwendungskonfigurationspfad geprüft werden soll.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rückgabewerte
&lt;/h2&gt;

&lt;p&gt;Puppet erwartet, dass benutzerdefinierte Fakten einen gültigen Werttyp zurückgeben, insbesondere Strings, Ganze Zahlen oder Arrays.&lt;br&gt;
Wenn der Wert eines Fakts &lt;code&gt;nil&lt;/code&gt; oder &lt;code&gt;undef&lt;/code&gt; ist, wird der Fakt einfach nicht definiert.&lt;/p&gt;
&lt;h2&gt;
  
  
  Windows-Fakten
&lt;/h2&gt;

&lt;p&gt;Facter ist eine plattformübergreifende API und stellt die gleichen Funktionen auf allen Betriebssystemen bereit. Es ist jedoch wichtig zu beachten, dass Pfade und ausführbare Dateien in einer Windows-Umgebung unterschiedlich sein können.&lt;/p&gt;

&lt;p&gt;Mit Hilfe des &lt;code&gt;Facter::Core::Execution.execute&lt;/code&gt; werden üblicherweise Powershell Kommandos gestartet.&lt;/p&gt;

&lt;p&gt;Das folgende Beispiel erstellt einen Windows-Fakt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/windows_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:windows_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="ss"&gt;osfamily: &lt;/span&gt;&lt;span class="s1"&gt;'windows'&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'reg query "HKLM\Software\Microsoft\Internet Explorer" /v svcVersion'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das Beispiel verwendet den &lt;code&gt;reg query&lt;/code&gt;-Befehl, um die Version des Internet Explorers zu erhalten.&lt;/p&gt;

&lt;p&gt;Bitte beachten, dass die &lt;a href="https://docs.microsoft.com/en-us/windows/win32/api/" rel="noopener noreferrer"&gt;Win32 API calls&lt;/a&gt; seit Ruby 1.9 deprecated sind.&lt;br&gt;
Man kann statt dessen die &lt;a href="https://github.com/ruby/fiddle" rel="noopener noreferrer"&gt;Fiddle&lt;/a&gt; oder andere Ruby Bibliotheken nutzen.&lt;/p&gt;
&lt;h2&gt;
  
  
  Weitere Konzepte
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Protokollierung
&lt;/h3&gt;

&lt;p&gt;Zur Analyse eines Facts kann man auf die &lt;code&gt;debug&lt;/code&gt; Methode zugreifen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Custom fact 'betadots_application_version' running"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es sind folgende Log Level möglich: &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; und &lt;code&gt;fatal&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggregierung
&lt;/h3&gt;

&lt;p&gt;Eine weitere Option ist das Zusammenstellen von Facts als Hash mit Hilfe von &lt;code&gt;aggregates&lt;/code&gt; und &lt;code&gt;chunks&lt;/code&gt;.&lt;br&gt;
Jeder &lt;code&gt;chunk&lt;/code&gt;benötigt einen Namen (key) und einen Wert (value), wobei der Wert entweder ein Array oder ein Hash sein &lt;strong&gt;muss&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Nachdem Facter alle chunks eingelesen und validiert hat, werden diese aggregiert. Als default werden die Arrays oder Hashes gemerged.&lt;br&gt;
Es besteht die Möglichkeit, eine eigene Aggregierungs Funktion zu schreiben.&lt;/p&gt;

&lt;p&gt;Wir gehen davon aus, dass unsere Beispielanwendung es uns ermöglicht, Einstellungen auszulesen: &lt;code&gt;/opt/app/bin/app config &amp;lt;key&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;app_cfg_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'os'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'family'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'RedHat'&lt;/span&gt;
                     &lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;
                   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'Debian'&lt;/span&gt;
                     &lt;span class="s1"&gt;'/etc/default/application'&lt;/span&gt;
                   &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_cfg_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:config_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:version&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config version'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:config_cache_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:cache_size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config cache_size'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:config_log_level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:log_level&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config log_level'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ergebnis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;betadots_application_version =&amp;gt; {
  version =&amp;gt; '1.2.4',
  cache_size =&amp;gt; '400M',
  log_level =&amp;gt; 'info',
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gewichtung
&lt;/h3&gt;

&lt;p&gt;Facter ermöglicht es, den Rückgabe Wert auf unterschiedliche Arten zu ermitteln. Die einzelnen Arten bekommen eine Gewichtung, damit Facter danach entscheiden kann, welche Art (Methode) vals Rückgabewert verwendet werden soll.&lt;/p&gt;

&lt;p&gt;Wir gehen davon aus, dass unsere Besipielanwendung entweder über systemd oder über eine andere Art gestartet wurde (PID Datei).&lt;/p&gt;

&lt;p&gt;Hinweis: Externe Fakten haben eine Gewichtung von 1000. Man kann externe Fakten überschreiben, indem man für den Custom Fakt den gleichen Namen verwendet und eine Gewichtung über 1000 hinterlegt.&lt;/p&gt;

&lt;p&gt;Weitere Informationen findet man in der &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts#configuring_facts-fact-precedence" rel="noopener noreferrer"&gt;Fact precedence&lt;/a&gt; Sektion der &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;Custom facts overview&lt;/a&gt; Seite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'application_running'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;has_weight&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'systemctl status app'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'application_running'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;has_weight&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/var/run/application.pid'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blocking und Caching
&lt;/h3&gt;

&lt;p&gt;Normalerweise werden Facts bei jedem Lauf eines Puppet Agenten erneut ermittelt. Dies kann bei manchen Facts dazu führen, dass Facter mehr Zeit benötigt, bis alle Facts geladen sind. Dies gilt insbesondere, wenn Facts eine Anwendung abfragen und diese Abfrage eine hohe Last erzeugt und lange dauert oder es gibt "teure" Facts, die gar nicht benötigt werden.&lt;br&gt;
Facter ermöglicht es, einen Cache zu verwenden, so dass Facts nur einmalig und danch erst nach der Lebensdauer des Cache Objektes erneut abgefragt werden und man kann Blacklisten mit unerwünschen Facts pflegen.&lt;/p&gt;

&lt;p&gt;Die Facter Konfiguration erfolgt in &lt;code&gt;/etc/puppetlabs/facter/facter.conf&lt;/code&gt; auf *nix Systemen oder in &lt;code&gt;C:\ProgramData\PuppetLabs\facter\etc\facter.conf&lt;/code&gt; auf Windows.&lt;br&gt;
Bitte beachten, dass die Konfiguration auf dem Puppet Agent erfolgen muss!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/puppetlabs/facter/facter.conf
facts : {
  blocklist : [ "file system", "EC2", "processors.isa" ]
  ttls : [
    { "timezone": 30 days },
    { "my-fact-group": 30 days },
  ]
}
fact-groups : {
  my-fact-group : [ "os", "ssh.version"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Innerhalb des Elementes &lt;code&gt;blocklist&lt;/code&gt; kann man entweder einen Fact, ein Subelement eines Fact Hashes oder eine Facter Gruppe angeben.&lt;br&gt;
Existierende Facter Gruppen kann man aus Facter auslesen:&lt;/p&gt;
&lt;h4&gt;
  
  
  Facter block groups
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;facter &lt;span class="nt"&gt;--list-block-groups&lt;/span&gt;
EC2
  - ec2_metadata
  - ec2_userdata
file system
  - mountpoints
  - filesystems
  - partitions
hypervisors
  - hypervisors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Facter cache groups
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;facter &lt;span class="nt"&gt;--list-cache-groups&lt;/span&gt;
EC2
  - ec2_metadata
  - ec2_userdata
GCE
  - gce
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Außerdem kann man eigene Facter Gruppen innerhalb der &lt;code&gt;facts-group&lt;/code&gt; Sektion hinterlegen.&lt;/p&gt;

&lt;p&gt;Bitte beachten, dass das &lt;a href="https://forge.puppet.com/modules/dylanratcliffe/facter_cache/readme" rel="noopener noreferrer"&gt;Dylan Ratcliffe's facter_cache Module&lt;/a&gt; seit Puppet 6 nicht mehr notwendig ist.&lt;/p&gt;

&lt;p&gt;Mehr Informationen findet man auf der  Puppet Webseite im Bereich &lt;a href="https://www.puppet.com/docs/puppet/latest/configuring_facter" rel="noopener noreferrer"&gt;&lt;code&gt;facter.conf&lt;/code&gt; settings&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Codierungsstrategien und strukturierte Daten
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Anzahl dr Fakten redizieren&lt;/li&gt;
&lt;li&gt;Hashes verwenden&lt;/li&gt;
&lt;li&gt;Generischer Code und Ruby Module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wenn ein Fakt komplexe Daten zurückgeben muss, sollten strukturierte Daten verwendet werden, um hierarchische Informationen bereitzustellen. Weitere Informationen zur Strukturierung von Daten finden Sie in der Puppet-Dokumentation zu &lt;a href="https://www.puppet.com/docs/puppet/8/custom_facts.html#structured_data_in_custom_facts" rel="noopener noreferrer"&gt;strukturierten Daten in benutzerdefinierten Fakten&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eine bewährte Praxis besteht darin, sich für eine Struktur zu entscheiden, wenn ein Fakt mehr als eine einzelne Zeichenfolge zurückgeben soll. Solche Daten werden in der Regel in Hashes oder Arrays organisiert, die wahlweise direkt einen Hash liefern oder über aggregates zusammengesetzt werden:&lt;/p&gt;

&lt;p&gt;Lösung 1: Hash in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_file_check&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;file_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:kernel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'windows'&lt;/span&gt;
                  &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'c:/Program Data/Application/bin/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'c:/backup/state.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'Linux'&lt;/span&gt;
                  &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'/opt/app/bin/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'/etc/backup/state.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;file_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;:size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="ss"&gt;:world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;world_writable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Facter Ergebnis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'/opt/app/bin/app'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;64328&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s1"&gt;'/etc/backup/state.txt'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;438&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lösung 2: Hashes mit Hilfe von aggregate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_file_check&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;file_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:kernel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'windows'&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="s1"&gt;'c:/Program Data/Application/bin/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'c:/backup/state.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'Linux'&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="s1"&gt;'/opt/app/bin/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'/etc/backup/state.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;file_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file_world_writable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;file_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;world_writable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Facter Ergebnis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'/opt/app/bin/app'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;64328&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s1"&gt;'/etc/backup/state.txt'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;438&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ein weiteres Facter Besipiel liest das Agent Zertifikat aus und liefert die Zertifikatserweiterungen als Fact Hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_cert_extension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'openssl'&lt;/span&gt;
    &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet'&lt;/span&gt;
    &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/ssl/oids'&lt;/span&gt;

    &lt;span class="c1"&gt;# set variables&lt;/span&gt;
    &lt;span class="n"&gt;extension_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;certdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:certdir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;certname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:certname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;certificate_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;certdir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;certname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.pem"&lt;/span&gt;

    &lt;span class="c1"&gt;# get puppet ssl oids&lt;/span&gt;
    &lt;span class="n"&gt;oids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Oids&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PUPPET_OIDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;oids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# read the certificate&lt;/span&gt;
    &lt;span class="n"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;X509&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt; &lt;span class="n"&gt;certificate_file&lt;/span&gt;

    &lt;span class="c1"&gt;# cert extensions differs if we run via agent (numeric) or via facter (names)&lt;/span&gt;
    &lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;%r{^1&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;3&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;6&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;4&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;34380&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;1}&lt;/span&gt;
        &lt;span class="n"&gt;short_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;extension_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;short_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;short_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'pp_preshared_key'&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;%r{^pp_}&lt;/span&gt;
        &lt;span class="n"&gt;short_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;oid&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;extension_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;short_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;short_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'pp_preshared_key'&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;extension_hash&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;Dieser Beitrag hat die Grundlagen zur Erstellung von benutzerdefinierten Fakten in Puppet beschrieben. Benutzerdefinierte Fakten sind ein leistungsfähiges Werkzeug, um einem Puppet-Server oder einem Management-Tool Informationen zu liefern. Facts werden in Modulen hinterlegt und sollten bei Bedarf eingeschränkt (confine) werden. Die Rückgabewerte sollten als Hash oder Array und nur in seltenen Fällen als Bool, String oder Integer ausgegeben werden.&lt;/p&gt;

&lt;p&gt;Ein letzter Hinweis: Nur lokale Kommandos ausführen! Wenn man Remote Systeme in Kombination mit Facter nutzen möchte, müssen diese Hochverfügbar und skaliert aufgebaut sein.&lt;/p&gt;

&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>ruby</category>
    </item>
    <item>
      <title>The Ruby side of Puppet - Part 1 - Custom Facts</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Wed, 01 Jan 2025 17:26:04 +0000</pubDate>
      <link>https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7</link>
      <guid>https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7</guid>
      <description>&lt;p&gt;This is the first in a series of three posts covering the concepts and best practices for extending &lt;a href="https://www.puppet.com" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt; using &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;custom facts&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/8/custom_functions_ruby" rel="noopener noreferrer"&gt;custom functions&lt;/a&gt; and &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types" rel="noopener noreferrer"&gt;custom types&lt;/a&gt; and &lt;a href="https://www.puppet.com/docs/puppet/latest/provider_development" rel="noopener noreferrer"&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1&lt;/strong&gt; (this post) explains custom facts and how a node can provide information to the Puppet Server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7"&gt;&lt;strong&gt;Part 2&lt;/strong&gt;&lt;/a&gt; focuses on custom functions, detailing how to develop data processing or execution functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma"&gt;&lt;strong&gt;Part 3&lt;/strong&gt;&lt;/a&gt; covers custom types and providers, extending Puppet DSL functionality.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Custom Facts&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;There are several reasons to develop custom facts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whenever a VM naming model is used, we recommend providing the naming schema as a custom fact.&lt;/li&gt;
&lt;li&gt;It is sometimes necessary to determine the state of certain configurations or whether specific software is installed.&lt;/li&gt;
&lt;li&gt;The datacenter department may need an overview of physical hardware, hardware vendor details, and end-of-life (EOL) support dates. Additionally, the finance department may require information on the number of commercial licenses in use and the versions of installed software.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever information from a node is needed, Puppet offers the option to deploy custom facts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fact Development&lt;/li&gt;
&lt;li&gt;Facts in Modules&lt;/li&gt;
&lt;li&gt;General API&lt;/li&gt;
&lt;li&gt;Confining&lt;/li&gt;
&lt;li&gt;Helpers&lt;/li&gt;
&lt;li&gt;Accessing Other Facts&lt;/li&gt;
&lt;li&gt;Return Values&lt;/li&gt;
&lt;li&gt;Windows Facts&lt;/li&gt;
&lt;li&gt;Additional Concepts&lt;/li&gt;
&lt;li&gt;Coding Strategies and Structured Data&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fact Development
&lt;/h2&gt;

&lt;p&gt;You can develop custom facts locally on a workstation or on the system where the fact is needed. Before adding the untested fact to your Puppet code, you can specify the fact load path in two ways.&lt;/p&gt;

&lt;p&gt;The following directory structure is used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; ~/new_fact/
betadots_application_version.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To execute the fact, use one of the following commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;FACTERLIB=~/new_fact facter betadots_application_version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;facter --custom-dir ~/new_fact betadots_application_version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Facts in Modules
&lt;/h2&gt;

&lt;p&gt;Puppet provides an API to develop custom facts. To ensure the Puppet agent finds them, custom facts must be placed in a specific directory within a module.&lt;/p&gt;

&lt;p&gt;All files in the &lt;code&gt;lib&lt;/code&gt; directory of modules are synchronized to all agents. It’s not possible to restrict which files are synchronized.&lt;/p&gt;

&lt;p&gt;Custom facts should be placed in the &lt;code&gt;lib/facter&lt;/code&gt; directory of a module. Although you can choose any filename, it's recommended to use the fact name as the filename for easy identification. The filename must end with &lt;code&gt;.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We also recommend prefixing the custom fact name with a company or department identifier.&lt;/p&gt;

&lt;h2&gt;
  
  
  General API
&lt;/h2&gt;

&lt;p&gt;Facter provides an API to create new custom facts.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Code here&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;setcode do ... end&lt;/code&gt; block contains all the Ruby code for the fact. The result of the last command or variable is used as the fact’s return value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confining
&lt;/h2&gt;

&lt;p&gt;Since all custom facts are distributed to all Puppet agents, it’s essential to ensure that OS- or application-specific facts are only executed where necessary.&lt;/p&gt;

&lt;p&gt;This is achieved by confining facts.&lt;/p&gt;

&lt;p&gt;Confinement can be applied to simple or structured facts or any other Ruby code, such as &lt;code&gt;File.exist?&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Simple fact confinement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="ss"&gt;kernel: &lt;/span&gt;&lt;span class="s1"&gt;'Linux'&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Code here&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confinement using Ruby block and facts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="s1"&gt;'os'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'family'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'RedHat'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Code here&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confinement using Ruby block and Ruby code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Code here&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Helpers
&lt;/h2&gt;

&lt;p&gt;Facter provides several helper methods that can be used without requiring the &lt;code&gt;facter&lt;/code&gt; class:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Helper&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter::Core::Execution::execute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs an executable on the system with a timeout option to prevent hanging facter runs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter::Core::Execution::which&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks if an executable is available. Works on any supported OS, searching the default paths &lt;code&gt;ENV['PATH'] + ['/sbin', '/usr/sbin']&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter.value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provides access to the value of any other fact. Be careful to avoid loops!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;More information is available in the &lt;a href="https://www.rubydoc.info/gems/facter" rel="noopener noreferrer"&gt;API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please note that &lt;code&gt;Facter::Core::Execution::exec&lt;/code&gt; has been deprecated in favor of &lt;code&gt;Facter::Core::Execution::execute&lt;/code&gt;. This is important when migrating from older versions of Facter.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;timeout&lt;/code&gt; options hash can be set in two ways:&lt;/p&gt;

&lt;p&gt;Valid for the whole fact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;name&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per execute method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if an application returns its configuration via &lt;code&gt;/opt/app/bin/app config&lt;/code&gt;, you can set a timeout of 5 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result of the last command or a variable with content is used as fact result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;config_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;config_list&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accessing Other Facts
&lt;/h2&gt;

&lt;p&gt;It’s possible for a custom fact to leverage the value of another fact by using &lt;code&gt;Facter.value&lt;/code&gt;.However, this should be done cautiously to avoid introducing cyclic dependencies between facts.&lt;/p&gt;

&lt;p&gt;In our example the application has to different config paths for different OS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RedHat - /etc/sysconfig/application&lt;/li&gt;
&lt;li&gt;Debian - /etc/default/application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example with access to another fact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;app_cfg_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'os'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'family'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'RedHat'&lt;/span&gt;
                     &lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;
                   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'Debian'&lt;/span&gt;
                     &lt;span class="s1"&gt;'/etc/default/application'&lt;/span&gt;
                   &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_cfg_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example retreives the &lt;code&gt;os&lt;/code&gt; fact and uses the &lt;code&gt;family&lt;/code&gt; key to compare the OS, then applies logic to determine which application config path to check for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Return values
&lt;/h2&gt;

&lt;p&gt;Puppet expects custom facts to return a valid value type, specifically strings, integers, booleans, or structured data (such as arrays and hashes). If the fact cannot determine a valid value, it should return &lt;code&gt;nil&lt;/code&gt; or &lt;code&gt;undef&lt;/code&gt; to indicate that no fact value is available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows Facts
&lt;/h2&gt;

&lt;p&gt;Windows-based systems support custom facts just like Linux or other Unix-like systems.&lt;br&gt;
The main difference is that many Windows-specific tasks (such as checking installed software or reading from the registry)may require platform-specific Ruby code.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;Facter::Core::Execution.execute&lt;/code&gt; usually powershell commands is used.&lt;/p&gt;

&lt;p&gt;Here's an example of a Windows custom fact that retrieves the version of Internet Explorer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_internet_explorer_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_internet_explorer_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="ss"&gt;kernel: &lt;/span&gt;&lt;span class="s1"&gt;'windows'&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'reg query "HKLM\Software\Microsoft\Internet Explorer" /v svcVersion'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we use the &lt;code&gt;reg query&lt;/code&gt;command to check the Internet Explorer version in the Windows registry.&lt;/p&gt;

&lt;p&gt;Please note that the &lt;a href="https://docs.microsoft.com/en-us/windows/win32/api/" rel="noopener noreferrer"&gt;Win32 API calls&lt;/a&gt; are deprecated since ruby 1.9.&lt;br&gt;
Please consider using &lt;a href="https://github.com/ruby/fiddle" rel="noopener noreferrer"&gt;Fiddle&lt;/a&gt; or other Ruby-based libraries for interacting with the system.&lt;/p&gt;
&lt;h2&gt;
  
  
  Additional Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Logging
&lt;/h3&gt;

&lt;p&gt;It's often useful to include logging within custom facts to help with troubleshooting and development. You can log messages using Puppet's built-in logging mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Custom fact 'betadots_application_version' running"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logging levels include &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, and &lt;code&gt;fatal&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggregates
&lt;/h3&gt;

&lt;p&gt;Another option to create structured facts is the usage of chunks.&lt;br&gt;
Each chunk must have a name and returns a data structure which must be of type array or hash.&lt;br&gt;
After reading all chunks, the chunks will be aggregated. The default is to merge arrays or hashes.&lt;/p&gt;

&lt;p&gt;It is possible to write a different aggregate definition.&lt;/p&gt;

&lt;p&gt;We assume that our app can directly read config keys: &lt;code&gt;/opt/app/bin/app config &amp;lt;key&amp;gt;&lt;/code&gt; returns the value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_application_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;confine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;app_cfg_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'os'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'family'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'RedHat'&lt;/span&gt;
                     &lt;span class="s1"&gt;'/etc/sysconfig/application'&lt;/span&gt;
                   &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'Debian'&lt;/span&gt;
                     &lt;span class="s1"&gt;'/etc/default/application'&lt;/span&gt;
                   &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_cfg_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:config_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:version&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config version'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:config_cache_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:cache_size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config cache_size'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:config_log_level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:log_level&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app config log_level'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;betadots_application_version =&amp;gt; {
  version =&amp;gt; '1.2.4',
  cache_size =&amp;gt; '400M',
  log_level =&amp;gt; 'info',
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Weight
&lt;/h3&gt;

&lt;p&gt;Facter offers the option to select a value based on priority of a fact.&lt;br&gt;
Let's assume that an application is either started form systemd or in any other way (so it has a pid file).&lt;/p&gt;

&lt;p&gt;Note: external facts have a built in weight value of 1000. Overriding external facts is possible by creating a fact with the same name and specifying a weight value over 1000.&lt;/p&gt;

&lt;p&gt;More details can be found in the &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts#configuring_facts-fact-precedence" rel="noopener noreferrer"&gt;Fact precedence&lt;/a&gt; section of the &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;Custom facts overview page&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'application_running'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;has_weight&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'systemctl status app'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'application_running'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;has_weight&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;

  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/var/run/application.pid'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blocking and Caching
&lt;/h3&gt;

&lt;p&gt;By default, facts are recalculated each time they are queried. In certain scenarios, this might be inefficient, especially for computationally expensive facts or facts that rarely change. Facter offers a caching mechanism that allows you to persist fact values between Puppet runs.&lt;/p&gt;

&lt;p&gt;Besides this some facts might be of no interest, but take long time to read.&lt;br&gt;
In this case Facters blocklist can be used.&lt;/p&gt;

&lt;p&gt;Configuration takes place in &lt;code&gt;/etc/puppetlabs/facter/facter.conf&lt;/code&gt; on *nix systems or &lt;code&gt;C:\ProgramData\PuppetLabs\facter\etc\facter.conf&lt;/code&gt; on Windows.&lt;br&gt;
Please note that this configuratoin must be done on the Puppet agents!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/puppetlabs/facter/facter.conf
facts : {
  blocklist : [ "file system", "EC2", "processors.isa" ]
  ttls : [
    { "timezone": 30 days },
    { "my-fact-group": 30 days },
  ]
}
fact-groups : {
  my-fact-group : [ "os", "ssh.version"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see: within the &lt;code&gt;blocklist&lt;/code&gt; we can mention the facts itself, a sub fact, or a fact group.&lt;br&gt;
Existing facter groups can be read on the command line:&lt;/p&gt;
&lt;h4&gt;
  
  
  Facter block groups
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;facter &lt;span class="nt"&gt;--list-block-groups&lt;/span&gt;
EC2
  - ec2_metadata
  - ec2_userdata
file system
  - mountpoints
  - filesystems
  - partitions
hypervisors
  - hypervisors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Facter cache groups
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;facter &lt;span class="nt"&gt;--list-cache-groups&lt;/span&gt;
EC2
  - ec2_metadata
  - ec2_userdata
GCE
  - gce
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Besides this we can add our own fact groups using the &lt;code&gt;facts-group&lt;/code&gt; setting.&lt;/p&gt;

&lt;p&gt;Please note that in the past people were using &lt;a href="https://forge.puppet.com/modules/dylanratcliffe/facter_cache/readme" rel="noopener noreferrer"&gt;Dylan Ratcliffe's facter_cache Module&lt;/a&gt;. This is no longer needed as of Puppet 6, when the facter.conf settings were introduced.&lt;/p&gt;

&lt;p&gt;The Puppet website provides more information on &lt;a href="https://www.puppet.com/docs/puppet/latest/configuring_facter" rel="noopener noreferrer"&gt;&lt;code&gt;facter.conf&lt;/code&gt; settings&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Coding Strategies and Structured Data
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Limit the total number of facts&lt;/li&gt;
&lt;li&gt;Use data hashes&lt;/li&gt;
&lt;li&gt;Generic code and Ruby modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In more advanced cases, facts can return structured data, such as arrays or hashes. Structured facts allow for more complex data to be passed back to the Puppet server.&lt;/p&gt;

&lt;p&gt;It is absolutely required to limit the total number of facts for a node. Main reason is the performance of PuppetDB.&lt;/p&gt;

&lt;p&gt;Hashes or arrays can be built directly in Ruby code or via Facter aggregates.&lt;/p&gt;

&lt;p&gt;e.g. We need the state of several files.&lt;/p&gt;

&lt;p&gt;Solution 1: Hash in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_file_check&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;file_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:kernel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'windows'&lt;/span&gt;
                  &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'c:/Program Data/Application/bin/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'c:/backup/state.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'Linux'&lt;/span&gt;
                  &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'/opt/app/bin/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'/etc/backup/state.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;file_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'size'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'world_writable'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;world_writable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sr"&gt;/opt/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;64328&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="sr"&gt;/etc/&lt;/span&gt;&lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;438&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Solution 2: Hashes using aggregate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_file_check&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:aggregate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;file_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:kernel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'windows'&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="s1"&gt;'c:/Program Data/Application/bin/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'c:/backup/state.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'Linux'&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="s1"&gt;'/opt/app/bin/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'/etc/backup/state.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;file_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file_world_writable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;file_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;world_writable?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sr"&gt;/opt/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;64328&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="sr"&gt;/etc/&lt;/span&gt;&lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;world_writable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;438&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example reads the Puppet agent certificate and provides the certificate extensions as a Facter hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:betadots_cert_extension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'openssl'&lt;/span&gt;
    &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet'&lt;/span&gt;
    &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/ssl/oids'&lt;/span&gt;

    &lt;span class="c1"&gt;# set variables&lt;/span&gt;
    &lt;span class="n"&gt;extension_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;certdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:certdir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;certname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:certname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;certificate_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;certdir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;certname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.pem"&lt;/span&gt;

    &lt;span class="c1"&gt;# get puppet ssl oids&lt;/span&gt;
    &lt;span class="n"&gt;oids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Oids&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PUPPET_OIDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;oids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# read the certificate&lt;/span&gt;
    &lt;span class="n"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;X509&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt; &lt;span class="n"&gt;certificate_file&lt;/span&gt;

    &lt;span class="c1"&gt;# cert extensions differs if we run via agent (numeric) or via facter (names)&lt;/span&gt;
    &lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;%r{^1&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;3&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;6&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;4&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;34380&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;1}&lt;/span&gt;
        &lt;span class="n"&gt;short_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;extension_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;short_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;short_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'pp_preshared_key'&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="sr"&gt;%r{^pp_}&lt;/span&gt;
        &lt;span class="n"&gt;short_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;oid&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;extension_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;short_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;short_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'pp_preshared_key'&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;extension_hash&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Custom facts are a powerful way to extend Puppet’s built-in facts with your organization’s specific information. Whether you’re retrieving application versions, monitoring system configuration, or providing custom details for a specific environment, custom facts help ensure that Puppet has the right data to apply the correct configuration.&lt;/p&gt;

&lt;p&gt;In this post, we’ve covered the basics of custom fact development, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to create custom facts in a local environment or as part of a module.&lt;/li&gt;
&lt;li&gt;The API for creating and confining facts.&lt;/li&gt;
&lt;li&gt;Accessing other facts and helper methods to enhance fact functionality.&lt;/li&gt;
&lt;li&gt;Returning structured data for complex requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One more advise: Run local commands only! If connections to remote systems are required you must ensure scalability and high availability.&lt;/p&gt;

&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>ruby</category>
      <category>devops</category>
      <category>cfgmgmt</category>
    </item>
    <item>
      <title>Puppet best practice</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Wed, 08 May 2024 08:10:51 +0000</pubDate>
      <link>https://dev.to/betadots/puppet-best-practice-3b9c</link>
      <guid>https://dev.to/betadots/puppet-best-practice-3b9c</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.betados.de"&gt;betadots&lt;/a&gt;, during our &lt;a href="https://puppet.com"&gt;Puppet&lt;/a&gt; code reviews, we often receive requests for a comprehensive summary of best practices and guidelines.&lt;br&gt;
In response, we've compiled this article to delve deep into Puppet's best practices and implementations.&lt;/p&gt;




&lt;p&gt;Table of content&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Control Repo

&lt;ul&gt;
&lt;li&gt;Test your control-repo!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Hiera&lt;/li&gt;
&lt;li&gt;Node Classification&lt;/li&gt;
&lt;li&gt;Library Module Usage&lt;/li&gt;
&lt;li&gt;Profiles: Doing Them right&lt;/li&gt;
&lt;li&gt;
Module Development

&lt;ul&gt;
&lt;li&gt;General Concepts and Structure&lt;/li&gt;
&lt;li&gt;Parameters and Variables&lt;/li&gt;
&lt;li&gt;Declaration and References&lt;/li&gt;
&lt;li&gt;Class and Resource Ordering&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;
Examples

&lt;ul&gt;
&lt;li&gt;environment.conf&lt;/li&gt;
&lt;li&gt;bin/config_script.sh&lt;/li&gt;
&lt;li&gt;manifests/site.pp&lt;/li&gt;
&lt;li&gt;Hiera Config&lt;/li&gt;
&lt;li&gt;Simple Hiera Data&lt;/li&gt;
&lt;li&gt;Module Classes&lt;/li&gt;
&lt;li&gt;Class Spec Tests&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Control Repo
&lt;/h2&gt;

&lt;p&gt;The control repo's layout is crucial. We scrutinize files such as &lt;code&gt;environment.conf&lt;/code&gt;, &lt;code&gt;Puppetfile&lt;/code&gt;, &lt;code&gt;manifests/site.pp&lt;/code&gt;, and &lt;code&gt;hiera.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;environment.conf&lt;/code&gt;, we focus on settings like &lt;code&gt;config_version&lt;/code&gt;, &lt;code&gt;modulepath&lt;/code&gt;, and &lt;code&gt;environment_timeout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When examining &lt;code&gt;Puppetfile&lt;/code&gt;, we emphasize library modules, versions, and sources. While &lt;a href="https://forge.puppet.com/"&gt;Puppet forge&lt;/a&gt; offers convenience, mirroring library module GIT repositories internally is advisable for control and security. Using git tags in &lt;code&gt;Puppetfile&lt;/code&gt; for module versioning facilitates pre-upgrade reviews and eliminates Puppet server internet dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your Control Repo
&lt;/h3&gt;

&lt;p&gt;Thorough testing validates code changes effectively. Our tests include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linting with &lt;a href="https://github.com/puppetlabs/puppet-lint"&gt;puppet lint&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Unit testing with &lt;a href="https://github.com/puppetlabs/rspec-puppet"&gt;rspec puppet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Acceptance testing using &lt;a href="https://github.com/voxpupuli/beaker"&gt;puppet beaker&lt;/a&gt; or &lt;a href="https://puppetlabs.github.io/litmus/"&gt;puppet litmus&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Catalog diffing with &lt;a href="https://github.com/voxpupuli/puppet-catalog_diff"&gt;catalog diff tool&lt;/a&gt; and &lt;a href="https://github.com/voxpupuli/puppet-catalog-diff-viewer"&gt;catalog diff viewer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider adopting &lt;a href="https://www.puppet.com/docs/pdk/3.x/pdk.html"&gt;PDK - Puppet Development Kit&lt;/a&gt; or leveraging &lt;a href="https://github.com/voxpupuli/onceover"&gt;onceover&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ensure you're using the right version of RSpec-Puppet, distinguishing between maintained projects by &lt;a href="https://github.com/rodjek/rspec-puppet"&gt;Rodjek&lt;/a&gt; and &lt;a href="https://github.com/puppetlabs/rspec-puppet"&gt;Puppet Inc.&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For your reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The official website for puppet-lint is now located at &lt;a href="https://puppetlabs.github.io/puppet-lint/"&gt;https://puppetlabs.github.io/puppet-lint/&lt;/a&gt;.&lt;br&gt;
Please note that the older version is no longer maintained.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Similarly, for RSpec, we advise consulting the &lt;a href="https://github.com/puppetlabs/rspec-puppet/blob/main/README.md"&gt;README&lt;/a&gt; file in the official GitHub repository. Be cautious not to mistake it for the project by Rodjek. While they may appear similar currently, this could change in the future.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Hiera
&lt;/h2&gt;

&lt;p&gt;Hiera is pivotal, covering node classification, library module API parameters, and infrastructure differences. We stress correct Hiera implementation, aligning hierarchy layers with infrastructure complexity while balancing diversity and maintainability.&lt;br&gt;
All Hiera layers should rely solely on Puppet or trusted facts.&lt;/p&gt;

&lt;p&gt;Next we analyze the data and check for data keys and their module namespace usage.&lt;/p&gt;

&lt;p&gt;If no module namespace is used, we can be sure that all hiera values are looked up using explizit lookup function.&lt;br&gt;
This has a huge impact on analyzing and understanding code and data, as class parameters are mapped to different names within hiera.&lt;/p&gt;




&lt;h2&gt;
  
  
  Node Classification
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;manifests/site.pp&lt;/code&gt;, we inspect resource defaults, node declarations, and data processing.&lt;/p&gt;

&lt;p&gt;Setting resource defaults simplifies coding, while following the roles and profiles pattern aids in managing numerous identically configured systems.&lt;/p&gt;

&lt;p&gt;For divergent infrastructures, Hiera-based node classification offers flexibility, shifting complexity from static code to dynamic data.&lt;/p&gt;

&lt;p&gt;We usually recommend to set the following resource defaults:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file : disable backup&lt;/li&gt;
&lt;li&gt;exec : path parameter default&lt;/li&gt;
&lt;li&gt;package : default provider (only needed in an environment with Windows nodes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another topic we look for is data processing based on node data like fqdn or network information. Here we ask to please move this to a custom fact.&lt;br&gt;
Nice side effect: this will drastically reduce the compilation time, as the node calculates its own data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Library Module Usage
&lt;/h2&gt;

&lt;p&gt;Leverage upstream library modules whenever possible to solve problems efficiently. However, consider community contributions or feature requests for missing functionalities rather than internal forks or rewrites.&lt;/p&gt;

&lt;p&gt;Modern libraries adopting the data-in-modules pattern facilitate configuration solely through Hiera data, minimizing Puppet code development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Profiles: Doing Them Right
&lt;/h2&gt;

&lt;p&gt;Profiles encapsulate infrastructure components like user management, authentication, and application deployment. Combining resource and library module declarations within profiles streamlines configuration management.&lt;/p&gt;

&lt;p&gt;Clear profile class naming aids in identifying responsibilities and maintaining a single source of truth.&lt;/p&gt;

&lt;p&gt;The main win over just using library module data is the capability to identify which profile declares the library class.&lt;/p&gt;

&lt;p&gt;Any subprofile which is split into multiple files - maybe due to better readability or maintainability - must be specified being a private subprofile.&lt;br&gt;
This can be achieved by placing &lt;code&gt;assert_private()&lt;/code&gt; into the beginning of the class body.&lt;/p&gt;

&lt;p&gt;The concept of profile module development is the same as for library modules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Module Development
&lt;/h2&gt;

&lt;p&gt;Module development involves several considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General Concepts and Structure: Utilize PDK for module creation and consider modulesync for managing multiple modules.&lt;/li&gt;
&lt;li&gt;Parameters and Variables: Emphasize class parameters for flexibility and avoid unnecessary variable mapping.&lt;/li&gt;
&lt;li&gt;Declaration and References: Ensure resource references are in the same class as the declaration and use Puppet-lint plugins for reference checks.&lt;/li&gt;
&lt;li&gt;Class and Resource Ordering: Maintain proper class and resource ordering for predictable execution.&lt;/li&gt;
&lt;li&gt;Documentation: Leverage Puppet Strings for comprehensive module documentation, including parameter usage, limitations, and code examples.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  General Concepts and Structure
&lt;/h3&gt;

&lt;p&gt;To simply get started with a new module we recommend using PDK. It will help to ramp up the module skeleton.&lt;br&gt;
If you create a class with PDK, it will also create a simple test class for it.&lt;/p&gt;

&lt;p&gt;In case that many modules must be managed, we recommend to switch to &lt;a href="https://github.com/voxpupuli/modulesync"&gt;modulesync&lt;/a&gt; from &lt;a href="https://github.com/voxpupuli"&gt;Voxpupuli Puppet community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every module must have a dedicated API.&lt;/p&gt;

&lt;p&gt;That means that there are specific classes to be used by others, which have parameter to allow adoptions.&lt;br&gt;
Any subclass which is not an API endpoint, must be marked as being &lt;code&gt;private&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Testing should be done on all classes, the private ones should verify for compiler error, whereas your API classes spec tests should also cover different parameters and operating systems.&lt;/p&gt;

&lt;p&gt;Differences between operating systems must be done via hiera data in library or profile modules.&lt;/p&gt;

&lt;p&gt;If you find self written profile class code repeated or copied within multiple modules you must check if building an additional internal library module can be used to reduce the multiple development overhead.&lt;/p&gt;

&lt;p&gt;Testing must at least cover linting and unit tests.&lt;br&gt;
Acceptance tests need VM automation or container technologies and should be added as soon as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameters and Variables
&lt;/h3&gt;

&lt;p&gt;Class Parameters allow flexible usage of profiles. Any parameter can be validated using a &lt;a href="https://www.puppet.com/docs/puppet/latest/lang_data_type.html"&gt;Puppet Data Type&lt;/a&gt;. You also can create custom data types if the basic ones are not sufficient for you use case. In the past validation was often done with &lt;code&gt;assert_*&lt;/code&gt; or &lt;code&gt;validate_*&lt;/code&gt; functions in the class body. With data types you can specify the type of data you are expecting right in the class header and don't need to care about validation later on.&lt;/p&gt;

&lt;p&gt;With class parameters you can specify different settings for specific use cases like access to a dev and a prod machine via hiera.&lt;/p&gt;

&lt;p&gt;Please stop doing variable mapping!&lt;/p&gt;

&lt;p&gt;We often see that hash parameters are not used directly but that certain elements of a hash are placed into a new variable.&lt;br&gt;
This only makes sense if you need the specific value multiple times.&lt;br&gt;
Rule of thumb, if you need a special piece of data more then twice, you might do a mapping.&lt;br&gt;
But in most cases we recommend to not do it!&lt;/p&gt;

&lt;p&gt;Variable mapping produces code which is hard to understand, because you must always keep in mind the original variable and the mapped variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declaration and References
&lt;/h3&gt;

&lt;p&gt;Using resource references, you can set default values and ordering.&lt;/p&gt;

&lt;p&gt;We sometimes see references on resources which are not local to the class.&lt;br&gt;
The class &lt;code&gt;config&lt;/code&gt; declares the file and a class &lt;code&gt;service&lt;/code&gt; declares the service.&lt;/p&gt;

&lt;p&gt;Whenever someone refactors on of the classes, you must also take care on the references.&lt;/p&gt;

&lt;p&gt;We highly recommend to make use of the &lt;a href="https://github.com/voxpupuli/puppet-lint-reference_on_declaration_outside_of_class-check"&gt;reference on declaration outside of class&lt;/a&gt; puppet-lint plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Class and Resource Ordering
&lt;/h3&gt;

&lt;p&gt;We mentioned that references should be local to a declaration.&lt;br&gt;
But how to ensure resource ordering?&lt;/p&gt;

&lt;p&gt;Normally resources inside a class are always processed in the same order as they appear in the class.&lt;br&gt;
There are some resource types which have &lt;code&gt;soft autorequire dependecies&lt;/code&gt;, like user and their primary group.&lt;/p&gt;

&lt;p&gt;In general you must ensure that classes are always done in right order.&lt;br&gt;
Otherwise puppet tries to make an educated guess in which order resources and classes might be.&lt;/p&gt;

&lt;p&gt;Classes and resources ordering should be separate from declaration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Classes can have many parameters. So where do you look for information on how to make use of them?&lt;br&gt;
This is where &lt;a href="https://www.puppet.com/docs/puppet/latest/puppet_strings.html"&gt;Puppet strings&lt;/a&gt; will be used.&lt;br&gt;
Puppet strings allows you to render the file &lt;code&gt;REFERENCE.md&lt;/code&gt; using a rake task.&lt;/p&gt;

&lt;p&gt;Additionally it offers the option to run a documentation web server with access to your completely deployed control-repository.&lt;br&gt;
It will check for any file updates and update the documentation automatically.&lt;br&gt;
We recommend to put a web server with user authentication in front of the puppet strings web service.&lt;/p&gt;

&lt;p&gt;Next to class responsibility, limitations, usage and parameter documentation we recommend to document any complex code.&lt;br&gt;
We sometimes see multiple usages of &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;reduce&lt;/code&gt; functions which parse and restructure data.&lt;br&gt;
Any new person working on the code must be able to understand why and what is done. Data examples can be useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Structured and documented code simplifies Puppet management. Adopting automated testing and coding principles from reputable library modules - like the ones from &lt;a href="https://forge.puppet.com/modules/puppetlabs"&gt;Puppet Inc&lt;/a&gt; and &lt;a href="https://forge.puppet.com/modules/puppet"&gt;Voxpupuli&lt;/a&gt; - enhances code quality and reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;Below are examples illustrating various Puppet practices.&lt;/p&gt;

&lt;h3&gt;
  
  
  environment.conf
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;config_version &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'bin/config_script.sh $environmentpath $environment'&lt;/span&gt;
modulepath &lt;span class="o"&gt;=&lt;/span&gt; site:modules:&lt;span class="nv"&gt;$basemodulepath&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  bin/config_script.sh
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; /usr/bin/git &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;ENVGITDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;/environments/&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;/.git"&lt;/span&gt;
  /usr/bin/git &lt;span class="nt"&gt;--git-dir&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENVGITDIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; log &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s2"&gt;"%h - %an, %ad : %s"&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"no git - environment &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  manifests/site.pp
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="nc"&gt;File&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;backup&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;Exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$facts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;$facts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'os'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'family'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'windows'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Package&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'choco'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Node classification - sorted Hash
# e.g.
# classes_hash:
#   '01_base': 'profile::base'
#   '02_security': 'profile::security'
#   '12_application': 'profile::application::billing'
#   '13_service':
#     - 'profile::application::billing::backend'
#     - 'profile::application::billing::admin_ui'
#
# overwriting identifier classes:
# classes_hash:
#   '13_service': 'profile::application::billing2'
#
# disabling identifier classes:
# classes_hash:
#   '13_service': ''
#
# $element[0]: the hash key identifier
# $element[1]: the class name to load
#
&lt;/span&gt;&lt;span class="nv"&gt;$classes_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'classes_hash'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'value_type'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'default_value'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nv"&gt;$classes_hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;$classes_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="nv"&gt;$classes_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="py"&gt;message&lt;/span&gt;  &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Class for &lt;/span&gt;&lt;span class="nv"&gt;${key}&lt;/span&gt;&lt;span class="s2"&gt; on &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;{facts['networking']['fqdn']} is disabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="py"&gt;withpath&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;node&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hiera Config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

&lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;datadir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
  &lt;span class="na"&gt;lookup_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eyaml_lookup_key&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pkcs7_private_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/etc/puppetlabs/puppet/keys/private_key.pkcs7.pem"&lt;/span&gt;
    &lt;span class="na"&gt;pkcs7_public_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/etc/puppetlabs/puppet/keys/public_key.pkcs7.pem"&lt;/span&gt;

&lt;span class="na"&gt;hierarchy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hierarchy"&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nodes/%{trusted.certname}.yaml"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hierarchy"&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role/%{trusted.extensions.pp_role}-%{trusted.extensions.pp_env}.yaml"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role/%{trusted.extensions.pp_role}.yaml"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;os&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hierarchy"&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;os/%{facts.os.family}-%{facts.os.release.major}.yaml"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;os/%{facts.os.family}.yaml"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zone&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hierarchy"&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zone/%{trusted.extentions.pp_zone}.yaml"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Common"&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;common.yaml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Simple Hiera Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# data/common.yaml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Node classification using classes_hash&lt;/span&gt;
&lt;span class="na"&gt;classes_hash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00_base_ssh'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssh'&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;01_base_nft'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile::nftables'&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;02_base_nft_rules'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile::nftables::rules::base'&lt;/span&gt;

&lt;span class="c1"&gt;# Commmon SSH data&lt;/span&gt;
&lt;span class="c1"&gt;# using saz-ssh: https://forge.puppet.com/modules/saz/ssh/readme&lt;/span&gt;
&lt;span class="na"&gt;ssh::server_options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2'&lt;/span&gt;
    &lt;span class="na"&gt;ListenAddress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;127.0.0.0'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%{::facts.networking.hostname}"&lt;/span&gt;
    &lt;span class="na"&gt;PasswordAuthentication&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yes'&lt;/span&gt;
    &lt;span class="na"&gt;SyslogFacility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AUTHPRIV'&lt;/span&gt;
    &lt;span class="na"&gt;UsePAM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yes'&lt;/span&gt;
    &lt;span class="na"&gt;X11Forwarding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yes'&lt;/span&gt;

&lt;span class="na"&gt;ssh::server::match_block&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;filetransfer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;group'&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ChrootDirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/home/sftp'&lt;/span&gt;
      &lt;span class="na"&gt;ForceCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;internal-sftp'&lt;/span&gt;

&lt;span class="c1"&gt;# Adding core resources&lt;/span&gt;
&lt;span class="c1"&gt;# requires puppetlabs-stdlib : https://forge.puppet.com/modules/puppetlabs/stdlib/readme&lt;/span&gt;
&lt;span class="na"&gt;stdlib::manage::create_resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;package'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nano'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ensure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;absent'&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vim'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ensure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;present'&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monitoring'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ensure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;present'&lt;/span&gt;
      &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;11225'&lt;/span&gt;
      &lt;span class="na"&gt;gid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;11225'&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Module Classes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# @summary Installs and configures the application
# @author betadots GmbH
# @param parameter1 Parameter to set thing 1
# @param parameter2 Parameter to set thing 2
# @example
#     include application
#   or
#     class { 'application':
#       parameter1 =&amp;gt; 'value,
#     }
#
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;application&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Datatype&lt;/span&gt; &lt;span class="nv"&gt;$parameter1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;Datatype&lt;/span&gt; &lt;span class="nv"&gt;$parameter2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'default'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="n"&gt;application::install&lt;/span&gt;
  &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="n"&gt;application::config&lt;/span&gt;
  &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="n"&gt;application::service&lt;/span&gt;

  &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'application::install'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'application::config'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;~&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'application::service'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;application::install&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;assert_private&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c"&gt;# Puppet DSL
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Class Spec Tests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# spec/classes/application_spec.rb&lt;/span&gt;
&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'application'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;on_supported_os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os_facts&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:facts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;os_facts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_expected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_all_deps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_expected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;contain_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'application::install'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# spec/classes/application_install_spec.rb&lt;/span&gt;
&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'application::install'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;on_supported_os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os_facts&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:facts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;os_facts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_expected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;and_raise_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;%{private}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy puppetizing,&lt;br&gt;&lt;br&gt;
Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>bestpractice</category>
      <category>itautomation</category>
      <category>platformengineering</category>
    </item>
    <item>
      <title>Scaling Puppet Infrastructure</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Mon, 05 Feb 2024 09:55:29 +0000</pubDate>
      <link>https://dev.to/betadots/scaling-puppet-infrastructure-3p2o</link>
      <guid>https://dev.to/betadots/scaling-puppet-infrastructure-3p2o</guid>
      <description>&lt;p&gt;In large environments with many nodes one must take care of Puppet server scaling.&lt;br&gt;
When and if scaling is needed, depends on the number of nodes and on code complexity and size.&lt;br&gt;
This article describes the different ways of tuning Puppet server infrastructure.&lt;/p&gt;

&lt;p&gt;Table of content:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Puppet Server tuning

&lt;ol&gt;
&lt;li&gt;Performance tuning a single node&lt;/li&gt;
&lt;li&gt;Scaling horizontally&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;PuppetDB tuning&lt;/li&gt;
&lt;li&gt;Analyzing performance win&lt;/li&gt;
&lt;li&gt;Distributed Puppet agent runs&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Scaling the hard way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scaling beyond JRuby limits&lt;/li&gt;
&lt;li&gt;Multiple Puppet Server instances&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Puppet Server tuning
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Performance tuning single node
&lt;/h3&gt;

&lt;p&gt;We sometimes see the usage of horizontal scaling, even when there is no need to do it.&lt;br&gt;
As scaling horizontally needs further infrastructure components like Load Balancer and Puppet Server Compilers, we usually first ask to do performance tuning on the single node instance.&lt;/p&gt;

&lt;p&gt;A properly configured and optimized single Puppet server should be able to handle &lt;a href="https://www.puppet.com/docs/pe/2023.5/hardware_requirements#hardware_requirements_standard"&gt;up to 2500 nodes&lt;/a&gt; - using default runinterval of 30 minutes.&lt;/p&gt;

&lt;p&gt;There are three different possibilities for tuning, which sometimes rely upon each other:&lt;/p&gt;
&lt;h4&gt;
  
  
  Java Version
&lt;/h4&gt;

&lt;p&gt;Puppet server package has a dependency on java openjdk (headless).&lt;br&gt;
On most Linux distributions this will install Java 1.8.&lt;/p&gt;

&lt;p&gt;Please ensure to upgrade to Java 17 and set the java alternative accordingly:&lt;/p&gt;

&lt;p&gt;e.g (on Almalinux 8)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alternatives &lt;span class="nt"&gt;--set&lt;/span&gt; java /usr/lib/jvm/java-17-openjdk-17.0.9.0.9-2.el8.x86_64/bin/java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Number of JRuby instances
&lt;/h4&gt;

&lt;p&gt;With its default configuration a Puppet Server spins up 2 JRuby instances.&lt;br&gt;
Each instance is able to handle a single Puppet Agent request.&lt;br&gt;
When there are more requests, these get queued.&lt;/p&gt;

&lt;p&gt;Each JRuby instance needs 512 MB of Java Heap RAM.&lt;br&gt;
Just to be sure: it is not possible to run more than 32 JRuby instances on a single node!&lt;/p&gt;

&lt;p&gt;Adopting the number of JRuby instances takes place in &lt;code&gt;/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf&lt;/code&gt; within the &lt;code&gt;jruby-puppet&lt;/code&gt; section by setting the &lt;code&gt;max-active-instances&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jruby-puppet: {
    ...
    max-active-instances: 8   # &amp;lt;------- JRuby instances
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that increasing the number of JRuby instances causes the Java process to need more Java HEAP RAM.&lt;/p&gt;

&lt;h4&gt;
  
  
  Puppet Server Java Heap Size
&lt;/h4&gt;

&lt;p&gt;The Java engine should have enough RAM to be able to spin up and maintain the JRuby instances.&lt;br&gt;
If you forget to increase Java Heap size, you will see Java out-of-memory error messages in the Puppet server logfile.&lt;/p&gt;

&lt;p&gt;Increasing Java Heapsize takes place in &lt;code&gt;/etc/default/puppetserver&lt;/code&gt; (Debian based) or &lt;code&gt;/etc/sysconfig/puppetserver&lt;/code&gt; (RedHat based).&lt;/p&gt;

&lt;p&gt;The Java Heap size is provided as a Java argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/[sysconfig|default]/puppetserver
JAVA_ARGS="-Xms18g -Xmx18g -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we have set the Java Heap size (upper and lower limit) to 18 GB RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Lower limit
-Xms18g
# Upper limit
-Xmx18g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is considered best practice to set upper and lower limit to the same value, so Java reserves the RAM upon start up.&lt;/p&gt;

&lt;p&gt;Please note that the maximum possible value is limited by the amount of the system RAM.&lt;br&gt;
If you increase the Java heap size beyond system RAM, you will find Kernel out-of-memory errors in the journal.&lt;/p&gt;

&lt;p&gt;Besides this one must be aware that Java switches memory handling when using more than 32 GB of heap, which reduce the amount of objects. Also see the &lt;a href="https://www.codecentric.de/wissens-hub/blog/35gb-heap-less-32gb-java-jvm-memory-oddities"&gt;blog post from Codecentric&lt;/a&gt; or &lt;a href="https://confluence.atlassian.com/jirakb/do-not-use-heap-sizes-between-32-gb-and-47-gb-in-jira-compressed-oops-1167745277.html#:~:text=Java%20disables%20Compressed%20Oops%20for,can%20store%20in%20the%20heap."&gt;Atlassian&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Reserved Code Cache
&lt;/h4&gt;

&lt;p&gt;When Puppet servers receive a request from a node the Puppet Code is loaded into code cache in memory.&lt;br&gt;
The default value is set to 512 MB RAM.&lt;/p&gt;

&lt;p&gt;A larger Puppet code base or complex code might need a larger Code cache setting.&lt;br&gt;
Code cache is configured as Java argument in &lt;code&gt;/etc/[sysconfig|default]/puppetserver&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/[sysconfig|default]/puppetserver
JAVA_ARGS="-Xms18g -Xmx18g -XX:ReservedCodeCacheSize=1024m -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we have set the code cache size to 1024 MB RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-XX:ReservedCodeCacheSize=1024m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that the maximum possible value is at &lt;code&gt;2048m&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Please ensure that the Puppet server process was restarted after setting all the required configuration options.&lt;/p&gt;

&lt;p&gt;A short mathematical sizing rule of thumb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Java Heap size (M) = ( No of JRuby instances * 512M ) + 512M
Reserved Code Cache Size (M) = No of JRuby instances * 128M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scaling horizontally
&lt;/h2&gt;

&lt;p&gt;If a single Puppet server is not able to handle the amount of requests - e.g. in large infrastructures exceeding 2000 nodes - one has the option to set up additional compilers.&lt;/p&gt;

&lt;p&gt;Please note that each Puppet server infrastructure component should receive the performance tuning settings!&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure setup
&lt;/h3&gt;

&lt;p&gt;A high performance Puppet infrastructure consists of the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CA Server&lt;/li&gt;
&lt;li&gt;Compiler(s)&lt;/li&gt;
&lt;li&gt;Load balancer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Puppet agent sends the request to the Load balancer.&lt;br&gt;
The load balancer passes the request to a free compiler.&lt;/p&gt;

&lt;p&gt;From a Puppet agent point of view, the request is sent to the Load balancer and the response is received from the compiler.&lt;br&gt;
This has a special meaning when it comes to SSL certificates and strict SSL validation.&lt;/p&gt;

&lt;p&gt;We will discuss this when we come to the compiler setup.&lt;/p&gt;
&lt;h3&gt;
  
  
  Puppet CA Server
&lt;/h3&gt;

&lt;p&gt;The Puppet CA server is a standalone single instance, which is used to spin up and maintain additional Puppet compilers.&lt;br&gt;
From CA server point of view a compiler is just a node.&lt;/p&gt;

&lt;p&gt;All new CSR's must be signed on the CA server.&lt;br&gt;
When you want to scale your Puppet infrastructure the CA Server will be your existing Puppet server.&lt;/p&gt;

&lt;p&gt;When we want to sign the compilers certificates including dns_alt_names, we must configure the CA instance, to be able to do this by modifying the &lt;code&gt;/etc/puppetlabs/puppetserver/conf.d/ca.conf&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;We must allow subject alt names setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/puppetlabs/puppetserver/conf.d/ca.conf
certificate-authority: {
    # allow CA to sign certificate requests that have subject alternative names.
    allow-subject-alt-names: true  # &amp;lt;----- enable SAN cert signing

    # allow CA to sign certificate requests that have authorization extensions.
    # allow-authorization-extensions: false

    # enable the separate CRL for Puppet infrastructure nodes
    # enable-infra-crl: false
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please ensure that the Puppet server process was restarted after doing all the required changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding compilers
&lt;/h3&gt;

&lt;p&gt;Compilers should &lt;strong&gt;not&lt;/strong&gt; act as CA Server!&lt;br&gt;
The CA functionality is managed in &lt;code&gt;/etc/puppetlabs/puppetserver/services.d/ca.cfg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now we need to change the settings so that a compiler does not act as CA server, but pass all CA related requests to the CA server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# To enable the CA service, leave the following line uncommented
# puppetlabs.services.ca.certificate-authority-service/certificate-authority-service
# To disable the CA service, comment out the above line and uncomment the line below
puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service
puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please not that all compilers (and the CA server) should receive the Puppet code. In most environments we see that the compilers and the CA server have a NFS mount on which the code is deployed on the CA server and used on all compilers.&lt;/p&gt;

&lt;p&gt;The NFS share is beyond scope of this document.&lt;/p&gt;

&lt;p&gt;The Puppet agent will not connect to a compiler but to the load balancer.&lt;/p&gt;

&lt;p&gt;If we keep the default setup, the Puppet agent will refuse to connect to the load-balancer, if DNS ALT Names are missing in the compilers certificate.&lt;/p&gt;

&lt;p&gt;The compiler &lt;strong&gt;MUST&lt;/strong&gt; have the Load balancer DNS name configured, prior generating the CSR.&lt;/p&gt;

&lt;p&gt;This can be achieved by adding the &lt;code&gt;dns_alt_names&lt;/code&gt; configuration setting into &lt;code&gt;7etc/puppetlabs/puppet/puppet.conf&lt;/code&gt; into the &lt;code&gt;agent&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[agent]
dns_alt_names = loadbalancer.domain.tld,compiler-fqdn.domain.tld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding load balancer
&lt;/h3&gt;

&lt;p&gt;Each request to a Puppet Server starts a compilation process with different runtimes. One should &lt;strong&gt;not&lt;/strong&gt; configure the roundrobin distribution algorithm on the load balancer.&lt;/p&gt;

&lt;p&gt;Instead we want to distribute the connections to the Puppet compiler with the least work to do - which means the one with the least connections. In HAproxy this setting is called &lt;code&gt;leastconn&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Besides this you do not want to rely on Layer 3 IP connection, but on Layer 7 functionality. Within HAproxy one should add the &lt;code&gt;SSL&lt;/code&gt; directive to each backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  PuppetDB tuning
&lt;/h2&gt;

&lt;p&gt;In most environments PuppetDB is used to store facts, reports, catalogs and exported resources.&lt;br&gt;
The more nodes you have in your infrastructure, the higher the load and the memory requirement on the PuppetDB process.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tuning PuppetDB
&lt;/h3&gt;

&lt;p&gt;PuppetDB is a HTTP(S) Rest API in front of a PostgreSQL database.&lt;br&gt;
One can say that PuppetDB also is a kind of web service.&lt;/p&gt;
&lt;h4&gt;
  
  
  Java Heap size
&lt;/h4&gt;

&lt;p&gt;The most important setting is Java heap size.&lt;br&gt;
Per default a PuppetDB is configured to use 512MB Heap size.&lt;/p&gt;

&lt;p&gt;Configuration takes place in &lt;code&gt;/etc/sysconfig/puppetdb&lt;/code&gt; (RedHat/SLES) or &lt;code&gt;7etc/default/puppetdb&lt;/code&gt; (Debian).&lt;/p&gt;

&lt;p&gt;The Java Heap size is provided as a Java argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/[sysconfig|default]/puppetdb
JAVA_ARGS="-Xms1g -Xmx1g ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we have set the Java Heap size (upper and lower limit) to 1 GB RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Lower limit
-Xms1g
# Upper limit
-Xmx1g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is considered best practice to set upper and lower limit to the same value, so Java reserves the RAM upon start up.&lt;/p&gt;

&lt;p&gt;We usually receive good results with 1GB or 2GB heap site (depending on the number of nodes)&lt;/p&gt;

&lt;h4&gt;
  
  
  Database connection pool
&lt;/h4&gt;

&lt;p&gt;Within the PuppetDB configuration file, which is located at &lt;code&gt;/etc/puppetlabs/puppetdb/conf.d/database.conf&lt;/code&gt;, one can set the maximum numbers of idle and active PostgreSQL connections within the &lt;code&gt;database&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;maximum-pool-size = 25 # default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that PuppetDB uses two connection pools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read pool&lt;/li&gt;
&lt;li&gt;write pool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The read pool can be set individually by setting &lt;code&gt;maximum-pool-size&lt;/code&gt; in the &lt;code&gt;read-database&lt;/code&gt; section. The default value is &lt;code&gt;10&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;maximum-pool-size&lt;/code&gt; should be set to the sum of both.&lt;/p&gt;

&lt;p&gt;Please check your PostgreSQL configuration (especially the max connection setting) prior increasing the connection pool.&lt;/p&gt;

&lt;h4&gt;
  
  
  Block facts
&lt;/h4&gt;

&lt;p&gt;Another possibility is to lower the number of facts stored.&lt;/p&gt;

&lt;p&gt;This can be achieved by setting &lt;code&gt;facts-blocklist&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;facts-blocklist = ["fact1", "fact2", "fact3"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Command processing threads
&lt;/h4&gt;

&lt;p&gt;Besides this PuppetDB lets you configure the number of threads by setting &lt;code&gt;threads&lt;/code&gt; within the &lt;code&gt;command-processing&lt;/code&gt; section in &lt;code&gt;/etc/puppetlabs/puppetdb/conf.d/config.ini&lt;/code&gt;.&lt;br&gt;
As default PuppetDB will use half the number of cores of the system.&lt;/p&gt;
&lt;h4&gt;
  
  
  Web server threads
&lt;/h4&gt;

&lt;p&gt;Last but least one can set the &lt;code&gt;max-threads&lt;/code&gt; in &lt;code&gt;/etc/puppetlabs/puppetdb/conf.d/jetty.ini&lt;/code&gt;.&lt;br&gt;
This specifies the number if parallel possible HTTP and HTTPS connections.&lt;/p&gt;
&lt;h3&gt;
  
  
  Scaling PuppetDB
&lt;/h3&gt;

&lt;p&gt;In former times, it was best practice to have a single PuppetDB instance on the Puppet Server.&lt;br&gt;
If you have the need for scaling Puppet Servers, one should consider having PuppetDB on each Puppet infrastructure node, which connect to a centralized PostgreSQL database.&lt;/p&gt;
&lt;h2&gt;
  
  
  Analyzing performance win
&lt;/h2&gt;

&lt;p&gt;Every time one does performance tuning, one also wants to get proof that the new setting has improved performance.&lt;br&gt;
The most simple check is by getting the compile times from puppetserver logfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/Compiled catalog/ {print $12" "$14}'&lt;/span&gt; /var/log/puppetlabs/puppetserver/puppetserver.log

production 0.07
production 0.05
production 0.05
production 0.04
production 0.04
production 0.03
production 0.03
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also get highest and lowest compile time&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/Compiled catalog/ {print $12" "$14}'&lt;/span&gt; /var/log/puppetlabs/puppetserver/puppetserver.log | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR==1; END{print}'&lt;/span&gt;

production 0.03
production 0.07
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need an average compile time the following command will be helpful&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/Compiled catalog/ {print $12" "$14}'&lt;/span&gt; /var/log/puppetlabs/puppetserver/puppetserver.log | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{total += $2; count++ } END { print total/count }'&lt;/span&gt;

0.0442857
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another option is provided by the &lt;a href="https://forge.puppet.com/modules/puppetlabs/puppet_operational_dashboards/readme"&gt;Puppet Operational Dashboards&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This will setup a Grafana frontend which reads data from an InfluxDB, which gets its content from a Telegraf agent which connects via HTTPS to the Puppet server metrics endpoints.&lt;/p&gt;

&lt;p&gt;In Puppet Enterprise the PostgreSQL database also uses SSL certificates, which also allows fetching PostgreSQL metrics.&lt;br&gt;
When running Puppet Open Source, one needs to add an additional telegraf configuration to query the PostgreSQL database using a user and password and push the data into InfluxDB.&lt;/p&gt;
&lt;h2&gt;
  
  
  Distributed Puppet agent runs
&lt;/h2&gt;

&lt;p&gt;Within the introduction we mentioned that a single server can handle up to 2500 nodes.&lt;br&gt;
This number of nodes requires that Puppet agent runs must be evenly distributed over time.&lt;/p&gt;

&lt;p&gt;The default way for a Puppet agent is running as a daemon, which will schedule a Puppet agent run upon start and triggers the next runs every 30 minutes (&lt;code&gt;runinterval&lt;/code&gt; config option).&lt;/p&gt;

&lt;p&gt;Due to the reason that systems might be starting in parallel one will still see overloaded Puppet server infrastructure.&lt;br&gt;
As a result one will recognize that the Puppet agent run takes very long time. If the Agent did not receive a reply from Puppet server within 5 minutes (&lt;code&gt;http_read_timeout&lt;/code&gt; config option), it will stop the actual request and repeat again in 30 minutes.&lt;/p&gt;

&lt;p&gt;The config options can be modified in &lt;code&gt;puppet.conf&lt;/code&gt; config file in section &lt;code&gt;agent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Please reconsider if Puppet should run every 30 minutes.&lt;br&gt;
In some infrastructure Puppet is running in &lt;code&gt;noop&lt;/code&gt; mode as changes may only be deployed during maintenance window.&lt;br&gt;
In this case one still wants the node to regular check its configuration, but maybe 4 times a day only, so people can identify within the reporting that the systems are still in desired state.&lt;br&gt;
Doubling the &lt;code&gt;runinterval&lt;/code&gt; option reduces the load on the Puppet server to its half, so the Puppet server can handle double number of nodes.&lt;/p&gt;

&lt;p&gt;But one still will see huge and many load spikes on the Puppet server.&lt;br&gt;
It still can happen that to many nodes try to request for their configuration in parallel.&lt;/p&gt;

&lt;p&gt;We usually recommend to customers to run the agent via cron.&lt;br&gt;
One run will be done at reboot and the other runs will take place as recurring cron entries.&lt;br&gt;
To distribute the agent runs over time one can make use of the &lt;code&gt;fqdnrand&lt;/code&gt; function inside Puppet. This function generates a unique number for a hostame or FQDN.&lt;/p&gt;

&lt;p&gt;One can use the &lt;a href="https://forge.puppet.com/modules/reidmv/puppet_run_scheduler/readme"&gt;reidmv puppet_run_scheduler module&lt;/a&gt; to configure Puppet accordingly.&lt;/p&gt;

&lt;p&gt;Please note that within Puppet Enterprise environments the Puppet agent on the Puppet servers &lt;strong&gt;must&lt;/strong&gt; be running as agent daemon service!&lt;/p&gt;
&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Tuning and scaling a Puppet server is very simple, as it is a single Java process and a web service only.&lt;/p&gt;

&lt;p&gt;Tuning is mostly related to the Java process and memory handling and is usually limited by the amount of RAM and numbers of CPUs being available.&lt;/p&gt;

&lt;p&gt;Scaling can be achieved by using simple, already established solutions to distribute web traffic amongst several servers.&lt;/p&gt;

&lt;p&gt;Distributing the Puppet agent runs ensures evenly used Puppet servers which allows more nodes to get managed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Scaling beyond JRuby limits
&lt;/h2&gt;

&lt;p&gt;We already mentioned that you can not spin up more than 32 JRuby instances.&lt;/p&gt;

&lt;p&gt;We tried it. Be sure. It is not working at all.&lt;/p&gt;

&lt;p&gt;But what to do if you have high end (e.g. HPC) hardware?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;72 cores&lt;/li&gt;
&lt;li&gt;248 GB RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Usually you want to slice the big hardware e.g using KVM or Docker and run multiple VMs or &lt;a href="https://dev.to/betadots/puppet-containers-are-back-alive-5c2n"&gt;Puppet server containers&lt;/a&gt; for example by using &lt;a href="https://github.com/voxpupuli/crafty"&gt;Voxpupuli CRAFTY&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Normally we see such a hardware only within virtualization or container based environments or within a HPC platform.&lt;/p&gt;

&lt;p&gt;In our specific use case the usage of KVM, Docker, Podman or Kubernetes was not possible. Don't ask.&lt;/p&gt;

&lt;p&gt;We talked about this in the &lt;a href="https://puppetcommunity.slack.com/archives/C0W298S9G/p1697392478214749"&gt;Puppet community Slack channel&lt;/a&gt; and people told us that the only option is to spin up multiple Puppet server instances by "some specific symlinking and copying of config files".&lt;/p&gt;

&lt;p&gt;Just to be sure: this is not something you can configure out-of-the-box, but needs lots of changes also to Puppet internal scripts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multiple Puppet Server instances
&lt;/h2&gt;

&lt;p&gt;You want it the hard way. Here we go..&lt;/p&gt;

&lt;p&gt;Please note that this is just first findings, not automated and of course unsupported!&lt;/p&gt;

&lt;p&gt;Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;big hardware&lt;/li&gt;
&lt;li&gt;multiple IP addresses on the node

&lt;ul&gt;
&lt;li&gt;one for the main Puppet CA server&lt;/li&gt;
&lt;li&gt;one for the HAproxy loadbalancer&lt;/li&gt;
&lt;li&gt;and one for each compiler&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DNS configured for each IP&lt;/li&gt;
&lt;li&gt;Puppet CA server configured and running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the main installation is used for the Puppet CA server&lt;/li&gt;
&lt;li&gt;compilers will use copy of this installation&lt;/li&gt;
&lt;li&gt;main CA server FQDN: puppetserver.domain.tld&lt;/li&gt;
&lt;li&gt;compiler FQDN: puppetcompiler1.domain.tld&lt;/li&gt;
&lt;li&gt;load balancer FQDN: puppetbalancer.domain.tld&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First we need to stop the running Puppet Server, then we can copy several directories and make modifications to configuration files and scripts.&lt;br&gt;
Afterwards we can start the Puppet server processes and then we can install and configure HAproxy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl stop puppetserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Files and directories needed
&lt;/h3&gt;

&lt;p&gt;Each Puppet server instance needs several files and directories copied from the main installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Service config&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-pr&lt;/span&gt; /etc/sysconfig/puppetserver /etc/sysconfig/puppetservercompiler1
&lt;span class="c"&gt;# Puppet agent config&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-pr&lt;/span&gt; /etc/puppetlabs/puppet /etc/puppetlabs/puppetcompiler1
&lt;span class="c"&gt;# Puppet server config&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-pr&lt;/span&gt; /etc/puppetlabs/puppetserver /etc/puppetlabs/puppetservercompiler1
&lt;span class="c"&gt;# Puppet server application&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-pr&lt;/span&gt; /opt/puppetlabs/server /opt/puppetlabs/servercompiler1
&lt;span class="c"&gt;# Puppet server logging&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-pr&lt;/span&gt; /var/log/puppetlabs/puppetserver /var/log/puppetlabs/puppetservercompiler1
&lt;span class="c"&gt;# Puppet server service&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-pr&lt;/span&gt; /usr/lib/systemd/system/puppetserver.service /usr/lib/systemd/system/puppetservercompiler1.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Config files
&lt;/h3&gt;

&lt;h4&gt;
  
  
  /etc/puppetlabs/puppet/puppet.conf
&lt;/h4&gt;

&lt;p&gt;The main Puppet configuration from the main Puppet CA Server should only receive an entry for the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;puppet config &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--section&lt;/span&gt; main server puppetserver.domain.tld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/sysconfig/puppetservercompiler1
&lt;/h4&gt;

&lt;p&gt;Change the paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
INSTALL_DIR="/opt/puppetlabs/servercompiler1/apps/puppetserver"
CONFIG="/etc/puppetlabs/puppetservercompiler1/conf.d"
# Bootstrap path
BOOTSTRAP_CONFIG="/etc/puppetlabs/puppetservercompiler1/services.d/,/opt/puppetlabs/servercompiler1/apps/puppetserver/config/services.d/"
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/services.d/ca.cfg
&lt;/h4&gt;

&lt;p&gt;Disable CA&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# To enable the CA service, leave the following line uncommented
#puppetlabs.services.ca.certificate-authority-service/certificate-authority-service
# To disable the CA service, comment out the above line and uncomment the line below
puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service
puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/conf.d/global.conf
&lt;/h4&gt;

&lt;p&gt;Adopt path&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;global: {
    # Path to logback logging configuration file; for more
    # info, see http://logback.qos.ch/manual/configuration.html
    logging-config: /etc/puppetlabs/puppetservercompiler1/logback.xml
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/conf.d/metrics.conf
&lt;/h4&gt;

&lt;p&gt;Adopt the server id&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metrics: {
    # a server id that will be used as part of the namespace for metrics produced
    # by this server
    server-id: puppetservercompiler1
    registries: {
        ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/conf.d/puppetserver.conf
&lt;/h4&gt;

&lt;p&gt;Adopt paths&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jruby-puppet: {
    ruby-load-path: [/opt/puppetlabs/puppet/lib/ruby/vendor_ruby]

    # Adopt path
    gem-home: /opt/puppetlabs/servercompiler1/data/puppetserver/jruby-gems

    # Adopt paths
    gem-path: [${jruby-puppet.gem-home}, "/opt/puppetlabs/servercompiler1/data/puppetserver/vendored-jruby-gems", "/opt/puppetlabs/puppet/lib/ruby/vendor_gems"]

    # Adopt path
    server-conf-dir: /etc/puppetlabs/puppetcompiler1

    server-code-dir: /etc/puppetlabs/code

    # Adopt path
    server-var-dir: /opt/puppetlabs/servercompiler1/data/puppetserver

    # Adopt path
    server-run-dir: /var/run/puppetlabs/puppetservercompiler1

    # Adopt path
    server-log-dir: /var/log/puppetlabs/puppetservercompiler1

    ...
}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/conf.d/webserver.conf
&lt;/h4&gt;

&lt;p&gt;Adopt Path and IP and/or Port&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;webserver: {
    access-log-config: /etc/puppetlabs/puppetservercompiler1/request-logging.xml
    client-auth: want
    ssl-host: 10.110.10.102 # &amp;lt;---- add compiler1 IP and/or
    ssl-port: 8141          # &amp;lt;---- use another port
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/logback.xml
&lt;/h4&gt;

&lt;p&gt;Adopt paths&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;configuration&lt;/span&gt; &lt;span class="na"&gt;scan=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;scanPeriod=&lt;/span&gt;&lt;span class="s"&gt;"60 seconds"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;appender&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"STDOUT"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ch.qos.logback.core.ConsoleAppender"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;encoder&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%t] [%c{2}] %m%n&lt;span class="nt"&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/encoder&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;appender&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"F1"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ch.qos.logback.core.rolling.RollingFileAppender"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- TODO: this path should not be hard-coded --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;file&amp;gt;&lt;/span&gt;/var/log/puppetlabs/puppetservercompiler1/puppetserver.log&lt;span class="nt"&gt;&amp;lt;/file&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;append&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/append&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;rollingPolicy&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- rollover daily --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;fileNamePattern&amp;gt;&lt;/span&gt;/var/log/puppetlabs/puppetservercompiler1/puppetserver-%d{yyyy-MM-dd}.%i.log.gz&lt;span class="nt"&gt;&amp;lt;/fileNamePattern&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- each file should be at most 200MB, keep 90 days worth of history, but at most 1GB total--&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;maxFileSize&amp;gt;&lt;/span&gt;200MB&lt;span class="nt"&gt;&amp;lt;/maxFileSize&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;maxHistory&amp;gt;&lt;/span&gt;90&lt;span class="nt"&gt;&amp;lt;/maxHistory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;totalSizeCap&amp;gt;&lt;/span&gt;1GB&lt;span class="nt"&gt;&amp;lt;/totalSizeCap&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/rollingPolicy&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;encoder&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%t] [%c{2}] %m%n&lt;span class="nt"&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/encoder&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;appender&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"STATUS"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ch.qos.logback.core.rolling.RollingFileAppender"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;file&amp;gt;&lt;/span&gt;/var/log/puppetlabs/puppetservercompiler1/puppetserver-status.log&lt;span class="nt"&gt;&amp;lt;/file&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;append&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/append&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;rollingPolicy&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- rollover daily --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;fileNamePattern&amp;gt;&lt;/span&gt;/var/log/puppetlabs/puppetservercompiler1/puppetserver-status-%d{yyyy-MM-dd}.%i.log.gz&lt;span class="nt"&gt;&amp;lt;/fileNamePattern&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- each file should be at most 200MB, keep 90 days worth of history, but at most 1GB total--&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;maxFileSize&amp;gt;&lt;/span&gt;200MB&lt;span class="nt"&gt;&amp;lt;/maxFileSize&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;maxHistory&amp;gt;&lt;/span&gt;90&lt;span class="nt"&gt;&amp;lt;/maxHistory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;totalSizeCap&amp;gt;&lt;/span&gt;1GB&lt;span class="nt"&gt;&amp;lt;/totalSizeCap&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/rollingPolicy&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;encoder&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- note that this will only log the JSON message (%m) and a newline (%n)--&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%m%n&lt;span class="nt"&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/encoder&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- without additivity="false", the status log messages will be sent to every other appender as well--&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;logger&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"puppetlabs.trapperkeeper.services.status.status-debug-logging"&lt;/span&gt; &lt;span class="na"&gt;level=&lt;/span&gt;&lt;span class="s"&gt;"debug"&lt;/span&gt; &lt;span class="na"&gt;additivity=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"STATUS"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/logger&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;logger&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"org.eclipse.jetty"&lt;/span&gt; &lt;span class="na"&gt;level=&lt;/span&gt;&lt;span class="s"&gt;"INFO"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;logger&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"org.apache.http"&lt;/span&gt; &lt;span class="na"&gt;level=&lt;/span&gt;&lt;span class="s"&gt;"INFO"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;logger&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"jruby"&lt;/span&gt; &lt;span class="na"&gt;level=&lt;/span&gt;&lt;span class="s"&gt;"info"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;root&lt;/span&gt; &lt;span class="na"&gt;level=&lt;/span&gt;&lt;span class="s"&gt;"info"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!--&amp;lt;appender-ref ref="STDOUT"/&amp;gt;--&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- ${logappender} logs to console when running the foreground command --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"${logappender}"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"F1"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /usr/lib/systemd/system/puppetservercompiler1.service
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#
# Local settings can be configured without being overwritten by package upgrades, for example
# if you want to increase puppetserver open-files-limit to 10000,
# you need to increase systemd's LimitNOFILE setting, so create a file named
# "/etc/systemd/system/puppetservercompiler1.service.d/limits.conf" containing:
#   [Service]
#   LimitNOFILE=10000
# You can confirm it worked by running systemctl daemon-reload
# then running systemctl show puppetserver | grep LimitNOFILE
#
[Unit]
Description=puppetserver compiler1 Service
After=syslog.target network.target nss-lookup.target

[Service]
Type=forking
EnvironmentFile=/etc/sysconfig/puppetservercompiler1
User=puppet
TimeoutStartSec=300
TimeoutStopSec=60
Restart=on-failure
StartLimitBurst=5
PIDFile=/run/puppetlabs/puppetservercompiler1/puppetserver.pid

# https://tickets.puppetlabs.com/browse/EZ-129
# Prior to systemd v228, TasksMax was unset by default, and unlimited. Starting in 228 a default of '512'
# was implemented. This is low enough to cause problems for certain applications. In systemd 231, the
# default was changed to be 15% of the default kernel limit. This explicitly sets TasksMax to 4915,
# which should match the default in systemd 231 and later.
# See https://github.com/systemd/systemd/issues/3211#issuecomment-233676333
TasksMax=4915

#set default privileges to -rw-r-----
UMask=027


ExecReload=/opt/puppetlabs/servercompiler1/apps/puppetserver/bin/puppetserver reload
ExecStart=/opt/puppetlabs/servercompiler1/apps/puppetserver/bin/puppetserver start
ExecStop=/opt/puppetlabs/servercompiler1/apps/puppetserver/bin/puppetserver stop

KillMode=process

SuccessExitStatus=143

StandardOutput=syslog

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scripts to adopt
&lt;/h3&gt;

&lt;p&gt;Unluckily several scripts must be modified, too.&lt;/p&gt;

&lt;p&gt;Mostly due to hardcoded paths.&lt;/p&gt;

&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/bin/puppetserver
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;#set default privileges to -rw-r-----&lt;/span&gt;
&lt;span class="nb"&gt;umask &lt;/span&gt;027

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"/etc/default/puppetservercompiler1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;.&lt;/span&gt; /etc/default/puppetservercompiler1
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"/etc/sysconfig/puppetservercompiler1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;.&lt;/span&gt; /etc/sysconfig/puppetservercompiler1
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"OpenBSD"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;JAVA_BIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;javaPathHelper &lt;span class="nt"&gt;-c&lt;/span&gt; puppetserver&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;JAVA_ARGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Xms2g -Xmx2g -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger"&lt;/span&gt;
    &lt;span class="nv"&gt;TK_ARGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"_puppet"&lt;/span&gt;
    &lt;span class="nv"&gt;INSTALL_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/puppetlabs/servercompiler1/apps/puppetserver"&lt;/span&gt;
    &lt;span class="nv"&gt;CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/etc/puppetlabs/puppetservercompiler1/conf.d"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You seem to be missing some important configuration files; could not find /etc/default/puppetservercompiler1 or /etc/sysconfig/puppetservercompiler1"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/apps/foreground
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;restartfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/puppetlabs/servercompiler1/data/puppetserver/restartcounter"&lt;/span&gt;
&lt;span class="nv"&gt;cli_defaults&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INSTALL_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/cli/cli-defaults.sh
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/apps/reload
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; +e

&lt;span class="nv"&gt;restartfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/puppetlabs/servercompiler1/data/puppetserver/restartcounter"&lt;/span&gt;
&lt;span class="nv"&gt;reload_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RELOAD_TIMEOUT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;120&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$reload_timeout&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;realname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"puppetservercompiler1"&lt;/span&gt;

...

&lt;span class="nv"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$restartfile&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pgrep &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetservercompiler1/conf.d"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-HUP&lt;/span&gt; &lt;span class="nv"&gt;$pid&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/apps/start
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; +e
&lt;span class="nb"&gt;env

&lt;/span&gt;&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pgrep &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetservercompiler1/conf.d"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;restartfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/puppetlabs/servercompiler1/data/puppetserver/restartcounter"&lt;/span&gt;
&lt;span class="nv"&gt;start_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;START_TIMEOUT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;300&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;real_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"puppetservercompiler1"&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/apps/stop
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; +e

&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pgrep &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetservercompiler1/conf.d"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;realname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"puppetservercompiler1"&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/cli-defaults.sh
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;INSTALL_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/puppetlabs/servercompiler1/apps/puppetserver"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JRUBY_JAR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Warning: the JRUBY_JAR setting is no longer needed and will be ignored."&lt;/span&gt; 1&amp;gt;&amp;amp;2
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;CLASSPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLASSPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/facter.jar:/opt/puppetlabs/servercompiler1/data/puppetserver/jars/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adoptions to scripts from main installation
&lt;/h3&gt;

&lt;p&gt;As we now have changed the way on how to determine the correct pid, we must also do this adoption at the main puppet server cli commands.&lt;/p&gt;

&lt;h4&gt;
  
  
  /opt/puppetlabs/server/apps/puppetserver/cli/apps/reload
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...

&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pgrep &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetserver/conf.d"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/server/apps/puppetserver/cli/apps/start
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...

&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pgrep &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetserver/conf.d"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/server/apps/puppetserver/cli/apps/stop
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...

&lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pgrep &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetserver/conf.d"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start the stack
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start puppetserver
systemctl start puppetservercompiler1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy hacking and success on performance tuning your Puppet server infrastructure.&lt;/p&gt;

&lt;p&gt;Martin Alfke&lt;br&gt;
&lt;a href="mailto:ma@betadots.de"&gt;ma@betadots.de&lt;/a&gt;&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>scaling</category>
      <category>tuning</category>
      <category>itautomation</category>
    </item>
    <item>
      <title>HDM Foreman integration</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Thu, 25 Jan 2024 08:55:17 +0000</pubDate>
      <link>https://dev.to/betadots/hdm-foreman-integration-3ge4</link>
      <guid>https://dev.to/betadots/hdm-foreman-integration-3ge4</guid>
      <description>&lt;p&gt;Analyzing Hiera Data can be very difficult.&lt;br&gt;
&lt;a href="https://github.com/betadots/hdm" rel="noopener noreferrer"&gt;HDM - Hiera Data manager&lt;/a&gt; is a web UI, which let's you analyze Hiera Data in a user-friendly way.&lt;/p&gt;

&lt;p&gt;We now have built and released the HDM Foreman integration, which consists of two packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/betadots/smart_proxy_hdm" rel="noopener noreferrer"&gt;Foreman HDM Smart Proxy plugin&lt;/a&gt;: smart_proxy_hdm&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/betadots/foreman_hdm/" rel="noopener noreferrer"&gt;Foreman HDM plugin&lt;/a&gt;: foreman_hdm&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  HDM Foreman Smart Proxy
&lt;/h2&gt;

&lt;p&gt;Foreman needs to read data from HDM. HDM offers an API endpoint to allow remote systems to query for data and structure.&lt;br&gt;
The &lt;a href="https://github.com/betadots/smart_proxy_hdm" rel="noopener noreferrer"&gt;Foreman HDM Smart Proxy plugin&lt;/a&gt; needs to know where it can find the HDM API endpoint and how to authorize.&lt;/p&gt;
&lt;h2&gt;
  
  
  HDM Foreman Plugin
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/betadots/foreman_hdm/" rel="noopener noreferrer"&gt;Foreman HDM plugin&lt;/a&gt; is an extension to the node view and adds a new tab which shows node hiera data.&lt;br&gt;
Node data are fetched via HDM Smart Proxy from HDM API endpoint.&lt;/p&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;First you need a &lt;a href="https://github.com/betadots/hdm" rel="noopener noreferrer"&gt;HDM - Hiera Data manager&lt;/a&gt; installation. The installation can take place on an individual node or a Puppet Server.&lt;br&gt;
HDM needs access to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PuppetDB to read environments, nodes and facts&lt;/li&gt;
&lt;li&gt;Puppet Code to read hiera config file and hiera data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Besides this HDM requires to &lt;strong&gt;only&lt;/strong&gt; make use of facts and trusted variables in hiera.yaml file.&lt;br&gt;
Any variable which gets set during catalog compilation can not be evaluated by HDM and HDM will display these hierarchies as unresolvable.&lt;/p&gt;

&lt;p&gt;The HDM Foreman integration needs at least Foreman 3.6 or newer. We don't build packages for older versions.&lt;br&gt;
Older versions must use HDM integration from ruybgems.org.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;You must install the packages from the Foreman Plugins repository and initialize the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; rubygem-foreman_hdm rubygem-smart_proxy_hdm
foreman-rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next you need to configure the HDM Smart Proxy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /etc/foreman-proxy/settings.d/hdm.yml&lt;/span&gt;
&lt;span class="c1"&gt;# HDM Smart Proxy&lt;/span&gt;
&lt;span class="na"&gt;:enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;
&lt;span class="na"&gt;:hdm_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://&amp;lt;HDM&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;IP&amp;gt;:&amp;lt;HDM&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Port&amp;gt;'&lt;/span&gt;
&lt;span class="na"&gt;:hdm_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;HDM&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;User&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Email&amp;gt;'&lt;/span&gt;
&lt;span class="na"&gt;:hdm_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;HDM&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;User&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Password&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HDM User and Password must be taken from an HDM API user!&lt;br&gt;
You can omit these settings if you have disabled HDM user authentication.&lt;/p&gt;

&lt;p&gt;Now the smart proxy must be restarted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart foreman-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&gt;

&lt;p&gt;HDM Data view&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbetadots%2Fhdm%2Fmain%2Fscreenshots%2FHDM-12-node_data_details_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbetadots%2Fhdm%2Fmain%2Fscreenshots%2FHDM-12-node_data_details_2.png" alt="HDM Data View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Foreman Data View&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbetadots%2Fhdm%2Fmain%2Fscreenshots%2Fforeman%2FForeman-Host-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbetadots%2Fhdm%2Fmain%2Fscreenshots%2Fforeman%2FForeman-Host-3.png" alt="Foreman Data View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy hacking on Puppet and Hiera data,&lt;/p&gt;

&lt;p&gt;Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>foreman</category>
    </item>
    <item>
      <title>Puppet container version schema update</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Wed, 03 Jan 2024 16:08:30 +0000</pubDate>
      <link>https://dev.to/betadots/puppet-container-version-schema-update-4n42</link>
      <guid>https://dev.to/betadots/puppet-container-version-schema-update-4n42</guid>
      <description>&lt;p&gt;Community: we hear you!&lt;/p&gt;

&lt;p&gt;Several people have asked to please change the version schema on &lt;a href="https://github.com/voxpupuli/container-puppetserver/"&gt;puppetserver-container&lt;/a&gt; and &lt;a href="https://github.com/voxpupuli/container-puppetdb/"&gt;puppetdb-container&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Puppet Inc version schema
&lt;/h2&gt;

&lt;p&gt;Prior the repositories have been handed over from &lt;a href="https://puppet.com"&gt;Puppet Inc&lt;/a&gt; to &lt;a href="https://voxpupuli.org"&gt;Puppet Community&lt;/a&gt;, the container images were using the Puppet server and PuppetDB versions, which were used inside the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;puppet.major&amp;gt;.&amp;lt;puppet.minor&amp;gt;.&amp;lt;puppet.patch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; puppet &lt;span class="nt"&gt;--hostname&lt;/span&gt; puppet puppetlabs-puppetserver:7.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;puppet.major&lt;/td&gt;
&lt;td&gt;Describes the contained major Puppet version (7 or 8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet.minor&lt;/td&gt;
&lt;td&gt;Describes the contained minor Puppet version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet.patch&lt;/td&gt;
&lt;td&gt;Describes the contained patchlevel Puppet version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Initial voxpupuli version schema
&lt;/h2&gt;

&lt;p&gt;The original version schema did not show if changes to the container image have been done.&lt;br&gt;
At &lt;a href="https://voxpupuli.org"&gt;Voxpupuli&lt;/a&gt; it was decided to use a new version schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v&amp;lt;major&amp;gt;.&amp;lt;minor&amp;gt;.&amp;lt;patch&amp;gt;-&amp;lt;puppet release&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; puppet &lt;span class="nt"&gt;--hostname&lt;/span&gt; puppet ghcr.io/voxpupuli/container-puppetserver:v1.0.0-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;major&lt;/td&gt;
&lt;td&gt;Describes the major version of the base container (Ubunutu 22.04) or incompatible changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;minor&lt;/td&gt;
&lt;td&gt;Describes new features or refactoring with backward compatibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;patch&lt;/td&gt;
&lt;td&gt;Describes if minor changes or bugfixes have been implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet release&lt;/td&gt;
&lt;td&gt;Describes the contained major Puppet release (7 or 8)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At &lt;a href="https://github.com/voxpupuli/crafty.git"&gt;Voxpupuli Crafty&lt;/a&gt; we had a &lt;a href="https://github.com/voxpupuli/crafty/discussions/22"&gt;discussion&lt;/a&gt; on how to improve the version schema.&lt;br&gt;
Additionally we received feedback from customers, that they were unhappy with the new version schema as it was unclear, which exact Puppet version is inside the containers.&lt;/p&gt;
&lt;h2&gt;
  
  
  New version schema
&lt;/h2&gt;

&lt;p&gt;The new version schema has the following layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;puppet.major&amp;gt;.&amp;lt;puppet.minor&amp;gt;.&amp;lt;puppet.patch&amp;gt;-v&amp;lt;container.major&amp;gt;.&amp;lt;container.minor&amp;gt;.&amp;lt;container.patch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; puppet &lt;span class="nt"&gt;--hostname&lt;/span&gt; puppet ghcr.io/voxpupuli/container-puppetserver:7.13.0-v1.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;puppet.major&lt;/td&gt;
&lt;td&gt;Describes the contained major Puppet version (7 or 8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet.minor&lt;/td&gt;
&lt;td&gt;Describes the contained minor Puppet version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet.patch&lt;/td&gt;
&lt;td&gt;Describes the contained patchlevel Puppet version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;container.major&lt;/td&gt;
&lt;td&gt;Describes the major version of the base container (Ubunutu 22.04) or incompatible changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;container.minor&lt;/td&gt;
&lt;td&gt;Describes new features or refactoring with backward compatibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;container.patch&lt;/td&gt;
&lt;td&gt;Describes if minor changes or bugfixes have been implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Many thanks to the community for the great feedback and ideas for improvement.&lt;/p&gt;

&lt;p&gt;Happy hacking on Puppet in containers.&lt;/p&gt;

&lt;p&gt;Martin Alfke&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>containers</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Puppet Containers are back alive</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Tue, 31 Oct 2023 09:40:15 +0000</pubDate>
      <link>https://dev.to/betadots/puppet-containers-are-back-alive-5c2n</link>
      <guid>https://dev.to/betadots/puppet-containers-are-back-alive-5c2n</guid>
      <description>&lt;p&gt;In early 2023 &lt;a href="https://www.puppet.com"&gt;Puppet Inc.&lt;/a&gt; decided to stop working on their containers for Puppetserver and PuppetDB and offered the repositories for adoption.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://betadots.de"&gt;betadots GmbH&lt;/a&gt; we believe that there is an usecase for Puppet servers based on containers.&lt;br&gt;
We also have several customers who already use Puppet in containers or are planning to migrate to a container based technology.&lt;/p&gt;

&lt;p&gt;Therefor we decided to continue, upgrade and improve the work on the containers.&lt;/p&gt;

&lt;p&gt;Content:&lt;/p&gt;

&lt;p&gt;Hosting and License&lt;br&gt;
Latest Updates&lt;br&gt;
Future Improvements&lt;br&gt;
Commercial Support&lt;/p&gt;




&lt;h2&gt;
  
  
  Hosting and License
&lt;/h2&gt;

&lt;p&gt;To prevent license changes done by a company, we asked the &lt;a href="https://voxpupuli.org"&gt;Voxpupuli community&lt;/a&gt; to be the host for the Open Source based repositories.&lt;/p&gt;

&lt;p&gt;This gives users and customers the benefit of being sure that the Open Source License will be kept. And also the potential number of contributors is much higher.&lt;/p&gt;

&lt;p&gt;On mid October &lt;a href="https://github.com/bastelfreak"&gt;Tim (bastelfreak) Meusel&lt;/a&gt; asked Puppet's famous and well known &lt;a href="https://github.com/binford2k"&gt;Ben (binford2k) Ford&lt;/a&gt; to migrate the container repositories to Voxpupuli. They where then transferred and are now owned and maintained by Voxpupuli.&lt;/p&gt;

&lt;p&gt;We now have two new repositories for container based Puppet infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/voxpupuli/container-puppetserver"&gt;container-puppetserver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/voxpupuli/container-puppetdb"&gt;container-puppetdb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2 hours after migration we were able to spin up an actual version of a Puppetserver and a PuppetDB on an updated base image.&lt;br&gt;
Both containers are now published under Apache-2.0 license.&lt;br&gt;
&lt;a href="https://betadots.de"&gt;betadots GmbH&lt;/a&gt; supports Voxpupuli with updating and maintaining the containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latest Updates
&lt;/h2&gt;

&lt;p&gt;For the moment we use the official Ubuntu 22.04 x86_64 base image in combination with the official Puppet Ubuntu packages released by Puppet Inc.&lt;/p&gt;

&lt;p&gt;We are looking forward to also maintain ARM based containers, but for this we have to wait for the official Packages being finished by Puppet Inc. Puppetserver and PuppetDB are already supported on ARM, to be more precise they don't depend on an arch. But we have still to wait for the Puppet Agent to be Released on the ARM platform. The nightly builds are already there, but we wait for the final, official release. The Agent is needed in some build steps for the container, so we wait. But the build pipeline is prepared and when the packages are available it is easy to add the new arch to it.&lt;/p&gt;

&lt;p&gt;On October 18th &lt;a href="https://github.com/rwaffen"&gt;Robert (rwaffen) Waffen&lt;/a&gt; announced on &lt;a href="https://puppetcommunity.slack.com/archives/C0W1Y5VL0/p1697641383534739"&gt;Puppet Community Slack in the voxpupuli channel&lt;/a&gt;, that he was able to deploy a fully functional container-based Puppet setup using a (Docker) compose file.&lt;/p&gt;

&lt;p&gt;The compose configuration is &lt;strong&gt;NOT&lt;/strong&gt; hosted within the container repositories, but in the &lt;a href="https://github.com/voxpupuli/crafty.git"&gt;CRAFTY - Containerized Resources And Funky Tools (in) YAML&lt;/a&gt; repository. This will serve as the central hub for bundling all components related to the containerized Puppet infrastructure. You can think of it as a spiritual successor to Pupperware.&lt;/p&gt;

&lt;p&gt;The containers are built to be compatible with Docker as well as other container systems such as Podman, for example. At present, they are exclusively built and tested in a Docker environment. If you encounter any issues with alternative systems, please don't hesitate to contact us. Additionally, we warmly welcome pull requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future improvements
&lt;/h2&gt;

&lt;p&gt;At the moment we work on a CI pipeline to automatically deploy newly built containers to docker hub and GitHub container registry. The Github part is already finished. But we might tweak some things here and there. So this is not yet production ready.&lt;/p&gt;

&lt;p&gt;Besides this, we need support for writing a proper helm chart within the &lt;a href="https://github.com/voxpupuli/crafty.git"&gt;CRAFTY Repository&lt;/a&gt;. Anyone who can help with this would be very welcome! We aim for the containers to be used in standalone container environments and also in cluster management systems like Kubernetes (k8s).&lt;/p&gt;

&lt;p&gt;We continue to enhance our documentation and are delighted to review and incorporate contributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commercial Support
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://betadots.de"&gt;betadots GmbH&lt;/a&gt;, we provide commercial support for container-based Puppet infrastructures.&lt;/p&gt;

&lt;p&gt;Please get &lt;a href="//mailto:info@betadots.de?subject=Puppet%20Support%20request"&gt;in touch with us&lt;/a&gt; if you need further information.&lt;/p&gt;

&lt;p&gt;Happy hacking on Puppet and containers to everyone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tuxmea"&gt;Martin (tuxmea) Alfke&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/rwaffen"&gt;Robert (rwaffen) Waffen&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/bastelfreak"&gt;Tim (bastelfreak) Meusel&lt;/a&gt;&lt;/p&gt;

</description>
      <category>containers</category>
      <category>puppet</category>
      <category>configurationmanagement</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
