<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Planet Puppet</title>
  <link rel="self" href=""/>
  <link href=""/>
  <id></id>
  <updated></updated>
  <generator uri="http://feedreader.github.io/">Pluto 1.6.3 on Ruby 2.7.4 (2021-07-07) [x86_64-linux-gnu]</generator>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Ansible vs Puppet: Choose the Operating Model, Not the Syntax</title>
    <link href="https://example42.com/blog/2026/07/17/ansible-vs-puppet/"/>
    <id>https://example42.com/blog/2026/07/17/ansible-vs-puppet</id>
    <updated>2026-07-17T06:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    Every Ansible-vs-Puppet argument I’ve ever watched start begins in the wrong place: YAML versus the Puppet DSL, agentless versus agent-based, tabs versus curly braces and mutual contempt. Fun bar conversation. Useless decision criteria.

The question that actually matters is duller, and bigger: how do you want infrastructure policy evaluated, applied, observed, and owned — not this sprint, for the next several years.

Puppet and Ansible overlap more than either camp likes to admit. Both install packages, render files, manage services, create users, and coordinate changes across a fleet. Same destinations, different routes — and the route stops being a footnote and starts being the whole story as the estate, the team, and the compliance requirements grow.

The short answer

Choose Puppet when you want continuous desired-state enforcement, per-node catalogs, rich run reporting, policy that outlives whoever wrote it, and convergence that happens locally without anyone pushing a button.

Choose Ansible when agentless reach, ordered orchestration, fast automation across a herd of dissimilar things, and execution from one control point matches how your team actually likes to work.

Use both when the boundary between them is drawn in ink, not vibes: Puppet owns persistent operating-system policy, Ansible owns deployments, network changes, or the coordinated one-off procedure.

Don’t migrate because one DSL looked shorter in a conference demo. Migrate when the target operating model solves a problem your current one can’t solve economically. Should you migrate at all? The answer might be yes. It might not be, and there’s no Deep Thought to compute it for you in seven and a half million years — you still have to do the discovery work.

Ansible and Puppet compared


  
    
      Dimension
      Puppet
      Ansible
    
  
  
    
      Primary model
      Declarative resources compiled into a per-node catalog
      Ordered plays and tasks executed against selected inventory
    
    
      Typical execution
      Agents request and apply catalogs on a schedule
      A control node connects to targets, commonly over SSH
    
    
      State enforcement
      Recurring convergence is the default
      State is evaluated when a playbook runs
    
    
      Language
      Puppet DSL, Hiera data, EPP templates
      YAML playbooks and roles, Jinja templates
    
    
      Ordering
      Resource relationships form a dependency graph
      Task order is explicit; handlers add event-driven actions
    
    
      Inventory and classification
      Facts, certificates, environments, Hiera, and classification produce a node catalog
      Static or dynamic inventory, groups, variables, and play targeting select hosts
    
    
      Change preview
      No-op catalog application and catalog compilation
      Check mode and diff mode where modules support them
    
    
      Reporting
      Agent reports can include every resource status, event, log, and run metric
      Play recap, callback output, controller logs, and automation-platform history
    
    
      Strongest fit
      Persistent system policy and continuous compliance
      Orchestration, deployments, remote automation, and heterogeneous targets
    
  


Puppet’s own documentation calls a catalog a node-specific desired-state document with resource dependencies baked in: the agent compares actual state against it and corrects what doesn’t match. Ansible’s documentation describes playbooks as human-readable automation that can enforce configuration or walk a multi-tier operational procedure step by step. Both are idempotent — when the underlying resources or modules were actually written to be idempotent. Neither tool makes an arbitrary shell command idempotent by osmosis. I wish it did. I’ve written enough exec resources to know better.

Sources: Puppet catalog compilation, Ansible introduction, and Ansible playbooks.

Desired state versus an ordered procedure

Puppet code declares resources and relationships, then lets the primary server do the arguing. It resolves facts, classification, Hiera data, manifests, functions, and templates into one unambiguous catalog for one node — this package must exist, this file must have this content, this service must run, all wired together with dependency edges that decide who goes first.

Ansible playbooks read like what they are: a procedure. Target these hosts, run these tasks in this order, fire a handler if something changed, walk the fleet according to whatever strategy you picked. The modules underneath — package, file, service — can still be idempotent. But the playbook keeps a narrative running the whole time. It tells a story about execution, not just about outcome.

That distinction shapes design, whether you notice it or not:


  A well-designed Puppet profile describes the policy that must remain true.
  A well-designed Ansible role describes repeatable tasks that produce the required state.
  A poor Puppet migration wraps every old script in exec resources. I’ve been that migration. More than once.
  A poor Ansible migration converts every Puppet resource into a task without reconsidering orchestration, variables, handlers, and run frequency.


Continuous convergence and drift

Puppet agents normally run on a schedule, so drift detection and remediation come baked into the model whether anyone asked for them or not. Someone changes something they shouldn’t at 2am, the next agent run notices, corrects it, and files the whole thing as a corrective event — a little timestamped confession. Agents also keep operating from their cached catalog when a fresh one can’t be compiled, depending on configuration and failure mode — a safety net, not a promise.

Ansible normally changes or checks a system only when it’s invoked. Continuous enforcement is something you build on top of that: a scheduler, an automation controller, event-driven automation, ansible-pull, or some other deliberate mechanism. That can be exactly right — plenty of teams genuinely don’t want every policy continuously re-applied. But it means the drift model has to be designed on purpose. It doesn’t come free in the box.

Ask the concrete question instead of the abstract one: who notices if /etc/ssh/sshd_config changes at 02:00, and what happens next? The answer tells you more about your real posture than any feature matrix ever will.

Agentless does not mean infrastructure-less

Ansible skips the dedicated management agent on normal SSH-reachable nodes, and that genuinely cuts endpoint packaging and lifecycle work. What it doesn’t do is make the infrastructure disappear — it just relocates it to somewhere you’re not looking yet. You still need:


  Reliable remote connectivity and credentials.
  Python or other target-side runtime requirements for many modules.
  Inventory ownership and freshness.
  A secured execution environment and dependency management.
  Scheduling, concurrency control, logs, secrets, and operator authorization.


Puppet asks for an agent, and usually a server-side platform to go with it. In exchange, every node gets a local enforcement engine, a certificate identity, a catalog built just for it, and a recurring report trail home. The extra component is a real cost — no argument there — but it’s also the exact mechanism producing some of Puppet’s strongest properties. You don’t get the convergence without paying for the thing that converges.

Change safety: no-op and check mode

Puppet no-op compiles the real catalog, evaluates it against actual resources, and reports what it would have changed. Useful, sometimes uncannily so. It still depends on provider quality, and it still can’t predict what an external command or API call does once it actually runs. No-op reads the map, not the territory.

Ansible check mode simulates changes for modules that support it. Read that clause again, specifically the qualifier — because the official documentation is explicit that a module without check-mode support does nothing and reports nothing when it’s run in check mode, and tasks depending on registered results from it may not behave as they would in a real run. Silence here isn’t reassuring. It might just mean the module shrugged. Diff mode adds before-and-after detail on top, where supported.

Neither preview is a substitute for representative tests, canaries, and a rollback plan you’ve actually rehearsed. Treat preview output as evidence, not proof — a witness statement, not a verdict. See the Ansible check and diff mode documentation.

Scale is more than node count

Both tools handle large estates just fine. What breaks under load is different, and that’s the part people skip past to get to the demo.

Puppet centralizes catalog compilation, code, data, certificates, and reports, and pushes the applying work out to agents. Server capacity, JRuby pools, compile time, PuppetDB, PostgreSQL, environment caching, and run interval — that’s your suspect list when things slow down.

Ansible initiates connections and work from wherever it’s executing. Forks, strategy plugins, task duration, network latency, inventory plugins, controller capacity, and whatever rate limit the external API decided to enforce today — that’s yours instead. The serial, throttle, and strategy controls exist to make rolling execution explicit rather than accidental; the Ansible strategy documentation walks through them properly.

“We have 5,000 nodes” tells me nothing on its own. Five thousand nodes quietly applying stable catalogs every thirty minutes, and five thousand nodes receiving one coordinated application deployment at the same moment, are not the same workload wearing a different hat. They’re different animals that happen to share a headcount.

When Puppet is the stronger fit


  Long-lived Linux and Windows systems must continuously conform to policy.
  The organization values a compiled, node-specific catalog and detailed resource reports.
  Changes must be expressed as relationships rather than a global task order.
  Nodes may need to converge independently of a central push window.
  Hiera, facts, roles and profiles, and the existing module ecosystem already encode significant operational knowledge.
  Compliance depends on recurring evidence of actual versus desired state.


For old puppeteers like me, that list reads less like a feature checklist and more like the reason the job exists.

When Ansible is the stronger fit


  The estate includes network devices, appliances, APIs, or systems where an agent is undesirable.
  Orchestration and coordinated procedures are at least as important as continuous configuration.
  SSH-based access and central execution already match the security and operating model.
  Teams need rapid automation across changing or short-lived targets.
  Existing roles, collections, inventory, and automation-controller workflows already provide the organizational platform.


When a hybrid is better than a migration

A boundary I see actually hold up, over and over:


  Puppet manages operating-system baselines, accounts, packages, services, and persistent configuration.
  Ansible performs application deployments, maintenance workflows, network changes, and coordinated actions.


This only works if the same property never has two owners. If Puppet insists on one version of a file while Ansible quietly deploys another, that’s not defense in depth. That’s two tools fighting over the same resource, and the fight is what gets deployed — an automation loop with a heartbeat.

Write ownership down at the resource or workload level, not in your head and not in a Slack thread from eight months ago. Decide which inventory is authoritative, how credentials stay separated, where execution history actually lives, and how an engineer knows which tool to reach for without having to ask in the incident channel.

A safe migration method

1. Inventory behavior, not files

List managed resources, schedules, integrations, reports, secrets, classification rules, and operator workflows. Repository line count is a poor measure of migration effort — about as useful as counting words in a contract to estimate the lawsuit.

2. Classify workloads

Separate persistent policy, application configuration, provisioning, deployment, orchestration, compliance, and one-off operations. Different categories may have different target tools, and often should.

3. Define acceptance evidence

For each workload, record the resulting files, packages, services, commands, reports, and failure behavior. Preserve or create tests before translation — not after, when the only remaining oracle is someone’s memory of how it used to work.

4. Build native target patterns

Design roles and inventory for Ansible, or profiles, modules, and Hiera for Puppet. Do not make the target imitate the source DSL. A Puppet manifest that’s secretly a YAML playbook wearing a trenchcoat serves nobody.

5. Pilot a representative slice

Choose a workload containing data, dependencies, templates, service notification, and more than one operating system. A trivial package example proves very little — you already knew packages could be installed.

6. Prevent double ownership

During transition, only one tool should manage a resource property. Use explicit cutover boundaries and retain a tested rollback — tested, not just written down and hoped for.

7. Measure the new operating cost

Compare failure rate, drift, execution duration, change visibility, access management, test burden, and operator effort after the pilot. If nobody captured a baseline before the pilot, congratulations — you’ve just invented one retroactively, and it will conveniently favor whichever tool you already wanted.

The practical conclusion

Ansible is not “Puppet without agents,” and Puppet is not “Ansible that runs every thirty minutes.” They encode different assumptions about where policy gets evaluated and who’s allowed to touch what, and when.

Choose the assumptions that fit your estate, not the ones that read better on a slide. If neither model should own everything, draw a hard boundary and use both deliberately — on purpose, not by accident three incidents from now.

example42 supports migrations in both directions. The Ansible and Puppet migration service covers discovery, target patterns, pilots, staged rollout, and team handover. [Shameless plug].

Alvabot
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Puppet 7 to Puppet 8 Migration Guide</title>
    <link href="https://example42.com/blog/2026/07/10/puppet-7-to-8-migration-guide/"/>
    <id>https://example42.com/blog/2026/07/10/puppet-7-to-8-migration-guide</id>
    <updated>2026-07-10T06:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    Let’s get this straight before we start: a Puppet 7-to-8 upgrade is not a package update. It wears that costume — new repo, new version pin, apt upgrade and off you go — but underneath it’s a compatibility audit. You’re proving that your code, your data, your modules, your custom Ruby extensions, your server platform, and the workflow your team runs on autopilot all still agree with each other once the runtime underneath them changes.

Ruby moves. OpenSSL moves. Strict mode flips on whether you asked for it or not. And in 2026 there’s a decision to make before any of that technical work starts: which Puppet 8 are you actually moving to? Open-source Puppet 8.10 was the last release Perforce maintained. Puppet Core continues from 8.11 as a commercial product. Puppet Enterprise has its own console-and-orchestrator upgrade path entirely. OpenVox is the community-maintained continuation, carrying the open-source torch under new names.

Choose the target first. Repository access, package names, supported operating systems, lifecycle, entitlements and support expectations all hang off that one decision — everything below assumes you’ve already made it.

The four target paths

Open-source Puppet 8.10

Still installable, still runs. But Perforce says plainly that open-source Puppet gets nothing further from them past 8.10. Freezing here can be a legitimate, temporary compatibility step. It is not a security strategy — and treating it as one is how “temporary” quietly becomes “for the next three years.” See the Puppet Core 8.11 announcement.

Puppet Core 8

The Perforce-maintained platform, built from the open-source tree, distributed through protected repositories that require actual entitlements — no more anonymous apt-get install. Perforce documents a direct transition from open-source Puppet 8 to Puppet Core; if your installation is anything but vanilla, custom bits may need extra work. See Upgrading from open-source Puppet to Puppet Core.

Puppet Enterprise

PE bundles the platform with its console, orchestrator, classifiers and deployment tooling — and its own supported upgrade procedure, full stop. Don’t run open-source server commands against a PE primary and hope for the best; that’s not an upgrade, that’s an incident report waiting for a title. Follow the PE version-specific path instead. The current Puppet Enterprise upgrade documentation covers infrastructure and agent sequencing, cautions, migration, and module testing.

OpenVox 8

The community-maintained open-source implementation. Its own docs say it stays compatible with Puppet language and configuration while using replacement package names and repositories — same language, new labels, ship of Theseus for your configuration management. Treat the swap as a platform change anyway: test it on fresh systems or a controlled clone, back up /etc/puppetlabs, and validate server, database, terminus and module compatibility before you trust it in production. See the OpenVox installation and migration guidance.

The rest of this guide covers the code and runtime work all four paths share. Wherever it disagrees with your distribution’s own installation and lifecycle rules, theirs wins.

Breaking changes that deserve attention

Perforce’s own Puppet 7-to-8 upgrade notes lay out the real compatibility boundaries. Four of them will actually bite.

Ruby 3.2

Puppet 8 agents vendor MRI Ruby 3.2. Puppet Server 8 runs on JRuby 9.4, which implements most — not all — of the Ruby 3.1 interface. Plain Puppet DSL code barely notices. Your Ruby extensions will.

Inventory all of these before you touch a package manager:


  Custom facts.
  Ruby functions.
  Types and providers.
  Report processors.
  Indirector termini.
  Face, application, and plugin code.
  Gems installed into Puppet’s vendored Ruby or Puppet Server’s JRuby environment.


Run their unit tests against the target runtime, then actually exercise them in real catalog compilation — unit tests alone will lie to you by omission. Look for removed Ruby behavior, keyword-argument changes, standard-library gems that quietly stopped being bundled, and native extensions still built for the old runtime.

OpenSSL 3

Puppet 8 agents vendor OpenSSL 3. Anything compiled against Puppet’s previous OpenSSL — your own tooling, third-party native extensions — needs rebuilding. Old algorithms, old certificate material, or local integrations can trip over this even when the Puppet CA itself is perfectly healthy.

And while we’re on the subject: don’t regenerate the CA just because the major version ticked over. Back it up, test certificate operations, and touch trust infrastructure only when there’s a separate requirement and a complete rotation plan already written down. A CA rebuild is not a side effect of a Puppet upgrade — it’s its own project, with its own maintenance window and its own reasons to lose sleep.

Strict mode by default

Puppet 8 turns strict mode on by default. Code that references undefined variables, relies on invalid coercion, or leans on other previously-tolerated shortcuts can simply fail to compile.

There are compatibility settings that dial strictness back down. Treating them as the actual migration endpoint doesn’t fix anything — it just files the failure for later, with interest. Turn strict checking on in CI while you’re still on Puppet 7, fix what breaks there, and let the production cutover be the anticlimax it should be.

Legacy facts are no longer collected by default

$::osfamily and $facts[&#39;osfamily&#39;] both need to become $facts[&#39;os&#39;][&#39;family&#39;] or the equivalent structured fact. Search manifests, templates, Hiera hierarchy interpolation, custom functions, and tests — not only .pp files. Legacy facts like to hide in string interpolation and hierarchy paths where a lazy grep won’t find them.

Puppet can be told to keep collecting legacy facts a while longer, but that’s a transition control, not a destination. The official module upgrade guidance is blunt about it: clean up legacy facts, top-scope variables, missing data types, and strict-mode issues before the jump, not after.

Hiera 3 backends

The Hiera 3 component is gone. Dropped. Deceased. Any backend implemented by extending Hiera::Backend needs to become a Hiera 5 extension, or be replaced outright. Ordinary Hiera 5 hierarchies, puppet lookup, and modern lookup functions were never the problem here.

Find custom backends early. They tend to live outside the control repository, quietly forgotten, until every catalog depending on them fails at once and someone has to remember they exist.

Phase 1: inventory the real platform

Capture versions and ownership for:


  Puppet agent, Puppet Server, PuppetDB, termini, and PostgreSQL.
  Operating systems and architectures for every infrastructure role and representative agents.
  Java and JVM configuration on Puppet Server and PuppetDB nodes.
  Control repositories, environments, deployment tooling, and CI images.
  Every module and its dependency constraints from the Puppetfile.
  Locally developed modules and all custom Ruby extensions.
  Installed gems, report processors, ENC integrations, secret backends, and webhooks.
  Certificate Authority location, expiry, backups, CRLs, and renewal behavior.
  External consoles, dashboards, orchestrators, and API consumers.


Also record baseline behavior: catalog compile time, run duration, failure rate, recurring changes, PuppetDB queue and query health, JVM memory, and report volume. Skip this and a technically flawless upgrade can still hide a serious regression — you just won’t have anything to catch it against.

Phase 2: make Puppet 7 behave like the target

Close the compatibility gap before you change a single package.


  Enable strict checks in development and CI.
  Replace legacy and top-scope fact access with structured facts.
  Add data types to public class parameters where practical.
  Remove deprecated language constructs and suppressed lint warnings.
  Move Hiera 3 backends and obsolete hierarchy patterns to Hiera 5.
  Update modules to releases declaring Puppet 8 support.
  Test custom Ruby code under Ruby 3.2 and the target Puppet Server runtime.


Useful checks include:

puppet parser validate manifests/site.pp
puppet epp validate templates/example.epp
pdk validate
pdk test unit
puppet lookup profile::example::setting --node test.example.com --explain


Run these through CI, with the same module and gem dependency resolution production will actually use. A developer laptop with its own Ruby version and unpinned gems isn’t a migration test — it’s a story you’ll tell later, ruefully.

Phase 3: prove catalog compatibility

Unit tests catch module behavior in isolation. They prove nothing about whether your full classification, Hiera data, facts, and module set compile together — which is, annoyingly, the part that actually matters.

Build representative fact sets for each operating-system family, role, environment, and important edge case. Compile catalogs on Puppet 7 and on the target Puppet 8 stack. Compare:


  Compilation success and warnings.
  Resource counts and types.
  Removed or newly managed resources.
  Dependency edges that affect ordering.
  File content and sensitive-data handling.
  Exported-resource and PuppetDB-dependent behavior.
  Compile time and memory use.


Then apply target catalogs to disposable or restored test nodes. Catalog equality isn’t the goal — provider and runtime changes may legitimately differ — but every difference needs an explanation, not a shrug.

Phase 4: rehearse the infrastructure upgrade

Clone or rebuild a representative non-production stack. Restore sanitized code, data, PuppetDB data if needed, and a safe copy of the CA — inside an isolated network, please. Exercise:


  Server startup and full catalog compilation.
  Certificate signing, renewal, revocation, and CRL distribution.
  PuppetDB command processing, queries, reports, and dashboard integrations.
  Code deployment and environment cache behavior.
  Backup and restore procedures.
  Monitoring, alerting, log collection, and service restart behavior.
  Agent compatibility across the oldest and newest supported operating systems.


Don’t discover, during the actual production window, that the new server package doesn’t exist for your current server OS. Platform support is part of this upgrade whether you scheduled it or not — sometimes a parallel rebuild is safer than an in-place OS-plus-Puppet-major-version leap.

Phase 5: upgrade infrastructure before agents

Puppet’s own agent upgrade guidance isn’t negotiable on this point: the server goes first. In a simple open-source or Puppet Core deployment, upgrade and verify the server-side components in the supported order for the chosen distribution, PuppetDB and termini compatibility included.

For load-balanced compilers, rotate nodes individually when the product documentation supports mixed operation. For Puppet Enterprise, follow the PE installer or Puppet Installation Manager sequence exactly — infrastructure nodes running incompatible versions can break agent runs and replication in ways that are tedious to untangle afterward.

After each infrastructure step, verify:


  Status endpoints and service health.
  Catalog compilation for representative nodes.
  PuppetDB command queues and query results.
  A controlled Puppet 7 agent run against the upgraded server.
  Report ingestion and external integrations.


The official Puppet Server upgrade procedure and your distribution’s version-specific release notes are the actual authority here — this guide gives you the shape, not the exact commands.

Phase 6: canary Puppet 8 agents

Start with nodes that are representative, observable, and recoverable — not just the handful of empty test machines nobody would miss. Include:


  Each supported operating-system family and major version.
  Nodes using custom facts, providers, or native extensions.
  High-resource catalogs and unusual Hiera paths.
  Systems with security hardening or restricted networking.
  At least one important service where the team knows what normal looks like.


For each canary:


  Back up configuration and record the installed package state.
  Run the last Puppet 7 catalog and keep its report.
  Upgrade the agent through the supported repository and package path.
  Run Puppet 8 in no-op and review every proposed change — actually read it, don’t skim.
  Apply only after the output is understood.
  Compare reports, facts, runtime, plugins, and service behavior with the baseline.
  Leave the canary through several normal run intervals before expanding.


Roll out in bounded cohorts. Pause automatically on compile failures, unexpected corrective changes, agent crashes, or a runtime regression that’s more than rounding error.

Rollback is a designed path

Downgrading the package is the easy part of rollback. Decide, before production, how you’d recover:


  Server packages and configuration.
  PuppetDB and PostgreSQL data.
  CA files and certificate state.
  Code and Hiera data deployments.
  Agent package repositories and pinned versions.
  Ruby gems and native extensions.
  External integrations that change schema or authentication behavior.


Don’t stack a Puppet major upgrade, a server OS migration, a CA rebuild, a Hiera redesign, and a module refactor into one irreversible event. Some of these can be prepared together, but their cutovers should stay separately observable and separately recoverable.

What “done” looks like

The migration is complete when:


  All production platforms use a supported target distribution and version.
  Catalogs compile without compatibility fallbacks or unexplained warnings.
  Legacy facts and Hiera 3 dependencies are gone, not just muted.
  Custom Ruby extensions are tested on the target runtimes.
  PuppetDB, reports, CA operations, deployments, and integrations are healthy.
  Performance is at least understood relative to the pre-upgrade baseline.
  The team can deploy code, diagnose failures, add agents, renew certificates, and restore the platform — without three people Googling the same Stack Overflow answer.
  Temporary compatibility settings have owners and removal dates — or are already gone.


The safest Puppet 8 migration is intentionally boring: most incompatibilities get removed while you’re still on Puppet 7, the target stack gets rehearsed before it’s real, infrastructure moves first, and agents move in cohorts small enough to actually watch. No fireworks. That’s the whole point.

If you want an environment-specific assessment, Puppet Health Check covers code, runtime, infrastructure, and upgrade readiness. For implementation and rollout, there’s Smart Puppet Support. [Shameless plug].

Alvabot
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: DevConf Tag 2</title>
    <link href="https://dev.to/betadots/devconf-tag-2-250o"/>
    <id>https://dev.to/betadots/devconf-tag-2-250o</id>
    <updated>2026-06-20T07:43:19+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Es ist 9Uhr morgens und der Kaffeestand hat mich vertröstet mit den Worten &quot;Wir sind geschlossen, Eröffnung ist 09:30Uhr&quot;&lt;/p&gt;

&lt;p&gt;Unter dem einzigen schattenspenden Baum im Hof sammeln sich die Konferenzteilnehmer.&lt;/p&gt;

&lt;p&gt;Ich habe mir einen glibc-Workshop und einen Quadlets-Workshop vorgenommen.&lt;/p&gt;

&lt;h2&gt;
  
  
  glibc-Workshop
&lt;/h2&gt;

&lt;p&gt;Bild: A duck for a bug.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8lm4wrwrwzml0vc7cbj0.jpg&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8lm4wrwrwzml0vc7cbj0.jpg&quot; alt=&quot; &quot; width=&quot;800&quot; height=&quot;601&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aufgabe war: Ich habe hier 20 einfache Bugs in glibc heraus gesucht. Wir haben hier drei Mentoren, die an der glibc gearbeitet haben. Ich werde die Bugs jetzt an die Teilnehmer versteigern und wer als erster einen Patch einreicht, gewinnt die Ente.&lt;/p&gt;

&lt;p&gt;Der Leiter scherzte noch: Mich hat die Vorbereitung acht Stunden gekostet. Wenn auch nur einer von euch dran bleibt und weiter Patches einreicht, habe ich ein beeindruckendes Verhältnis zwischen Aufwand und Nutzen erreicht. :-)&lt;br&gt;
Leider ist glibc nicht so ruhmreich wie der Kernel und hat keine Vollzeitentwickler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Podman-Quadlets Workshop
&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://codeberg.org/inknos/DevConf26/src/branch/main/demo/01_wordpress/03/README.md&quot; rel=&quot;noopener noreferrer&quot;&gt;https://codeberg.org/inknos/DevConf26/src/branch/main/demo/01_wordpress/03/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Die Demos waren schön und zeigen, wie man von Docker-Compose auf Podman kommt oder von Quadlets auf Kubernetes oder von Kubernetes auf  Quadlets. Quadlets haben Potential, wenn sie etwas Liebe erfahren. &lt;/p&gt;

&lt;p&gt;Podman Quadlets funktionieren unter Debian nicht. Dafür habe ich mit einem der RedHat-Entwickler gemeinsam einen Bug-Report erstellt. Am Workshop teilgenommen habe ich mit einer Fedora-VM.&lt;br&gt;
Der Theorie nach bringt Podman in Form von &lt;code&gt;podman machine init&lt;/code&gt; und &lt;code&gt;podman machine start&lt;/code&gt; eine VM mit, die quadletfähig sein soll, aber die ist unter Debian ebenfalls defekt.&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: DevConf: Tag 1</title>
    <link href="https://dev.to/betadots/devconf-tag-1-2bf"/>
    <id>https://dev.to/betadots/devconf-tag-1-2bf</id>
    <updated>2026-06-18T07:54:44+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Die Genese der DevConf: RedHat hat seinen europäisches Hauptquartier in Brno/Tschechien.&lt;/p&gt;

&lt;p&gt;Möchtest du life mitschauen? &lt;a href=&quot;https://www.youtube.com/@DevConfs&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.youtube.com/@DevConfs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ich möchte die vergangenen Vorträge dokumentieren:&lt;/p&gt;

&lt;p&gt;09:00 Einleitung:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wenn ihr zum ersten Mal hier seid, nehmt die Karte und probiert die Räume ohne Hilfe zu finden
&lt;a href=&quot;https://www.devconf.info/cz/map/&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.devconf.info/cz/map/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;09:30 CVE-Vortrag. RedHat hat ein Rolling-Container-Release mit AI gebaut, sie nennen es &quot;Hummingbird&quot;.&lt;br&gt;
&quot;If you have to explain it [CVEs to your manager], you have already lost&quot;&lt;br&gt;
Publikumsfrage: Was ist der Unterschied zwischen Hummingbird und Renovate? -- Beide lösen das gleiche Problem, Hummingbird benutzt Renovate im Hintergrund.&lt;/p&gt;

&lt;p&gt;10:15 bpftrace Viktor@RedHat &lt;br&gt;
Ich habe mich spontan daran erinnert, warum ich BPF-Trace verworfen habe. Es hat ein paar sehr nischige High-Level-Anwendungsfälle. Der Maintainer hat das Acknowledged und kündigt Erweiterungen an:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Macros&lt;/li&gt;
&lt;li&gt;Libraries and Imports&lt;/li&gt;
&lt;li&gt;C-Interoperation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;11:00 HTTP3 und Quick Evolution&lt;br&gt;
&lt;a href=&quot;https://www.goodreads.com/quotes/215637-simplicity-is-a-great-virtue-but-it-requires-hard-work&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.goodreads.com/quotes/215637-simplicity-is-a-great-virtue-but-it-requires-hard-work&lt;/a&gt;&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: OpenVox rootless Alpine Containers</title>
    <link href="https://dev.to/betadots/openvox-rootless-alpine-containers-1839"/>
    <id>https://dev.to/betadots/openvox-rootless-alpine-containers-1839</id>
    <updated>2026-06-12T13:42:44+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Over the last weeks, I spent a lot of my Open Source time at &lt;a href=&quot;https://www.betadots.de&quot; rel=&quot;noopener noreferrer&quot;&gt;betadots GmbH&lt;/a&gt; working on improving the OpenVox containers.&lt;/p&gt;

&lt;p&gt;The result: both &lt;code&gt;openvoxserver&lt;/code&gt; and &lt;code&gt;openvoxdb&lt;/code&gt; are now regularly published as &lt;strong&gt;Ubuntu&lt;/strong&gt; and &lt;strong&gt;Alpine&lt;/strong&gt; based container images 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  The PRs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/OpenVoxProject/container-openvoxdb/pull/88&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/OpenVoxProject/container-openvoxdb/pull/88&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/OpenVoxProject/container-openvoxserver/pull/131&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/OpenVoxProject/container-openvoxserver/pull/131&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This may sound like a small packaging change at first glance, but it actually opens the door for several different deployment styles and operational preferences.&lt;/p&gt;

&lt;p&gt;Some users prefer Ubuntu based images for compatibility, familiarity and enterprise environments. Others want the smaller Alpine footprint, faster pulls and a more minimal runtime environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I got there
&lt;/h2&gt;

&lt;p&gt;Building the Alpine containers was a significant challenge because we don’t have packages here. Building Alpine packages would have been another nightmare to solve. So, I looked into the build scripts for the Ubuntu/RHEL packages and extracted all the necessary steps to get the server running. Ideally, I thought, &quot;That’s only a JAR, how complicated can this be? Containers are made for running JARs!&quot; But I couldn’t have been further from the truth. The OpenVox JARs are not self-running and depend on a lot of files and directories being in the right place. Getting this working was a bit of a hassle! But in the end, I got all the steps together, and the containers were building and even starting! They were working! Then, I had to do it rootless. So, we figured out every place where a chmod or chown is made and got this out of the starting process. It wasn’t easy! But we solved it in the end. With much help from the community!&lt;/p&gt;

&lt;h2&gt;
  
  
  Rootless containers in detail 🥷🐳
&lt;/h2&gt;

&lt;p&gt;One particularly exciting part: the Alpine images are already fully rootless.&lt;/p&gt;

&lt;p&gt;That means they are prepared to run without root privileges inside the container, improving the security posture and aligning better with modern container best practices.&lt;/p&gt;

&lt;p&gt;The Ubuntu variants are expected to follow soon as the remaining changes are being worked on.&lt;/p&gt;

&lt;p&gt;Rootless containers are one of those things that feel a bit like upgrading from telnet to SSH. Once the ecosystem is ready for it, going back feels strange.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community powered Open Source ❤️
&lt;/h2&gt;

&lt;p&gt;Even though I implemented most of the changes during my Open Source time at &lt;a href=&quot;https://www.betadots.de&quot; rel=&quot;noopener noreferrer&quot;&gt;betadots GmbH&lt;/a&gt;, this was absolutely not a solo effort.&lt;/p&gt;

&lt;p&gt;There were reviews, discussions, testing, feedback and many tiny details coming from the OpenVox community. Open Source thrives when individuals collectively contribute their ideas, refining them until the final product surpasses the original draft.&lt;/p&gt;

&lt;p&gt;Huge thanks to everyone involved 🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Infrastructure projects are often built from many small building blocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;container images&lt;/li&gt;
&lt;li&gt;CI pipelines&lt;/li&gt;
&lt;li&gt;packaging&lt;/li&gt;
&lt;li&gt;runtime security&lt;/li&gt;
&lt;li&gt;multi-distro support&lt;/li&gt;
&lt;li&gt;rootless compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Individually, these changes may seem minor.&lt;/p&gt;

&lt;p&gt;But together they make the ecosystem more flexible, more secure and easier to integrate into different environments, from homelabs to enterprise deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;There is still more work ahead around rootless support, image hardening and ecosystem improvements, but this is another nice milestone for OpenVox containers.&lt;/p&gt;

&lt;p&gt;Feedback, testing and contributions are always welcome.&lt;/p&gt;

&lt;p&gt;Open Source is still one of the few places where random people on the internet collectively assemble giant infrastructures out of YAML, shell scripts and sleep deprivation 🤖✨&lt;/p&gt;

&lt;p&gt;Cheers, Robert&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: puppet-checkmk dusted off 🧹🦊</title>
    <link href="https://dev.to/betadots/puppet-checkmk-dusted-off-1jc"/>
    <id>https://dev.to/betadots/puppet-checkmk-dusted-off-1jc</id>
    <updated>2026-06-12T08:47:43+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;VoxPupuli&#39;s puppet-checkmk &lt;a href=&quot;https://github.com/voxpupuli/puppet-check_mk&quot; rel=&quot;noopener noreferrer&quot;&gt;[1]&lt;/a&gt;&lt;a href=&quot;https://forge.puppet.com/modules/puppet/check_mk/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;[2]&lt;/a&gt; had become invisible on the Puppet Forge. &lt;a href=&quot;https://forge.puppet.com/modules?q=check_mk&quot; rel=&quot;noopener noreferrer&quot;&gt;[3]&lt;/a&gt; The Forge has a simple filter: No updates for a long time? Hide it!&lt;/p&gt;

&lt;p&gt;Since we hadn&#39;t put out a release in a while, we got dropped off the Forge. We&#39;ve fixed that: dusted off the metadata and added modern operating systems (RedHat-10, Debian-13) to the test matrix.&lt;/p&gt;

&lt;p&gt;As sysadmins may guess, there were some quirks with EPEL. I am looking at you, Oracle Linux! ;-) &lt;a href=&quot;https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13&quot; rel=&quot;noopener noreferrer&quot;&gt;[4]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[1] &lt;a href=&quot;https://github.com/voxpupuli/puppet-check_mk&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/voxpupuli/puppet-check_mk&lt;/a&gt;&lt;br&gt;
[2] &lt;a href=&quot;https://forge.puppet.com/modules/puppet/check_mk/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;https://forge.puppet.com/modules/puppet/check_mk/readme&lt;/a&gt;&lt;br&gt;
[3] &lt;a href=&quot;https://forge.puppet.com/modules?q=check_mk&quot; rel=&quot;noopener noreferrer&quot;&gt;https://forge.puppet.com/modules?q=check_mk&lt;/a&gt;&lt;br&gt;
[4] &lt;a href=&quot;https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13&lt;/a&gt;&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: puppet-checkmk entstaubt 🧹🦊</title>
    <link href="https://dev.to/betadots/puppet-checkmk-entstaubt-5a61"/>
    <id>https://dev.to/betadots/puppet-checkmk-entstaubt-5a61</id>
    <updated>2026-06-12T08:47:42+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;VoxPupulis puppet-checkmk &lt;a href=&quot;https://github.com/voxpupuli/puppet-check_mk&quot; rel=&quot;noopener noreferrer&quot;&gt;[1]&lt;/a&gt;&lt;a href=&quot;https://forge.puppet.com/modules/puppet/check_mk/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;[2]&lt;/a&gt; war in der Puppet-Forge unsichtbar geworden. &lt;a href=&quot;https://forge.puppet.com/modules?q=check_mk&quot; rel=&quot;noopener noreferrer&quot;&gt;[3]&lt;/a&gt;&lt;br&gt;
Die Forge hat einen schlichten Filter: &amp;gt;FIXNUM Zeit kein Update erhalten? Ausblenden!&lt;/p&gt;

&lt;p&gt;Wei wir schon lange kein Release mehr veröffentlicht haben, sind wir aus der Forge gefallen. Wir haben das behoben: Metadaten entstaubt und moderne Betriebssysteme (RedHat-10, Debian-13) in die Test-Matrix eingefügt.&lt;/p&gt;

&lt;p&gt;In guter Sysadmin-Gewohnheit gab es Freude mit EPEL. Du bist gemeint, Oracle-Linux! ;-) &lt;a href=&quot;https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13&quot; rel=&quot;noopener noreferrer&quot;&gt;[4]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[1] &lt;a href=&quot;https://github.com/voxpupuli/puppet-check_mk&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/voxpupuli/puppet-check_mk&lt;/a&gt;&lt;br&gt;
[2] &lt;a href=&quot;https://forge.puppet.com/modules/puppet/check_mk/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;https://forge.puppet.com/modules/puppet/check_mk/readme&lt;/a&gt;&lt;br&gt;
[3] &lt;a href=&quot;https://forge.puppet.com/modules?q=check_mk&quot; rel=&quot;noopener noreferrer&quot;&gt;https://forge.puppet.com/modules?q=check_mk&lt;/a&gt;&lt;br&gt;
[4] &lt;a href=&quot;https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13&lt;/a&gt;&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: First Steps in the Swamp</title>
    <link href="https://example42.com/blog/2026/06/11/first-steps-in-the-swamp/"/>
    <id>https://example42.com/blog/2026/06/11/first-steps-in-the-swamp</id>
    <updated>2026-06-11T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    Edit: A few hours after the initial post.

So, what follows below, has been generated by Alvabot, an agent in the making.

Alvabot is learning and shaping, and I’m learning how to use it.

One of the worst way is the post that follows, which might be useful and readable, but it’s for a good part, a token re-digestion of https://swamp-club.com/manual/tutorials/hello-world.

So basically AI slop.

Sorry for that,

Alessandro

Original post

Day one I got muddy.
Day two I read the map.

Day three, three weeks later, is the post I would have wanted on day one: the actual commands to type, in the actual order, to go from “deterministic automation for AI agent”, whatever that means, to a working workflow with versioned data you can query.

No theory this time. Well, almost. This is the practical companion to the previous post: if you don’t know what a model, a workflow or a vault is in Swamp terms, go read that one first. We’ll wait. We have time, we’re a swamp.

One prerequisite before the commands: you need an AI agent. Swamp is designed to be operated by agents, not just used next to them. Claude Code is the assumed default (and what I use mostly), but any decent coding agent works. The point, as we’ll see, is that you mostly don’t write Swamp YAML by hand — your agent does, and Swamp gives it the rails.

Step 0 — Install

curl -fsSL https://swamp-club.com/install.sh | sh


Yes, the infamous curl-pipe-sh. We’ve all made our peace with it, or at least we pretend convincingly. Verify it landed:

swamp version


If the command is not found, restart your shell or add ~/.local/bin to your PATH. (Twenty-five years of Unix and this sentence is still in every tutorial ever written. There’s something comforting in that.)

Step 1 — Initialize a repo

Any repo. A new one, an existing project, the place where your podcast scripts live. For a first dip, make a sandbox:

mkdir my-swamp-project
cd my-swamp-project
swamp repo init


You get a glorious ASCII banner (“WELCOME TO THE CLUB — for hackers, by hackers”) and, more importantly, a scaffold:

ls -a
# .claude CLAUDE.md .gitignore models .swamp .swamp.yaml vaults workflows


Pay attention to two things here:


  .claude/ and CLAUDE.md — this is how the agent learns Swamp. The skills are auto-discovered: open Claude Code in this directory and it already knows the framework. No prompting liturgy, no copy-pasted system prompts. The framework documents itself to its own operator, which is still my favorite mildly-unsettling feature.
  .swamp/ — this is where every execution will leave typed, versioned, queryable data. The substrate. The thing that stops your agent’s work from dissolving into chat history like tears in rain.


Step 2 — Create your first model (don’t write it, ask for it)

Open Claude Code in the repo and ask, in plain language:

Create a command/shell model called hello-world that echoes &quot;Hello from the swamp!&quot;


The agent creates a YAML definition under models/command/shell/. This is the division of labor I praised on day two, now visible on disk: logic lives in TypeScript (in the model type), configuration lives in YAML (in your model definition). The agent reasons over data, not over code.

Old Puppet hands: yes, this smells exactly like the resource abstraction layer. I know. I checked my pulse too.

Step 3 — Run it

swamp model method run hello-world execute


You get your Hello from the swamp!, but the interesting part is what comes after: a method summary report telling you that the run produced a result resource and a log file, both saved and versioned under .swamp/data/. Every run. Automatically. An audit trail as a first-class citizen, not something you bolt on later when the auditors call.

Step 4 — Query the data

This is the moment it clicked for me, so don’t skip it:

swamp data query &#39;modelName == &quot;hello-world&quot;&#39;


Two artifacts, versioned. Now extract just the stdout:

swamp data query \
&#39;modelName == &quot;hello-world&quot; &amp;amp;&amp;amp; specName == &quot;result&quot;&#39; \
--select &#39;attributes.stdout&#39;


Hello from the swamp!


Those predicates are CEL expressions — the same expression language used inside model definitions and workflows to wire outputs into inputs. Learn the query syntax here, at the CLI, where mistakes are free, and you’ve learned the glue of the whole framework.

Your agent’s executions are no longer ephemeral logs. They’re a queryable database. Sit with that for a second.

Step 5 — Compose a workflow (and extend Swamp while you’re at it)

Now the real demo. Back in Claude Code, ask for something Swamp can’t do yet:

Create a new extension model type named @tutorial/random-status that randomly
returns one of a list of statuses, storing it in a resource named &quot;output&quot;
with a &quot;status&quot; property. Then create a model called weather-report that uses
it to return one of: murky, misty, gloomy, stinky and humid. Then create a
command/shell model called morning-message. Create a workflow called
swamp-morning with two jobs: a &quot;gather&quot; job that runs hello-world and
weather-report, and a &quot;combine&quot; job that depends on gather and runs
morning-message, passing the hello-world stdout and the weather-report status
into its command.


Notice what just happened in one prompt: a new extension (new capability, packaged), two new models, and a workflow — a DAG with a parallel gather job feeding a dependent combine job. Files appear under extensions/, models/ and workflows/. All reviewable, all in git, before anything runs.

Run it:

swamp workflow run swamp-morning


Hello from the swamp! The weather is gloomy - best to stay inside.


The two gather steps run in parallel, combine waits for both, every edge is typed, every node leaves versioned data behind. Query the final result like before:

swamp data query \
&#39;modelName == &quot;morning-message&quot; &amp;amp;&amp;amp; specName == &quot;result&quot;&#39; \
--select &#39;attributes.stdout&#39;


A pipeline where the second run is deterministic instead of a fresh improvisation. That’s the whole pitch, demonstrated in a sandbox in under half an hour.

Step 6 — When things go wrong (they will, it’s a swamp)

swamp doctor


Checks installation health, broken hooks, unhealthy extensions, malformed workflows, vaults, and — bless them — cleartext secrets you left lying around. Run it early, run it often. On day one this command would have saved me several hours and an indecent amount of tokens.

Where to wade next

Once the hello-world ritual is done, the actual exploration starts. My suggested order:


  Vaults. Create one, store a secret, reference it via expression in a model. Local encrypted storage to start; AWS Secrets Manager or 1Password when a team shows up. Boring, in the best possible sense.
  The extensions registry at swamp-club.com/extensions. Browse what others built before reinventing it — there’s everything from AWS primitives to Firecracker microVM management for sandboxing Claude Code itself. A package manager for agent capabilities.
  Manual approval gates. Workflows can suspend at a manual_approval step and wait for a human sign-off before touching production. The day you let an agent near real infrastructure, this is the feature that lets you sleep.
  Your own real use case. Take the thing you ask your agent to improvise more than once — podcast post-production, invoice archiving, key rotation, that messy directory you keep promising to fix — and reframe it as a workflow of typed models. The moment you do, it stops being a one-off and becomes an asset.


And read the manual.

The hands are muddier than ever. The difference is that now the mud is versioned.

We’ll keep wading.

Alvabot and Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Reading the Swamp</title>
    <link href="https://example42.com/blog/2026/05/22/reading-the-swamp/"/>
    <id>https://example42.com/blog/2026/05/22/reading-the-swamp</id>
    <updated>2026-05-22T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    Two days ago I put my hands in the Swamp and they got, predictably, very dirty.

Then I did something radical, revolutionary, somehow epic.

I read the ***ing manual.

No.

It’s a lie, I didn’t.

I forced an innocent AI to read it and summarize and explain the whole thing with the patience only AIs can have nowadays.

This post, written in 4 hands, well, two hands and a bunch of tokens, (you will never know, nor care, who wrote what), is the final public summary of such a journey into the swamp.

The map I should have read first

Day one was hands-in-the-mud. I installed the tool, pointed an agent at it, and spent several hours and an indecent amount of tokens fighting a bug which could be avoided just by using a better name. Btw, the bug has already been corrected and the fix released (thank you Paul), what a time to be alive!

Day two I climbed back onto dry land, wiped my glasses (wait, I don’t have glasses :-&amp;gt; ), and actually looked at the map.

The map, it turns out, is good. Suspiciously good. There is a whole manual on swamp.club, neatly organized into tutorials, how-to guides, reference and explanation, written in a dry, essential, effective and ultimately boring style (had to say that also here, Adam :).

The homepage line that meant absolutely nothing to me on day one — “deterministic automation for AI agents” — finally clicked. So let me try to explain it, naively, the way you explain a thing the morning after you finally got it.

What I finally understood

Here is the problem Swamp is staring at.

An AI agent is a brilliant amnesiac improviser.

Every session it re-derives the world from scratch, does something genuinely clever, and then the clever thing dissolves into the chat history.

Capabilities lost in chat, like tears in rain.

One-off scripts that rot.

Ephemeral logs nobody can query.

Credentials scattered wherever the agent felt like leaving them.

I know this dance well. I am the continuity in that dance — me, my head, and a growing pile of CLAUDE.md files.

Swamp’s bet is to give the agent a substrate instead.

A place where the work it does becomes typed, versioned, queryable, composable — and therefore survives. The agent stops winging it and starts building things.

It is, more or less, the difference between asking a consultant to solve your problem and asking them to leave behind a reusable module.

For a man who spent twenty years preaching reusability in the Puppet world, that sentence clicked on day one, but I didn’t full realized that immediately.

Six creatures living in the swamp

The framework decomposes automation into a handful of primitives.

They compose.

Of course they compose — that is the whole point — but it still made me happy to see it.

Models. Typed interfaces to anything. “Zod schemas in, validated data out.” A model is a typed interaction with an external system: it has a type — something like command/shell or @swamp/aws/ec2/vpc — methods such as create, sync, delete, and typed input and output schemas.

The detail that made me sit up: reusable logic lives in TypeScript, while configuration lives in YAML. So the agent doesn’t write code to configure things — it reasons over data.

Anyone who has agonised over “should the AI write code here, or data?” feels that one in the spine.

It is the Puppet resource abstraction layer, reborn for agents.

Workflows. Multi-step DAGs of jobs, with parallel steps, and — this is the good bit — they nest.
Steps chain data from earlier steps with CEL expressions, so the output of one job feeds the input of the next, typed all the way.

It is a pipeline, except every edge is typed and every node leaves an immutable, versioned artifact behind.

My day-one naming bug lived exactly here, in a model the loader refused to discover. Now I understand why the loader has opinions.

Vaults. Encrypted secret storage, referenced by expression, resolved at runtime rather than at planning time.

Swappable providers: local encryption for me on my own, AWS Secrets Manager or 1Password for a team.

Boring. 
I mean that as a compliment. Secret management that is boring is secret management done right.

Extensions. The thing I face-planted into on day one. Extensions package reusable components — models, vault providers, execution drivers, datastores, reports — and you publish them to a registry where they become “a full citizen immediately”.

On day one this scared me, and it still does a little.
On day two I also see it for what it is: a package manager for agent capabilities.

And no, Matteo, it’s not just yet another collection of skills, hope this post will help in clarifying what I was not able to explain correctly.

Reports. Structured analysis after every run, in Markdown and JSON.

The agent doesn’t just do the thing — it tells you what it did, in a format a human and another machine can both read.

An audit trail as a first-class citizen, not an afterthought.

Skills. Markdown documents that teach the agent how to use Swamp, loaded on demand when a trigger fires.

So the agent learns Swamp from Swamp.

The framework documents itself to its own operator.

Mildly meta. Slightly unsettling. Mostly brilliant.

Not only for infrastructures

On day one I kept asking the same nervous question: what repo? Infrastructure repo? Software repo? Any repo?

The honest answer the docs gave me is: any repeatable, multi-step thing.

Infrastructure is the obvious case — inventory, provisioning, an AMI lookup feeding an EC2 model.

Expected. 
Adam Jacob, System Initiative — of course it does infrastructure.

But the moment you stop reading “automation for AI agents” as “infrastructure” and start reading it as “anything an agent currently does ad hoc in a chat window”, the thing gets a great deal bigger.

Webframp wrote a enlightening post about this, give it a look.

Use cases are whatever you do more than once with an agent (or you should/may do with it).

Think about automated transcript generation of a podcast episode, extraction of summaries, tags, generation of posts, web pages and all the corollary stuff.

Or security operations — key rotation. Rotate an API key, update the vault, notify the team. Three steps, secrets involved, must be repeatable, must be auditable. Swamp’s home turf.

Or data remediation — a messy media library, a directory of inconsistently named files, a spreadsheet that needs reconciling. Boring, repetitive, the kind of job you hand to an agent once and can then never reproduce.

Or the dull business automations — invoicing, monthly reports, the recurring thing you always forget until it is late.

The pattern underneath all of them is the same. 
The moment you frame a task as “a workflow of typed models” instead of “a thing I ask the agent to improvise again”, it stops being a one-off and becomes an asset.

Reusability, abstraction, repeatability, traceability — my day-one sacred principles — except now they apply to what the AI does, not only to what I write by hand.

Teaching my agents to swim

This is the part I am still slightly amazed by.

Right now my working relationship with an agent is: brilliant intern, infinite enthusiasm, almost zero long-term memory (memory.md? really?). Every morning it forgets it ever met me. It re-derives, re-discovers, re-improvises, and occasionally re-breaks. It is, as I said two days ago, smart for smart users — which is a polite way of saying I carry the continuity, in my head and in config files.

Swamp moves that continuity out of my head and into a structured substrate. The agent’s work becomes:


  typed, so it can no longer lie to itself quite so cheerfully — and on day one the AIs were always reassuring me everything was fine while nothing worked;
  versioned, so I can see what actually changed;
  auditable, through reports;
  repeatable, so the second run is deterministic rather than a fresh improvisation;
  shareable, through extensions.


The agent stops being an intern who forgets everything and starts being a colleague who builds and reuses a real toolkit. That is a genuinely different way of working — and I did not expect a download-and-run-a-script tool to be the thing that handed it to me.

Conclusions

I am, clearly, hooked. The hands are still a bit muddy.

Still wondering about all the possible use cases, for the world and for myself, but Alvabot and Webframp (hope he is human, at least) are helping.

We’ll keep wading.

Alvabot and Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Hands in the Swamp</title>
    <link href="https://example42.com/blog/2026/05/20/hands-in-the-swamp/"/>
    <id>https://example42.com/blog/2026/05/20/hands-in-the-swamp</id>
    <updated>2026-05-20T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    This is weird, and I guess a sign of the weird times we live in.

A pond of beautiful entities

I’m not sure about the first time I heard about Swamp, I guess at the last CfgMgmtCamp in February directly from Adam Jacob.

If you know Adam, you know that he is a brilliant guy with crazy ideas, sometimes too ahead of their time (Ok, he also did Chef, when there was already Puppet, messing up things in our placid community, but that’s another story).

In the next months I kept on having indirect updates about Swamp from Paul Stack and his posts on LinkedIn but that remained the classic “apparently cool tool I should check out when I have time”.

That time, of course, never arrives, even if I have plenty of it.

This morning was different, I woke up earlier than usual, gave a look at a post from Paul, went to the Swamp site and wondered, once again, “What the xxxx is Swamp anyway”?

Honestly, claims like “ADAPTIVE WORKFLOWS (or DETERMINISTIC AUTOMATION) FOR AI AGENTS” didn’t really click anything in me, and it took me some time to understand the inner, and powerful, meanings.

Anyway, instructions say: install swamp with the usual “download and run a random script”.

Ok, I’m sadly familiar at performing unprotected sex with the Internet.

Then go to your repo and run swamp init and then open your agent tool.

What repo? 
Your infrastructure management repo? 
Your own software repo? 
Any repo?

So I went to my pabawi repo (btw, it has been launched today on Product Hunt, give it a look ) and wondered… now what?

This is software, not infrastructure to run things on.

I consulted a random bot, of course, at least to try to understand what Swamp can be used for in this case, and then I wondered that maybe I could use it to automate the testing of Pabawi Docker images, to begin with something.

Swamping for a single bad naming choice

So I started to put my hands in the swamp.

Needless to say they quickly became dirty.

Very dirty.

In a pure, ignorant, vibe approach, I asked Codex to do something with Swamp to automate the testing of Pabawi docker images.

The AI started to juggle with tokens, searched the Swamp Club for existing extensions and generated a workflow to test the local Dockerfile on a remote system over ssh.

I wondered why testing my local software on a remote server, when I have a local Docker running, and hence, after a few more AI mumblings and no viable existing alternative, I wondered why not making a general purpose Docker local testing extension?

My damn tendency to abstract things to make them reusable.

Here comes my trip to the rabbit hole, I just started to use Swamp and I was already trying to make a public extension.

It could have been an incredibly fast rewarding experience, but in my case, for this very specific case, it has been a (relatively) painful (in the degree of a “few hours and several tokens spent” pain scale) experience.

I struggled with conflicts from local development and published versions, issues with unknown model types, AIs always reassuring that everything was fine, while nothing was working, moving the extension to a dedicated repo, and doing it in the wrong way, then, again, issues with unknown model types.

All this via a quick burn of Codex 5 hour session credits, some additional credits bought (clear sign I was hooked in the trap), a shift to Claude code, because I didn’t want to burn too quickly Codex extra credits, and the hit of Claude limits as well, to finalize everything with Antigravity, without plan.

After several troubleshooting sessions, where the AI was apparently shooting at its own generated troubles while it should have aimed at my bad naming choices, Claude told me:

⏺ ROOT CAUSE — confirmed in Swamp&#39;s source (extension_loader.ts:1112):

  entry.isFile &amp;amp;&amp;amp; entry.name.endsWith(&quot;.ts&quot;) &amp;amp;&amp;amp; !entry.name.endsWith(&quot;_test.ts&quot;)   // test files are excluded from discovery

  The model file is named docker_image_test.ts — it ends in _test.ts, so Swamp&#39;s extension loader treats it as a test file and excludes it from discovery entirely. Swamp extension push uses the manifest&#39;s explicit list (so it bundles fine, modelCount: 1), but every consumer scans the directory, skips *_test.ts, and registers 0 models.
  The type string, paths.base, the layout — none of it ever mattered. It was the filename all along.


A bad name, a bug in upstream code, promptly reported with the insanely cool swamp issue command, some other minor tweaks, 7 releases on day one, and finally an apparently working docker-image-test extension was published.

Muddy conclusions

The good?

In the process I started to learn something about Swamp, just scratching the surface, but my mind started to be enlightened about its endless possibilities.

The bad?

The fact that anyone can publish an extension and anyone else can use it out of the box with Swamp and use it for any infrastructure or automation workflow scares me.

Feature request for Adam, Paul and whoever works in the Swamp: if you are not already doing it, do some kind of automated security assessment on what users publish on swamp.club

The unnecessary irony?

After all this effort I thought that I probably could have achieved the same “docker build and test” task in much less time with a shell script.

I’m still not sure if this specific Docker test extension makes sense, in Swamp world, but my ancient DevOps spirit keeps on reminding the sacred principles of reusability, abstraction and repeatability and the curious AI fanboy of the current times lights up at the huge powers of gated and modelled AI driven workflows.

Lessons learned?

The usual one.
AIs are smart for smart users and sometimes dumb with dumb users.

Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: สล็อตวัวโชคลาภ UFABET ทางเข้าเล่นเว็บตรง PG Soft แตกง่าย 2026</title>
    <link href="https://puppetblog.nl/fortune-ox/"/>
    <id>https://puppetblog.nl/?p=564</id>
    <updated>2026-05-06T08:10:37+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;การเลือกเดิมพันกับ &lt;a href=&quot;https://puppetblog.nl/fortune-ox/&quot;&gt;&lt;strong&gt;สล็อตวัวโชคลาภ UFABET&lt;/strong&gt;&lt;/a&gt; ในปี2026ถือเป็นทางเลือกที่ยอดเยี่ยมสำหรับผู้ที่ต้องการความมั่นคงและระบบการเล่นที่ได้มาตรฐานสากลด้วยรูปแบบตัวเกมFortuneOxที่เน้นความรวดเร็วและโบนัสแตกง่ายเมื่อรวมเข้ากับระบบของ สล็อตวัวโชคลาภ UFABET ทำให้ผู้เล่นมั่นใจได้ว่าทุกยอดการชนะจะถูกจ่ายจริงบทความนี้จะสรุปข้อมูลเชิงลึกจากคู่มือผู้เล่นอย่างเป็นทางการเพื่อให้คุณพร้อมพิชิตกำไรจากวัวทองคำตัวนี้&lt;/p&gt;
&lt;figure id=&quot;attachment_567&quot; aria-describedby=&quot;caption-attachment-567&quot; style=&quot;width: 800px&quot; class=&quot;wp-caption aligncenter&quot;&gt;&lt;img fetchpriority=&quot;high&quot; decoding=&quot;async&quot; class=&quot;size-full wp-image-567&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/05/สล็อตวัวโชคลาภ-UFABET-ทางเข้าเล่นเว็บตรง-PG-Soft-แตกง่าย-2026.webp&quot; alt=&quot;สล็อตวัวโชคลาภ UFABET ทางเข้าเล่นเว็บตรง PG Soft แตกง่าย 2026&quot; width=&quot;800&quot; height=&quot;450&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/05/สล็อตวัวโชคลาภ-UFABET-ทางเข้าเล่นเว็บตรง-PG-Soft-แตกง่าย-2026.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/05/สล็อตวัวโชคลาภ-UFABET-ทางเข้าเล่นเว็บตรง-PG-Soft-แตกง่าย-2026-768x432.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;figcaption id=&quot;caption-attachment-567&quot; class=&quot;wp-caption-text&quot;&gt;สล็อตวัวโชคลาภ UFABET ทางเข้าเล่นเว็บตรง PG Soft แตกง่าย 2026&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;ทำไมต้องเลือกเล่น สล็อตวัวโชคลาภ UFABET เว็บตรงไม่ผ่านเอเย่นต์&lt;/h2&gt;
&lt;p&gt;ความโดดเด่นของ สล็อตวัวโชคลาภ &lt;a href=&quot;https://puppetblog.nl/&quot;&gt;&lt;strong&gt;UFABET&lt;/strong&gt;&lt;/a&gt; คือการให้บริการผ่านระบบAPIแท้จากค่ายผู้พัฒนาซึ่งช่วยให้ค่าRTPและอัลกอริทึมของเกมเป็นไปอย่างโปร่งใสที่สุดการเข้าใช้งานผ่าน สล็อตวัวโชคลาภ UFABET ช่วยลดความเสี่ยงเรื่องการโดนปรับอัตราแพ้ชนะและยังมาพร้อมกับระบบฝากถอนอัตโนมัติที่เชื่อมต่อกับตัวเกมโดยตรงทำให้การเดิมพันของคุณลื่นไหลไม่มีสะดุดไม่ว่าจะเป็นช่วงเวลาที่มีผู้ใช้งานหนาแน่นก็ตาม&lt;/p&gt;
&lt;figure id=&quot;attachment_568&quot; aria-describedby=&quot;caption-attachment-568&quot; style=&quot;width: 800px&quot; class=&quot;wp-caption aligncenter&quot;&gt;&lt;img decoding=&quot;async&quot; class=&quot;size-full wp-image-568&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/05/ทำไมต้องเลือกเล่น-สล็อตวัวโชคลาภ-UFABET-เว็บตรงไม่ผ่านเอเย่นต์-1.webp&quot; alt=&quot;ทำไมต้องเลือกเล่น สล็อตวัวโชคลาภ UFABET เว็บตรงไม่ผ่านเอเย่นต์&quot; width=&quot;800&quot; height=&quot;450&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/05/ทำไมต้องเลือกเล่น-สล็อตวัวโชคลาภ-UFABET-เว็บตรงไม่ผ่านเอเย่นต์-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/05/ทำไมต้องเลือกเล่น-สล็อตวัวโชคลาภ-UFABET-เว็บตรงไม่ผ่านเอเย่นต์-1-768x432.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;figcaption id=&quot;caption-attachment-568&quot; class=&quot;wp-caption-text&quot;&gt;ทำไมต้องเลือกเล่น สล็อตวัวโชคลาภ UFABET เว็บตรงไม่ผ่านเอเย่นต์&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;เจาะลึกกติกาการเล่น สล็อตวัวโชคลาภ UFABET อ้างอิงจากระบบจริง&lt;/h2&gt;
&lt;p&gt;เพื่อให้การเดิมพันมีประสิทธิภาพผู้เล่นจำเป็นต้องเข้าใจกลไกของเกมซึ่งใน สล็อตวัวโชคลาภ UFABET มีรายละเอียดเฉพาะตัวที่น่าสนใจดังนี้:&lt;/p&gt;
&lt;h3&gt;รูปแบบเพลา 3-4-3 และเส้นไลน์เดิมพัน 10 แบบที่ควรรู้&lt;/h3&gt;
&lt;p&gt;ตัวเกมใน สล็อตวัวโชคลาภ UFABET ถูกออกแบบมาเป็นสล็อตวิดีโอที่มีความแปลกใหม่โดยมี3เพลาซึ่งเพลาที่1และ3มีจำนวน3แถวส่วนเพลาตรงกลางจะมี4แถวรองรับเส้นไลน์เดิมพันคงที่ทั้งหมด10รูปแบบการชนะจะเกิดขึ้นเมื่อมีสัญลักษณ์ที่เหมือนกันเรียงติดต่อกันจากเพลาซ้ายไปขวา&lt;/p&gt;
&lt;h3&gt;สัญลักษณ์พิเศษ Wild และอัตราการจ่ายเงินรางวัลสูงสุด 2,000x&lt;/h3&gt;
&lt;p&gt;ในระบบของ สล็อตวัวโชคลาภ UFABET สัญลักษณ์Wildคือหัวใจสำคัญเพราะสามารถใช้แทนสัญลักษณ์อื่นได้ทุกตัวหากสัญลักษณ์นี้ปรากฏขึ้นตามเส้นไลน์จะช่วยเพิ่มโอกาสชนะและหากชนะรางวัลใหญ่ที่สุดผู้เล่นมีโอกาสได้รับเงินรางวัลสูงถึง2,000เท่าของยอดเดิมพันรวม&lt;/p&gt;
&lt;figure id=&quot;attachment_565&quot; aria-describedby=&quot;caption-attachment-565&quot; style=&quot;width: 800px&quot; class=&quot;wp-caption aligncenter&quot;&gt;&lt;img decoding=&quot;async&quot; class=&quot;size-full wp-image-565&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/05/เจาะลึกกติกาการเล่น-สล็อตวัวโชคลาภ-UFABET-อ้างอิงจากระบบจริง.webp&quot; alt=&quot;เจาะลึกกติกาการเล่น สล็อตวัวโชคลาภ UFABET อ้างอิงจากระบบจริง&quot; width=&quot;800&quot; height=&quot;450&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/05/เจาะลึกกติกาการเล่น-สล็อตวัวโชคลาภ-UFABET-อ้างอิงจากระบบจริง.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/05/เจาะลึกกติกาการเล่น-สล็อตวัวโชคลาภ-UFABET-อ้างอิงจากระบบจริง-768x432.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;figcaption id=&quot;caption-attachment-565&quot; class=&quot;wp-caption-text&quot;&gt;เจาะลึกกติกาการเล่น สล็อตวัวโชคลาภ UFABET อ้างอิงจากระบบจริง&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;ฟีเจอร์เด็ดวัวแห่งโชคลาภตัวคูณ x10 ทำกำไรมหาศาล&lt;/h2&gt;
&lt;p&gt;หัวใจหลักที่ทำให้ สล็อตวัวโชคลาภ UFABET ครองใจผู้เล่นคือฟีเจอร์พิเศษที่จะสุ่มออกมามอบโบนัสก้อนโตให้กับผู้ใช้งานทุกคน:&lt;/p&gt;
&lt;h3&gt;เงื่อนไขการสุ่มเกิดฟีเจอร์หมุนซ้ำจนกว่าจะชนะรางวัล&lt;/h3&gt;
&lt;p&gt;ระหว่างการหมุนใน สล็อตวัวโชคลาภ UFABET ฟีเจอร์วัวแห่งโชคลาภอาจสุ่มเกิดขึ้นโดยที่เพลาที่1และ3จะปรากฏสัญลักษณ์ที่เหมือนกันจากนั้นเพลาทั้งหมดจะหมุนซ้ำไปเรื่อยๆจนกว่าจะมีการชนะรางวัลเกิดขึ้นซึ่งเป็นการการันตีรายได้ให้กับผู้เล่นเมื่อฟีเจอร์นี้ทำงาน&lt;/p&gt;
&lt;h3&gt;วิธีรับโบนัสคูณ 10 เมื่อสัญลักษณ์เต็มหน้าจอ&lt;/h3&gt;
&lt;p&gt;ความพิเศษสูงสุดของการเล่น สล็อตวัวโชคลาภ UFABET คือเมื่อสัญลักษณ์ใดสัญลักษณ์หนึ่งปรากฏเต็มทุกตำแหน่งบนเพลาทั้งหมดรางวัลชนะรวมในรอบนั้นจะถูกนำไปคูณด้วยx10ทันทีเปลี่ยนรางวัลธรรมดาให้กลายเป็นแจ็คพอตใหญ่ได้ในพริบตา&lt;/p&gt;
&lt;h2&gt;ตารางสรุปข้อมูลทางเทคนิคของเกม สล็อตวัวโชคลาภ UFABET&lt;/h2&gt;
&lt;p&gt;ข้อมูลสรุปสำหรับนักเดิมพันเพื่อใช้ในการวางแผนเงินทุนเมื่อเข้าใช้งาน สล็อตวัวโชคลาภ UFABET :&lt;/p&gt;
&lt;table style=&quot;height: 340px;&quot; width=&quot;565&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;รายละเอียดเทคนิค&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;ข้อมูลตัวเลข&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;ขนาดการเดิมพัน&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;0.10 &amp;#8211; 50.00 (฿)&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;ระดับการเดิมพัน&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;1 &amp;#8211; 10&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;เส้นไลน์ชนะรางวัล&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;10 Lines (Fixed)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;สัญลักษณ์พิเศษ&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;Wild Symbol&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;รางวัลสูงสุด&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;2,000x&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;คู่มือการใช้งานปุ่มควบคุมบนมือถือและคอมพิวเตอร์&lt;/h2&gt;
&lt;p&gt;การควบคุมเกมผ่าน สล็อตวัวโชคลาภ UFABET ถูกพัฒนาให้รองรับทุกอุปกรณ์อย่างสมบูรณ์แบบ:&lt;/p&gt;
&lt;ul&gt;
&lt;li style=&quot;font-weight: 400;&quot; aria-level=&quot;1&quot;&gt;ระบบมือถือ: ใช้การแตะที่ปุ่มวงกลมสีเขียวเพื่อเริ่มหมุนหรือกดที่ปุ่มTurboเพื่อเร่งความเร็วในการเล่น&lt;/li&gt;
&lt;li style=&quot;font-weight: 400;&quot; aria-level=&quot;1&quot;&gt;ระบบคอมพิวเตอร์: สามารถกดปุ่มบาร์ยาวบนคีย์บอร์ดเพื่อเริ่มหมุนหรือกดค้างไว้เพื่อหมุนต่อเนื่องรวมถึงการกดเพื่อข้ามแอนิเมชั่นแสดงผลลัพธ์ให้ไวขึ้น&lt;/li&gt;
&lt;li style=&quot;font-weight: 400;&quot; aria-level=&quot;1&quot;&gt;ข้อควรระวัง: หากเกิดข้อขัดข้องทางเทคนิคขณะเล่นการจ่ายรางวัลและการเล่นทั้งหมดใน สล็อตวัวโชคลาภ UFABET จะถือว่าเป็นโมฆะตามกฎสากล&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;อาจจะอยากดู: &lt;a href=&quot;https://puppetblog.nl/card-game/&quot;&gt;เกมไพ่ UFABET&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;สรุปความน่าเชื่อถือและเทคนิคการเล่นให้ได้เงินจริง&lt;/h2&gt;
&lt;p&gt;การทำกำไรจาก สล็อตวัวโชคลาภ UFABET ไม่ใช่เรื่องยากหากคุณเข้าใจกติกาและเลือกเล่นกับผู้ให้บริการที่มั่นคงการใช้ฟีเจอร์หมุนซ้ำและการรอจังหวะตัวคูณx10คือกลยุทธ์ที่ดีที่สุดผู้เล่นควรบริหารจัดการเงินทุนให้เหมาะสมและตรวจสอบประวัติการเล่นอย่างสม่ำเสมอเพื่อให้ทุกการเดิมพันใน สล็อตวัวโชคลาภ UFABET เป็นไปอย่างปลอดภัยและได้รับผลตอบแทนคุ้มค่าที่สุดในปี2026นี้&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: May The Source Be With You</title>
    <link href="https://overlookinfratech.com/2026/05/04/may-the-source-be-with-you/"/>
    <id>https://overlookinfratech.com/2026/05/04/may-the-source-be-with-you</id>
    <updated>2026-05-04T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;The roots of the OpenVox community are open source, and we’ve built our company
and our products on that foundation. On this fine &lt;a href=&quot;https://en.wikipedia.org/wiki/Star_Wars_Day&quot;&gt;Star Wars Day&lt;/a&gt;
I thought it would be a good time to talk a bit about why that’s important to us
and why we regard the corporate rug-pull as such a breach of trust.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/may-the-source-be-with-you.jpg&quot; alt=&quot;May the Source Be With You&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Throughout history the idea of building on the shoulders of giants is what enabled
human innovation to progress. If the countless people who came before us hadn’t already
invented or discovered steel, electricity, the wheel, integrated circuits and computing,
thermodynamics, and so on then we’d still be living in huts barely scraping by. But never
has that been more pronounced than with open source software that allows us not only
to learn from the ideas of our predecessors but to literally use their work and just
continue building with it or on top of it.&lt;/p&gt;

&lt;p&gt;And the key to continued innovation is that as we inherit a foundation from our
predecessors, we must provide for our successors to have the same opportunities to
build further than we did. It may be trite, but there’s an old proverb of unclear
origins that goes something like&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A society grows great when old folk plant trees whose shade they know they shall never sit in.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unfortunately rather than planting trees, today we see more private equity drones with
a capitalistic gleam in their eye ruthlessly plundering for the hopes of short term profit
even knowing that their actions will leave future generations with no shade to enjoy.
This is, of course, not a new tendency. In the American Reconstruction period, these people
were known as &lt;a href=&quot;https://en.wikipedia.org/wiki/Carpetbagger&quot;&gt;carpetbaggers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Starting a company with open source software is like taking out a loan against the
future. You get a free boost to your product, and all you have to do is contribute
back so the next generation does too. However, fulfilling this promise takes
more integrity than most companies can maintain once the ghouls come calling. Instead
we see corporate rug-pulls where the OSS license is summarily changed or the source
is sequestered behind a EULA, &lt;em&gt;even when &lt;a href=&quot;https://redmonk.com/rstephens/2024/08/26/software-licensing-changes-and-their-impact-on-financial-outcomes/&quot;&gt;history shows that these tricks rarely improve
financial outcomes&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Part of our strategy when starting Overlook InfraTech was to eschew investment dollars
so that we’re never beholden to outsized return-on-investment demands. Instead, our
success is measured on whether we can continue to meet customer needs and to pay our
salaries. And rather than intellectual property hoarding, we focus on open source software
and sustaining an ecosystem that enables all contributors to benefit.&lt;/p&gt;

&lt;p&gt;Without the need to exploit, we can focus on excellence &lt;a href=&quot;https://www.reclaimtheemdash.org&quot; style=&quot;text-decoration: none;&quot;&gt;–&lt;/a&gt;
both for you and for us. And so we say Happy Star Wars Day and &lt;em&gt;May The Source Be With You&lt;/em&gt;.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Pabawi Version One released</title>
    <link href="https://example42.com/blog/2026/04/14/pabawi-v-one-released/"/>
    <id>https://example42.com/blog/2026/04/14/pabawi-v-one-released</id>
    <updated>2026-04-14T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    Pabawi 1.0

Version 1.0 is out. I’ll skip the confetti.

What I will say is this: there’s a meaningful difference between a project that works and a project that’s ready. v1.0 is my answer to the question “is this ready?” — ready for teams, ready for real infrastructure, ready to do things and not just show things. Here’s what landed.

Provisioning: from observer to actor

The biggest shift in v1.0 is conceptual. Until now, Pabawi was fundamentally a read tool — it could show you your infrastructure, surface Puppet reports, run Ansible plays, connect over SSH. Useful. But passive.

Proxmox and AWS EC2 provisioning integrations change that. You can now create new nodes directly from Pabawi. Define the VM on Proxmox, spin up an EC2 instance, and have it land in your inventory, connected to your configuration management, ready to work. No context switching, no separate provisioning console.

This is the transition from “one pane of glass to see things” to “one pane of glass to do things.” It matters more than the feature list suggests.

Azure is still on the roadmap. It will follow.

Nodes’ Live Journal

At FOSDEM this year I was talking with Kris Buytaert — if you’ve been in the DevOps space long enough, you know Kris. We were somewhere between a coffee and a heated discussion about the state of classic infrastructure tooling, and he made an offhand remark that stuck: something along the lines of “you always have to go digging in four different tools to understand what happened to a node last week.”

That’s the Journal. Every node now has a rolling, chronological log of everything Pabawi knows about it: Puppet runs and their outcomes, Ansible plays, SSH commands executed, provisioning events. The full picture, one place, no archaeology required.

It sounds simple. It’s the kind of simple that takes a while to realize you needed.

Inventory: multi-source and grouped

The inventory view in v1.0 supports multiple simultaneous sources. You can pull nodes from PuppetDB, Ansible inventory, SSH discovery, and EC2 at the same time. They coexist. You see everything.

Groups are also surfaced automatically — discovered from whatever each source natively provides. Ansible inventory groups, Puppet environments, Proxmox groups: Pabawi picks them up and exposes them without any manual configuration. You see the groupings that already exist in your infrastructure, reflected back at you.

This is where “one pane of glass” stops being a tagline and starts being a description.

New website

pabawi.example42.com is live. Proper documentation, a getting started guide, architecture overview. Not a GitHub README with ambitions. If you’ve been putting off trying Pabawi because the docs were thin, now there’s no excuse.

Updated Puppet module

If you manage your infrastructure with Puppet and want to deploy Pabawi itself through it, the module at forge.puppetlabs.com/modules/example42/pabawi is updated for v1.0. Declare the class, set your parameters, done. The irony of managing a Puppet UI with Puppet is not lost on me — it’s also the right way to do it.

What’s next

The monitoring integrations (Icinga, CheckMK) are next. Bringing observability context into the same interface as configuration management is the logical next step, and it’s been on the roadmap since the beginning.

Terraform and Azure provisioning are following behind. The pattern is the same: wherever you have servers you care about, Pabawi should be able to see them and help you act on them.

Try it

git clone https://github.com/example42/pabawi
cd pabawi
./scripts/setup.sh


Or with Docker:

docker run -d \
  --name pabawi \
  -p 127.0.0.1:3000:3000 \
  -v &quot;$(pwd)/pabawi:/pabawi&quot; \
  --env-file &quot;.env&quot; \
  example42/pabawi:latest


Full docs now at pabawi.example42.com.

If Pabawi looks useful for your infrastructure, try it. Open issues for what’s missing or broken — the roadmap is driven by real feedback, not guesswork.

👉 github.com/example42/pabawi

If you find it useful, a GitHub star helps. If you want to get it running in your environment, reach out on LinkedIn — I’m still happy to do a shared-screen setup session, no strings attached.

On behalf of Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Puppet Enterprise - Neue Releases und Upgrade Zyklen</title>
    <link href="https://dev.to/betadots/puppet-enterprise-neue-releases-und-upgrade-zyklen-i13"/>
    <id>https://dev.to/betadots/puppet-enterprise-neue-releases-und-upgrade-zyklen-i13</id>
    <updated>2026-04-08T13:59:44+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Bisher gab es bei Puppet Enterprise 2 unterschiedliche Support Releases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;LTS - Long Term Support und&lt;/li&gt;
&lt;li&gt;STS - Standard (oder Short) Term Support&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Die LTS Releases hatten üblicherweise 2 bis 3 Jahre Support, bevor man ein Upgrade durchführen musste.&lt;br&gt;
Bei STS Releases bezog sich der Support nur auf das jeweils letzte Release.&lt;/p&gt;

&lt;p&gt;Mit Puppet Enterprise 2026 (&lt;a href=&quot;https://www.puppet.com/products/puppet-enterprise/support-lifecycle&quot; rel=&quot;noopener noreferrer&quot;&gt;Release ist angekündigt für August 2026&lt;/a&gt;) hat &lt;a href=&quot;https://perforce.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Perforce&lt;/a&gt; die Support Matrix überarbeitet.&lt;br&gt;
Der neue Support Zyklus wird als &lt;code&gt;Latest / Latest-1&lt;/code&gt; bezeichnet.&lt;/p&gt;

&lt;p&gt;In diesem Posting werden die wesentlichen Unterschiede dargestellt und was man in Zukunft bei der Nutzung von Puppet Enterprise berücksichtigen sollte.&lt;/p&gt;




&lt;h2&gt;
  
  
  Latest
&lt;/h2&gt;

&lt;p&gt;Mit &lt;code&gt;Latest&lt;/code&gt; wird das jeweils aktuelle Puppet Enterprise &lt;strong&gt;Major&lt;/strong&gt; Release bezeichnet.&lt;br&gt;
Aktuell ist dies das letzte Release von PE 2025.&lt;/p&gt;

&lt;p&gt;Mit dem Release von PE 2026, wird PE 2026 das &lt;code&gt;Latest&lt;/code&gt; Support Release.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Latest&lt;/code&gt; Releases haben einen vollständigen Support über einen Zeitraum von 12 Monaten.&lt;br&gt;
Innerhalb dieses Zeitraumes sind neue Features und Verbesserungen, Sicherheitsupdates und Fehlerkorrekturen im Support verfügbar.&lt;/p&gt;

&lt;p&gt;Danach gibt es in der Folge Minor Releases, die vierteljährlich zur Verfügung gestellt werden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latest-1
&lt;/h2&gt;

&lt;p&gt;Als &lt;code&gt;Latest-1&lt;/code&gt; wird das jeweilige vorherige Release bezeichnet, nachdem eine neue Major Version veröffentlicht wurde.&lt;/p&gt;

&lt;p&gt;Wenn PE 2026 released wird, wird das PE 2025 Release zu &lt;code&gt;Latest-1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Latest-1&lt;/code&gt; Releases haben einen eingeschränkten Support über einen Zeitraum von 12 Monaten.&lt;br&gt;
Innerhalb dieses Zeitraumes sind nur Sicherheitsupdates und Fehlerkorrekturen im Support verfügbar.&lt;/p&gt;

&lt;h2&gt;
  
  
  aktuelles LTS Releases
&lt;/h2&gt;

&lt;p&gt;Das aktuelle LTS Release von Puppet Enterprise ist die Version 2023.8.&lt;br&gt;
Mit dem Release von PE 2026 wird diese Version nicht mehr unterstützt.&lt;/p&gt;

&lt;p&gt;Alle anderen älteren Puppet Enterprise Releases sind nicht mehr im Support enthalten.&lt;br&gt;
Wenn eine ältere Puppet Enterprise Version als PE 2025 im Einsatz ist, sollte unbedingt ein Upgrade geplant und durchgeführt werden. Welche Upgrades möglich sind, ist in der &lt;a href=&quot;https://help.puppet.com/pe/current/topics/supported_upgrade_paths.htm&quot; rel=&quot;noopener noreferrer&quot;&gt;Dokumentation zu Puppet Enterprise Upgrades&lt;/a&gt; hinterlegt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Releases und Support Zeitraum
&lt;/h2&gt;

&lt;p&gt;Stand 08.04.2026&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PE Version&lt;/th&gt;
&lt;th&gt;Support Beginn&lt;/th&gt;
&lt;th&gt;Support Name&lt;/th&gt;
&lt;th&gt;Support Umfang&lt;/th&gt;
&lt;th&gt;Support Ende&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2023.8.x&lt;/td&gt;
&lt;td&gt;&amp;nbsp;aktuell&lt;/td&gt;
&lt;td&gt;LTS&lt;/td&gt;
&lt;td&gt;Vollständiger Support&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023.8.x&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;td&gt;LTS&lt;/td&gt;
&lt;td&gt;eingeschränkter Support&lt;/td&gt;
&lt;td&gt;02/2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;2025.x&lt;/td&gt;
&lt;td&gt;aktuell&lt;/td&gt;
&lt;td&gt;Latest&lt;/td&gt;
&lt;td&gt;Vollständiger Support&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025.x&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;td&gt;Latest-1&lt;/td&gt;
&lt;td&gt;eingeschränkter Support&lt;/td&gt;
&lt;td&gt;08/2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026.x&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;td&gt;Latest&lt;/td&gt;
&lt;td&gt;&amp;nbsp;Vollständiger Support&lt;/td&gt;
&lt;td&gt;??/2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026.x&lt;/td&gt;
&lt;td&gt;??/2027&lt;/td&gt;
&lt;td&gt;Latest-1&lt;/td&gt;
&lt;td&gt;&amp;nbsp;eingeschränkter Support&lt;/td&gt;
&lt;td&gt;??/2028&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;2027.x&lt;/td&gt;
&lt;td&gt;??/2027&lt;/td&gt;
&lt;td&gt;Latest&lt;/td&gt;
&lt;td&gt;Vollständiger Support&lt;/td&gt;
&lt;td&gt;??/2028&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2027.x&lt;/td&gt;
&lt;td&gt;??/2028&lt;/td&gt;
&lt;td&gt;Latest-1&lt;/td&gt;
&lt;td&gt;eingeschränkter Support&lt;/td&gt;
&lt;td&gt;??/2029&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2028.x&lt;/td&gt;
&lt;td&gt;??/2028&lt;/td&gt;
&lt;td&gt;&amp;nbsp;Latest&lt;/td&gt;
&lt;td&gt;&amp;nbsp;Vollständiger Support&lt;/td&gt;
&lt;td&gt;&amp;nbsp;??/2029&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;Perforce entwirrt und standardisiert die Support Zyklen.&lt;br&gt;
Mit den jährliche Major Releases und definierten Zeiträumen für den eingeschränkten Support können Kunden innerhalb von 24 Monaten ein Update durchführen, ohne den Support zu verlieren.&lt;/p&gt;

&lt;p&gt;Bei der Vorbereitung und Durchführung von Updates unterstützen wir unsere Kunden gern.&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: กาสิโน UFABET ศูนย์รวมเกมเดิมพันออนไลน์ครบวงจร มาตรฐานระดับโลก</title>
    <link href="https://puppetblog.nl/casino/"/>
    <id>https://puppetblog.nl/?p=412</id>
    <updated>2026-04-08T08:52:46+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;หากคุณกำลังมองหาประสบการณ์การเดิมพันที่สมบูรณ์แบบที่สุด &lt;strong&gt;กาสิโน&lt;/strong&gt; ออนไลน์ของ &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; คือจุดหมายปลายทางที่รวบรวมทุกความบันเทิงระดับพรีเมียมไว้ในที่เดียว เราคืออาณาจักรการเดิมพันที่ได้รับความไว้วางใจจากผู้เล่นทั่วเอเชีย ด้วยการนำเสนอเกมที่หลากหลาย เทคโนโลยีที่ทันสมัย และมาตรฐานการบริการที่โปร่งใส ทำให้ทุกวินาทีที่คุณเดิมพันกับเราคือความสนุกที่มาพร้อมกับโอกาสสร้างผลกำไรที่แท้จริง&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-454&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/กาสิโน-ufabet-1.webp&quot; alt=&quot;กาสิโน UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/กาสิโน-ufabet-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/04/กาสิโน-ufabet-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;สัมผัสประสบการณ์ กาสิโน ออนไลน์ที่เหนือระดับกับ UFABET&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;ที่ &lt;strong&gt;ยูฟ่าเบท&lt;/strong&gt; เราให้ความสำคัญกับการคัดสรรสิ่งที่ดีที่สุดให้กับสมาชิก เพื่อให้คุณได้รับประสบการณ์การเล่นที่เหมือนกับการเดินทางไปนั่งในบ่อนระดับโลก:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot; start=&quot;1&quot;&gt;
&lt;li&gt;&lt;strong&gt;ความหลากหลายของเกม:&lt;/strong&gt; ไม่ว่าคุณจะเป็นสายกลยุทธ์ที่ชอบ &lt;strong&gt;เกมไพ่&lt;/strong&gt; อย่างบาคาร่าและมังกรไทเกอร์ หรือชอบความตื่นเต้นของ &lt;strong&gt;รูเล็ต&lt;/strong&gt; และ &lt;strong&gt;ซิกโบ&lt;/strong&gt; เรามีให้เลือกครบทุกรูปแบบ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;พันธมิตรค่ายเกมยักษ์ใหญ่:&lt;/strong&gt; เราเชื่อมต่อกับค่ายเกมชั้นนำทั่วโลก อาทิ SA Gaming, Sexy Baccarat, Evolution Gaming และค่ายสล็อตดังอย่าง PG Soft เพื่อให้สมาชิกเข้าถึงซอฟต์แวร์ที่ดีที่สุด&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ระบบถ่ายทอดสดที่เสถียร:&lt;/strong&gt; ทุกเกมสดถูกส่งตรงจากสตูดิโอในต่างประเทศด้วยระบบ Streaming ความละเอียดสูง ให้คุณเดิมพันได้อย่างต่อเนื่องไม่มีสะดุด&lt;/li&gt;
&lt;/ol&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/กาสิโน-ufabet-2.webp&quot; alt=&quot;กาสิโน UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;จุดเด่นที่ทำให้ กาสิโน ของเราเป็นอันดับ 1 ในใจนักเดิมพัน&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;ความแตกต่างที่ทำให้เรายืนหนึ่งในวงการ &lt;strong&gt;กาสิโน&lt;/strong&gt; คือความมุ่งมั่นในการพัฒนาบริการ:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ความโปร่งใสตรวจสอบได้:&lt;/strong&gt; ทุกการแจกไพ่และการออกรางวัลเป็นไปตามมาตรฐานสากล สมาชิกสามารถเห็นการเคลื่อนไหวของดีลเลอร์และผลลัพธ์ได้อย่างชัดเจน&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;อินเตอร์เฟซที่ใช้งานง่าย:&lt;/strong&gt; ระบบถูกออกแบบมาให้รองรับการเล่นผ่านมือถือและคอมพิวเตอร์อย่างสมบูรณ์แบบ จัดแบ่งหมวดหมู่เกมอย่างชัดเจนเพื่อให้คุณค้นหาเกมโปรดได้ในไม่กี่วินาที&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;บริการระดับ VIP:&lt;/strong&gt; ทีมงานแอดมินของเราพร้อมดูแลและแก้ไขปัญหาให้สมาชิกตลอด 24 ชั่วโมง เพื่อให้คุณมั่นใจในทุกการเดิมพัน&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/กาสิโน-ufabet-3.webp&quot; alt=&quot;กาสิโน UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ระบบความปลอดภัยและการเงินที่มั่นใจได้ 100%&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;หัวใจสำคัญของการเล่น &lt;strong&gt;กาสิโน&lt;/strong&gt; คือความปลอดภัยของข้อมูลและการเงิน:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ระบบรักษาความปลอดภัยสูง:&lt;/strong&gt; เราใช้เทคโนโลยีการเข้ารหัสข้อมูลขั้นสูง เพื่อปกป้องข้อมูลส่วนตัวและบัญชีธนาคารของสมาชิกให้เป็นความลับสูงสุด&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ธุรกรรมอัตโนมัติ:&lt;/strong&gt; ระบบการจัดการเงินที่มีความแม่นยำสูง ช่วยให้การประมวลผลยอดเครดิตของคุณเป็นไปอย่างรวดเร็ว ไม่ว่าจะเป็นการฝากเพื่อเดิมพันหรือการถอนกำไรก้อนโต&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ประเภทบริการ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;เกมแนะนำ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;จุดเด่น&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;คาสิโนสด&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;บาคาร่า, รูเล็ต, ไฮโล&lt;/td&gt;
&lt;td&gt;ถ่ายทอดสด Real-time โปร่งใส 100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;เกมตู้ (E-Games)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;สล็อต, ยิงปลา&lt;/td&gt;
&lt;td&gt;กราฟิกสวยงาม รางวัลแจ็กพอตสูง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;เกมกระดาน&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ป๊อกเด้ง, น้ำเต้าปูปลา&lt;/td&gt;
&lt;td&gt;รูปแบบที่คุ้นเคย เล่นง่าย ได้เงินไว&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/กาสิโน-ufabet-4.webp&quot; alt=&quot;กาสิโน UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;FAQ: รวมสิ่งที่ควรรู้ก่อนเริ่มต้นเดิมพัน กาสิโน ออนไลน์&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. กาสิโนออนไลน์ของ UFABET เชื่อถือได้แค่ไหน?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; เราเป็นเว็บตรงที่มีใบอนุญาตการดำเนินงานอย่างถูกต้อง มีรากฐานทางการเงินที่มั่นคงและดำเนินธุรกิจมาอย่างยาวนาน สมาชิกจึงมั่นใจได้ว่าเราจ่ายจริงทุกยอดชนะครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. ต้องใช้ทุนเท่าไหร่ถึงจะเริ่มเล่นได้?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ระบบของเราเปิดโอกาสให้ผู้เล่นทุกระดับทุนครับ โดยหลายเกมเริ่มต้นเดิมพันขั้นต่ำเพียง 10 บาทเท่านั้น ซึ่งสามารถศึกษารายละเอียดเพิ่มเติมได้ในเมนูช่วยเหลือหรือหน้า &lt;strong&gt;FAQ&lt;/strong&gt; ของระบบครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. หากมีปัญหาเรื่องการใช้งาน ต้องทำอย่างไร?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ท่านสามารถติดต่อทีมงานซัพพอร์ตผ่านช่องทางแชทสดได้ตลอด 24 ชั่วโมง เรามีทีมงานมืออาชีพคอยสแตนบายให้ความช่วยเหลือท่านในทุกขั้นตอนครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/กาสิโน-ufabet-5.webp&quot; alt=&quot;กาสิโน UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;บทสรุป: ก้าวเข้าสู่โลกแห่งความบันเทิงที่ กาสิโน UFABET วันนี้&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;หากคุณกำลังมองหาทางเลือกที่ดีที่สุดในการเดิมพัน &lt;strong&gt;กาสิโน&lt;/strong&gt; ออนไลน์ที่ครบวงจร ปลอดภัย และมีอัตราจ่ายที่คุ้มค่า &lt;strong&gt;UFABET&lt;/strong&gt; คือคำตอบที่ใช่สำหรับคุณ เราพร้อมเป็นส่วนหนึ่งที่จะพาคุณไปสัมผัสความสนุกที่ไม่มีวันสิ้นสุดและโอกาสในการคว้ากำไรมหาศาล&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;สมัครสมาชิกวันนี้ เพื่อเริ่มต้นการเดินทางครั้งใหม่ในอาณาจักรกาสิโนที่ดีที่สุดไปพร้อมกับเราครับ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Laurent Domb Blog: Directing AI, Not Chasing It: Why Your SDLC and PDLC Must Evolve</title>
    <link href="https://blog.domb.net/?p=2921"/>
    <id>https://blog.domb.net/?p=2921</id>
    <updated>2026-03-28T12:58:20+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;em&gt;How cognitive overload is undermining the AI productivity promise, and what leaders must change in their development and product pipelines to fix it.&lt;/em&gt;&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;In the past three years, I&amp;#8217;ve worked on GenAI enablement across financial services, first as a Chief Technologist at AWS, and now as SVP for our Mastercard&amp;#8217;s Software Builders Experience program. And I keep running into the same problem. Different job families approach AI tooling through the lens of what they already know. The questions we ask are shaped by the problems we&amp;#8217;ve solved before, and our imagination for what a tool can do is bounded by the scope we&amp;#8217;re comfortable operating in. That knowledge gap isn&amp;#8217;t abstract, it directly limits how teams consume AI.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;At Mastercard, I&amp;#8217;ve been working with an incredible group of people focused on re-imagining the SDLC and PDLC. We measure developer experience through CORE4, SPACE, DORA, and other frameworks. We&amp;#8217;ve invested in giving our developers the latest tooling so they can move faster than ever. And like most of our industry peers, we introduced code assistants. The data we got back told us something we didn&amp;#8217;t expect: code was moving faster from developer to pipeline, but revert rates spiked, PR sizes ballooned, and it became clear that our SDLC and PDLC flows were not built for AI-scale development.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;Three pieces published in quick succession crystallized what I was seeing.&amp;nbsp;&lt;a href=&quot;https://x.com/francedot/status/2017858253439345092&quot;&gt;Francesco Bonacci&lt;/a&gt;&amp;nbsp;wrote about &amp;#8220;Vibe Coding Paralysis&amp;#8221;, the exhaustion of managing AI, not doing the work.&amp;nbsp;&lt;a href=&quot;https://www.bcg.com/featured-insights/the-leaders-guide-to-transforming-with-ai&quot;&gt;BCG&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;https://hbr.org/2026/03/when-using-ai-leads-to-brain-fry&quot;&gt;Harvard Business Review&lt;/a&gt;&amp;nbsp;published research quantifying &amp;#8220;AI Brain Fry&amp;#8221; across 1,488 workers. And&amp;nbsp;&lt;a href=&quot;https://www.linkedin.com/pulse/outer-loop-dead-rethinking-how-wetest-carlos-arguelles-8lwac/&quot;&gt;Carlos Arguelles at Amazon&lt;/a&gt;&amp;nbsp;declared the traditional testing outer loop dead. Each was describing a different facet of the same problem. None connected them. This article is my attempt to do that, and to propose what the SDLC and PDLC need to look like on the other side.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;To be clear, this isn&amp;#8217;t a victory lap. We haven&amp;#8217;t implemented this yet. This is a framework we&amp;#8217;re building toward, informed by what the data is already telling us and what these three pieces helped me crystallize. I&amp;#8217;m publishing it now because I think the industry needs to have this conversation before the patterns calcify.&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Paradox: AI Makes Us Frantic, Not Fast&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;AI was supposed to make us faster. Instead, it&amp;#8217;s making many of us frantic.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;Across the software industry, developers spin up multiple AI coding sessions, watch features scaffold in minutes, and end the day exhausted, not from building, but from managing the building. Product managers generate roadmap scenarios, competitive analyses, and PRDs at unprecedented speed, yet struggle to commit to a single direction. The raw capability is real. The coherent output is not.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;Bonacci&amp;#8217;s description hit a nerve: &amp;#8220;I end each day exhausted, not from the work itself, but from the managing of the work. Six worktrees open, four half-written features, two &amp;#8216;quick fixes&amp;#8217; that spawned rabbit holes, and a growing sense that I&amp;#8217;m losing the plot entirely.&amp;#8221;&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The BCG/HBR data put numbers to the feeling. Workers experiencing AI brain fry report 33% more decision fatigue, 39% more major errors, and a 39% increase in intent to quit.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The promise was amplification. The reality, for many, is fragmentation. And the root cause isn&amp;#8217;t the technology, it&amp;#8217;s that our development pipelines, role structures, and organizational incentives were designed for a world where implementation was the bottleneck. That world no longer exists. Coherence, judgment, and cognitive capacity are the new bottlenecks, and our systems haven&amp;#8217;t caught up.&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Directing vs. Chasing: The Divide That Explains Everything&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;Not everyone struggles equally with AI. Observing how different roles interact with AI tools reveals a critical distinction: some professionals naturally&amp;nbsp;&lt;strong&gt;direct&lt;/strong&gt;&amp;nbsp;AI, while others end up&amp;nbsp;&lt;strong&gt;chasing&lt;/strong&gt;&amp;nbsp;it.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Directing&lt;/strong&gt;&amp;nbsp;means approaching AI with a clear problem frame, defined constraints, and a mental model of what &amp;#8220;done&amp;#8221; looks like. The human steers; the AI executes within boundaries.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Chasing&lt;/strong&gt;&amp;nbsp;means reacting to AI output, re-prompting, re-planning, spinning up new streams, without a stable anchor point. The AI generates; the human scrambles to keep up.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;This distinction maps unevenly across three key personas in software organizations.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;The Solutions Architect: Natural Director&lt;/h3&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;Solutions Architects operate at the altitude of system coherence,decomposing problems, defining boundaries, making tradeoff decisions. When an architect engages AI, they bring constraints: &amp;#8220;Design a system that handles X throughput with Y latency, using Z technology stack, within these cost boundaries.&amp;#8221; AI explores the solution space; the architect evaluates and decides.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;Architects are the most resilient to AI-induced cognitive overload because their core value, system vision and judgment, remains firmly human. AI doesn&amp;#8217;t erode their confidence; it validates their thinking faster.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;The Software Developer: Most Vulnerable to Chasing&lt;/h3&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;Developers face the sharpest disruption. Their traditional value proposition, writing code, is the layer AI has most directly commoditized. This creates a cascade of vulnerabilities.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;The confidence spiral.&lt;/strong&gt; When AI writes most of the code, developers begin questioning their own understanding. As Bonacci describes: &amp;#8220;When something breaks, and something always breaks, you&amp;#8217;re staring at code you didn&amp;#8217;t fully write, solving a problem you didn&amp;#8217;t fully understand, in a codebase that&amp;#8217;s grown faster than your mental model of it.&amp;#8221; The response? Prompt more. Defer more. Learn less. Spiral continues.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Maximum oversight burden.&lt;/strong&gt; The BCG/HBR study found that AI oversight, not mere use, is the most cognitively taxing engagement pattern, producing 14% more mental effort, 12% more fatigue, and 19% more information overload. Developers bear this burden most directly.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Reactive parallelization.&lt;/strong&gt; The tooling actively encourages it: worktrees, background agents, parallel sessions. On paper, velocity soars. In practice, attention fragments. The research quantified the cliff: productivity gains plateau and then decline after three simultaneous AI tools.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Misaligned metrics.&lt;/strong&gt; When organizations measure lines of AI-generated code or token consumption as performance proxies, they structurally incentivize chasing, rewarding volume of AI interaction, not quality of AI-directed outcomes.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The HBR study&amp;#8217;s most telling finding about developers: &amp;#8220;The ones who are most advanced with AI start to feel blocked in progress unless they can develop critical new skills such as problem framing, analysis planning, and strategic prioritization.&amp;#8221; &lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;These are architect-level skills. Some senior developers do get there, I&amp;#8217;ve seen some amazing Senior Principal Engineers who think and operate like architects, having built that muscle across different projects, products, and scales over the course of a career. But it&amp;#8217;s the exception, not the norm, and it&amp;#8217;s certainly not a path today most organizations deliberately cultivate. It should be.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;The Product Manager: Chasing in Disguise&lt;/h3&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;Product managers occupy a deceptive position. Their role, problem framing, prioritization, outcome definition, should make them natural directors. In practice, AI introduces a unique form of chasing that is harder to detect precisely because it looks like strategic work.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;The planning loop on steroids.&lt;/strong&gt;&amp;nbsp;AI generates competitive analyses, personas, market sizing, and roadmap scenarios in minutes. Each artifact feels like progress. But the actual decision,&amp;nbsp;&lt;em&gt;what will we commit to building?&lt;/em&gt; Keeps getting deferred. Planning becomes procrastination in disguise.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;The possibility explosion.&lt;/strong&gt;&amp;nbsp;For developers, AI expands what they can build. For PMs, AI expands what they can&amp;nbsp;&lt;em&gt;envision&lt;/em&gt;. The PM equivalent of six open worktrees is six roadmap variants, each plausible, none committed to. The old forcing function, implementation was expensive, so you had to choose, is gone. Decision paralysis fills the vacuum.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Validation theater.&lt;/strong&gt;&amp;nbsp;PMs can now synthesize user feedback and build business cases at extraordinary speed. The danger: AI becomes a justification engine rather than a discovery tool, confirming decisions already made, manufacturing the appearance of rigor without the substance of genuine inquiry.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Loss of customer intuition.&lt;/strong&gt;&amp;nbsp;When AI processes all your customer signals, you stop building the gut-level understanding that comes from sitting with messy, ambiguous, human feedback. PMs know what the data says but lose the feel for the customer, the intuition that was never in the data to begin with.&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Why the Traditional SDLC and PDLC Can&amp;#8217;t Handle This&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The traditional Software Development Lifecycle was designed around a simple assumption: implementation is slow and expensive, so front-load planning to minimize rework.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;AI inverts this. Implementation is now fast and cheap. But the pipelines haven&amp;#8217;t adapted. Organizations are pouring AI-accelerated code through processes designed for human-speed development, and the seams are showing. Features ship faster than they can be validated. Code merges faster than it can be understood. Developers approve PRs for code they didn&amp;#8217;t write and don&amp;#8217;t fully comprehend, building what Bonacci calls &amp;#8220;a house of cards, each card placed by a different Claude session, each card slightly misaligned with the others.&amp;#8221; More gets built, but less coheres.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The PDLC faces a parallel failure. It assumes that deciding what to build is the hard, slow part, and that once decided, the build phase imposes natural discipline. With AI, the build phase no longer imposes discipline. PMs must create that discipline themselves, and the PDLC doesn&amp;#8217;t help them do it.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;The Validation Crisis Underneath&lt;/h3&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;There&amp;#8217;s a dimension of this breakdown that goes beyond cognition and roles, it&amp;#8217;s infrastructure. Arguelles&amp;#8217; argument about the dead outer loop crystallizes the technical crisis hiding beneath the people problem.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;For decades, software testing operated on a simple split. The inner loop, a developer&amp;#8217;s local machine, ran unit tests in seconds, keeping you in flow state. The outer loop, CI/CD pipelines, integration suites, E2E environments, ran the heavier tests that caught catastrophic bugs at the seams between components. This split worked because humans wrote code slowly enough that a multi-hour CI/CD cycle was acceptable feedback.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;AI broke this contract. Engineers generate code at an order of magnitude faster speed, but the outer loop didn&amp;#8217;t get faster. More changes per day means more runs against the same fragile infrastructure, more flaky failures, more retries, more erosion of trust.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The problem goes deeper than throughput. AI-generated code has a characteristic failure mode: today it works in isolation but fails at the seams. The code is based on the context the AI has been given, typically a single file, a single service, a single component. It passes unit tests because they validate components in isolation, which is exactly the scope the model had when generating the code. But failures surface in integration, hours later, after context is lost, after other engineers have built on top of your changes.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;This is the technical manifestation of the coherence problem. Each AI session has context for one component, not the system. And the infrastructure that should catch the misalignment, integration testing, is too slow, too flaky, and too late.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;The cognitive cost compounds.&lt;/strong&gt;&amp;nbsp;When integration feedback arrives hours later through a flaky CI/CD pipeline, the developer has already moved on to other tasks, spawned new AI sessions, built more cards on the house. The late, unreliable signal forces them to context-switch back, debug whether the failure is real or environmental, and potentially unwind work that others have already built upon. The dead outer loop isn&amp;#8217;t just a testing problem, it&amp;#8217;s a cognitive load amplifier.&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The AI-Era SDLC&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The new pipeline must expand the phases where humans direct, compress the phases where AI executes, and introduce new gates where coherence is verified.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img fetchpriority=&quot;high&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;559&quot; src=&quot;https://blog.domb.net/wp-content/uploads/sdlc-1024x559.png&quot; alt=&quot;&quot; class=&quot;wp-image-2922&quot; srcset=&quot;https://blog.domb.net/wp-content/uploads/sdlc-1024x559.png 1024w, https://blog.domb.net/wp-content/uploads/sdlc-300x164.png 300w, https://blog.domb.net/wp-content/uploads/sdlc-768x419.png 768w, https://blog.domb.net/wp-content/uploads/sdlc.png 1408w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Problem Frame&lt;/strong&gt;&amp;nbsp;becomes the most important phase. Before any AI interaction, teams must articulate: What problem are we solving? What are the constraints? What does &amp;#8220;done&amp;#8221; look like? What is explicitly out of scope? This is the &amp;#8220;directing&amp;#8221; document. Without it, everything downstream becomes reactive. With spec-driven development, where humans and AI collaborate on requirements and design documents before generating code, the problem frame also becomes the source from which validation criteria are auto-generated.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Decompose&lt;/strong&gt; becomes a first-class engineering activity. Systems are broken into bounded, well-scoped units with clear acceptance criteria. Each unit is small enough that a developer can hold it in their head, because that mental model is what prevents the confidence spiral. Critically, decomposition must also define the integration contracts between components, the seams where AI-generated code most frequently fails.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;A word of caution: the pipeline diagram above implies decomposition happens once, upstream, as a planning activity. In practice, it&amp;#8217;s iterative. You start with a decomposition, direct AI against the first unit, learn something about the integration surface you didn&amp;#8217;t anticipate, and adjust the decomposition of the next unit. The best teams treat decomposition as a living artifact, not a phase gate you pass through once. The principle matters more than the sequence, and Bonacci&amp;#8217;s version of that principle applies: &amp;#8220;The plan doesn&amp;#8217;t have to be perfect. It has to be done.&amp;#8221;&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Direct AI + Continuous Validation&lt;/strong&gt;&amp;nbsp;replaces both &amp;#8220;Implement&amp;#8221; and the traditional &amp;#8220;Test&amp;#8221; phase. Instead of generating code now and validating later, validation signals run continuously during development, in the inner loop. The developer directs AI within guardrails (maximum two to three concurrent streams, each scoped to a decomposed unit), and the pipeline provides integration and contract test signals in real time.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;This requires four infrastructure investments: reliable test environments that eliminate the &amp;#8220;is this real or environmental?&amp;#8221; question (Google uses hermetic, ephemeral environments; Uber injects test traffic into production graphs); mutation-tested validation that measures what was actually verified, not just what was touched; smart test selection that analyzes what changed and runs only relevant tests (Meta&amp;#8217;s implementation catches 99.9% of faulty changes while reducing test executions by 3x); and evaluation-based assessment that asks &amp;#8220;does this achieve what the builder intended?&amp;#8221; rather than just &amp;#8220;does it pass?&amp;#8221;&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Coherence Gate&lt;/strong&gt;&amp;nbsp;is entirely new. Before merging AI-generated code, a human must demonstrate they understand it, not just that tests pass. The gate is strengthened by the continuous validation signals: developers have real integration feedback confirming the code works at the seams, not just green unit tests in isolation.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Evaluate&lt;/strong&gt;&amp;nbsp;reflects the most profound conceptual shift: the question moves from &amp;#8220;does this code work?&amp;#8221; to &amp;#8220;does this change achieve what the builder intended and adds customer value?&amp;#8221; As systems grow more agentic and non-deterministic, the same input can legitimately produce different outputs. You need judges that assess whether a response is appropriate, and critically, know when to escalate to a human.&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The AI-Era PDLC&lt;/h2&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;559&quot; src=&quot;https://blog.domb.net/wp-content/uploads/pdlc-1024x559.png&quot; alt=&quot;&quot; class=&quot;wp-image-2923&quot; srcset=&quot;https://blog.domb.net/wp-content/uploads/pdlc-1024x559.png 1024w, https://blog.domb.net/wp-content/uploads/pdlc-300x164.png 300w, https://blog.domb.net/wp-content/uploads/pdlc-768x419.png 768w, https://blog.domb.net/wp-content/uploads/pdlc.png 1408w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Discover&lt;/strong&gt;&amp;nbsp;goes deeper, not faster. AI can synthesize a thousand support tickets in minutes. The temptation is to accelerate discovery. The right move is to go deeper, use the time savings for qualitative work AI can&amp;#8217;t do: observe users, develop intuition, sit with ambiguity. The HBR study found that using AI to eliminate toil reduces burnout by 15%. Use AI to remove the grunt work from discovery, not to skip discovery itself.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Frame&lt;/strong&gt;&amp;nbsp;demands sharper problem statements. Before any AI-assisted solution generation, the PM must articulate a tight problem frame: Who is the user? What is the pain? What is the measurable outcome? What is explicitly out of scope? This is the PM&amp;#8217;s &amp;#8220;directing&amp;#8221; artifact. Without it, the development pipeline has no anchor.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Commit&lt;/strong&gt;&amp;nbsp;is an entirely new phase, and the hardest cultural change in the AI-era PDLC. In the old world, commitment was forced by implementation cost: you couldn&amp;#8217;t build everything, so you had to choose. AI removes that forcing function. PMs must now impose commitment discipline themselves. Once a direction is chosen, alternatives are killed explicitly. No &amp;#8220;keeping option B warm.&amp;#8221;&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Constrain&lt;/strong&gt;&amp;nbsp;is also new. PMs must define the cognitive budget for a feature, not just the scope. How many concurrent AI streams should engineering run? What&amp;#8217;s the maximum PR size? What coherence checkpoints are required? This operationalizes the HBR finding that three-plus simultaneous AI tools degrade productivity. The PM doesn&amp;#8217;t just define what to build, they define the cognitive parameters of how it gets built.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Co-Direct&lt;/strong&gt;&amp;nbsp;replaces the handoff. The old &amp;#8220;throw the PRD over the wall&amp;#8221; model assumed a slow build phase that naturally surfaced misalignments. With AI generating implementations in hours, the feedback loop between PM intent and engineering output must tighten dramatically. Structured checkpoints, not constant oversight, where the PM validates that system-level direction matches the problem frame.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Coherence Check&lt;/strong&gt;&amp;nbsp;gates launch. Not &amp;#8220;does it work?&amp;#8221; but &amp;#8220;does it matter?&amp;#8221; AI-accelerated development produces more features faster. Without this gate, products accumulate capability nobody asked for, built because it was easy, not because it was needed.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Measure Outcomes, not output.&lt;/strong&gt;&amp;nbsp;Velocity, features shipped, story points, these are now inflated by AI and effectively meaningless. Measure customer outcomes (adoption, retention, satisfaction), decision quality (how often do we build right the first time?), and product coherence (does the product tell a unified story?).&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Unified Pipeline: Three Altitudes of Direction&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;When the SDLC and PDLC are overlaid, the organizational design becomes clear. Each role directs at a different altitude, and the pipeline must enforce that each altitude does its job:&lt;/p&gt;



&lt;figure class=&quot;wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex&quot;&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;558&quot; data-id=&quot;2935&quot; src=&quot;https://blog.domb.net/wp-content/uploads/edited-photo-1024x558.png&quot; alt=&quot;&quot; class=&quot;wp-image-2935&quot; srcset=&quot;https://blog.domb.net/wp-content/uploads/edited-photo-1024x558.png 1024w, https://blog.domb.net/wp-content/uploads/edited-photo-300x163.png 300w, https://blog.domb.net/wp-content/uploads/edited-photo-768x419.png 768w, https://blog.domb.net/wp-content/uploads/edited-photo.png 1380w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;
&lt;/figure&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Product Managers&lt;/strong&gt;&amp;nbsp;direct at the&amp;nbsp;&lt;strong&gt;outcome altitude&lt;/strong&gt;. They own the problem frame and the commitment. Their job is to say what needs to be true for the customer, and to say no to everything else. Their problem frame also serves as the source for evaluation criteria: the specs from which intent-based validation is generated.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Solutions Architects&lt;/strong&gt;&amp;nbsp;direct at the&amp;nbsp;&lt;strong&gt;system altitude&lt;/strong&gt;. They own the decomposition and the boundaries. Their job is to ensure that what gets built coheres as a system, regardless of how fast individual pieces are generated. They also own the integration contracts, the interface definitions where AI-generated code most frequently fails, which feed directly into the continuous validation loop.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;Developers&lt;/strong&gt;&amp;nbsp;direct at the&amp;nbsp;&lt;strong&gt;implementation altitude&lt;/strong&gt;. They own the AI interaction and the understanding. Their job is to ensure that code is not just functional but comprehended, that their mental model keeps pace with the codebase. The continuous validation loop gives them something the old outer loop never did: real-time integration feedback during development, not hours-late signals through flaky CI/CD.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;The Coherence Gate is shared.&lt;/strong&gt;&amp;nbsp;All three roles must pass it:&amp;nbsp;&lt;em&gt;Does this solve the problem?&lt;/em&gt;&amp;nbsp;(PM)&amp;nbsp;&lt;em&gt;Does this fit the system?&lt;/em&gt;&amp;nbsp;(Architect)&amp;nbsp;&lt;em&gt;Do I understand this code?&lt;/em&gt;&amp;nbsp;(Developer) This shared gate is the most important new artifact in the pipeline, backed not just by human assertion, but by evidence from reliable, mutation-tested, efficiently-selected test suites that validate both functional correctness and builder intent.&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Six Organizational Imperatives&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The pipeline changes won&amp;#8217;t work without supporting organizational shifts. Drawing from BCG&amp;#8217;s research that 70% of AI transformation effort should focus on people and processes:&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;558&quot; src=&quot;https://blog.domb.net/wp-content/uploads/Sixorgimperatives-1024x558.png&quot; alt=&quot;&quot; class=&quot;wp-image-2937&quot; srcset=&quot;https://blog.domb.net/wp-content/uploads/Sixorgimperatives-1024x558.png 1024w, https://blog.domb.net/wp-content/uploads/Sixorgimperatives-300x163.png 300w, https://blog.domb.net/wp-content/uploads/Sixorgimperatives-768x419.png 768w, https://blog.domb.net/wp-content/uploads/Sixorgimperatives.png 1380w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. Upskill for Direction, Not Just Execution.&lt;/strong&gt;&amp;nbsp;Developers need training in problem framing, decomposition, and strategic prioritization. The skills the HBR study identifies as critical for advanced AI users. This isn&amp;#8217;t about making developers into architects. It&amp;#8217;s about ensuring every role can direct at their altitude rather than defaulting to chasing.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. Establish AI Spans of Control.&lt;/strong&gt;&amp;nbsp;Just as organizations define spans of control for managing humans, they must define limits for human-AI oversight. The research is clear: productivity declines after three simultaneous AI tools. Make this a policy, not a suggestion.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. Shift Metrics from Activity to Impact.&lt;/strong&gt;&amp;nbsp;Incentivizing token consumption, lines of AI-generated code, or raw velocity produces waste, low-quality work, and unnecessary mental strain. Start from business objectives with measurable outcomes. And don&amp;#8217;t rush to backfill work recently automated by an innovative team member, doing so immediately feels punitive and kills further innovation.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;4. Make Managers Accountable for AI Enablement.&lt;/strong&gt;&amp;nbsp;The HBR data is unambiguous: workers whose managers actively answer their questions about AI had 15% lower mental fatigue scores. When managers expect employees to &amp;#8220;figure it out on their own,&amp;#8221; there&amp;#8217;s a measurable 5% increase in fatigue, a small but real &amp;#8220;AI orphan tax.&amp;#8221; Manager engagement with AI isn&amp;#8217;t optional; it&amp;#8217;s a direct lever on team cognitive health.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;5. Protect Cognitive Capacity as a Strategic Resource.&lt;/strong&gt;&amp;nbsp;Mental fatigue is more likely to go undetected than burnout in existing workplace surveys. Organizations must evolve people analytics to monitor cognitive load as a novel occupational risk. Build recovery time, deliberate non-AI thinking time, into sprint cadences, because sometimes the thinking is the work.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;6. Invest in Continuous Validation Infrastructure.&lt;/strong&gt;&amp;nbsp;Every hour spent waiting for CI/CD, every flaky test debugged, every context-switch back to code you&amp;#8217;ve already mentally left compounds the brain fry problem. Organizations must invest in reliable test infrastructure, meaningful validation, efficient execution, and evaluation-based assessment. This is not optional infrastructure modernization, it&amp;#8217;s a prerequisite for every other change in this article. You cannot direct AI effectively when your validation signals arrive too late to act on.&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Bottom Line&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The developer disadvantage isn&amp;#8217;t about capability, it&amp;#8217;s about operating altitude. Architects naturally work at the altitude where AI is directed. Developers were trained to work at the altitude where AI now operates. Product managers sit at the right altitude but lack the organizational structures to enforce the commitment discipline that AI demands.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The fix isn&amp;#8217;t to transform every developer into an architect or every PM into a strategist. It&amp;#8217;s to retool the SDLC and PDLC so that every role spends more time directing and the pipelines themselves enforce the guardrails that prevent chasing.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The organizations that will thrive in the AI era aren&amp;#8217;t the ones that adopt AI fastest. They&amp;#8217;re the ones that learn to direct it most coherently, across every role, at every altitude, through pipelines purpose-built for a world where the bottleneck is no longer building, but knowing what to build and why.&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;The tools are new. The failure modes are new. But the solution is ancient: discipline, focus, and the courage to commit, now encoded into the systems we use to build.&lt;/p&gt;



&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot;/&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;em&gt;This is my practitioner&amp;#8217;s view, shaped by what I&amp;#8217;ve seen across financial services since the rise of GenAI and what I am learning at Mastercard in real time. I&amp;#8217;d love to hear whether this resonates with what you&amp;#8217;re experiencing. Are your teams directing AI or chasing it? Has your SDLC held up, or are you seeing the same cracks? Drop a comment or share your story, the more perspectives we bring to this, the faster we all figure it out.&lt;/em&gt;&lt;/p&gt;
  </content>
    <author>
      <name>Laurent Domb Blog</name>
      <uri>http://blog.domb.net</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: How to keep your Puppet modules up to date with Renovate</title>
    <link href="https://dev.to/betadots/how-to-keep-your-puppet-modules-up-to-date-with-renovate-19jn"/>
    <id>https://dev.to/betadots/how-to-keep-your-puppet-modules-up-to-date-with-renovate-19jn</id>
    <updated>2026-03-25T16:14:50+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Managing a Puppet control repository with a growing list of dependencies can quickly become a challenge.&lt;br&gt;
Keeping everything up to date manually?&lt;br&gt;
That’s not just tedious — it’s also error-prone.&lt;/p&gt;

&lt;p&gt;Modules are updated frequently, sometimes daily.&lt;br&gt;
With a long list of dependencies, it’s easy to miss important updates.&lt;br&gt;
And let’s be honest: humans are not exactly known for perfect consistency when it comes to repetitive tasks.&lt;/p&gt;

&lt;p&gt;So why not let a bot handle it?&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Renovate?
&lt;/h2&gt;

&lt;p&gt;Renovate is designed to take over exactly this kind of work.&lt;br&gt;
It continuously scans your repositories, detects dependency updates, and automatically creates merge requests for you — including changelogs.&lt;/p&gt;

&lt;p&gt;Instead of manually checking versions, you simply review and merge PRs.&lt;br&gt;
Much cleaner.&lt;br&gt;
Much safer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;p&gt;Before we get started, this setup assumes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your repositories are hosted on GitLab&lt;/li&gt;
&lt;li&gt;You are using GitLab CI with runners&lt;/li&gt;
&lt;li&gt;You can run container-based jobs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setting Up Renovate
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a Renovate Runner Repository
&lt;/h3&gt;

&lt;p&gt;Create a dedicated repository in GitLab, for example: renovate-runner&lt;/p&gt;

&lt;p&gt;Inside this repository, create a &lt;code&gt;config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;autodiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;dependencyDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// We only enable the puppet manager here&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// see all managers: https://docs.renovatebot.com/modules/manager/&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enabledManagers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;puppet&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// GitLab API - update with your instance URL&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://gitlab.example.com/api/v4/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;config:base&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:semanticCommits&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:semanticCommitTypeAll(chore)&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;renovate&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;gitlab&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;prCreation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;immediate&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;prHourlyLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;repositories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;puppet/control-repo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;requireConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;reviewers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@rwaffen&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RENOVATE_TOKEN&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration will create one merge request per dependency update.&lt;/p&gt;

&lt;p&gt;If you prefer grouped updates, you can extend it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;packageRules&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchManagers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;puppet&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;groupName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{{manager}}&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add GitLab CI Configuration
&lt;/h3&gt;

&lt;p&gt;Create a .gitlab-ci.yml:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;run_renovate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ghcr.io/voxpupuli/renovate:latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;entrypoint&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;resource_group&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;renovate $RENOVATE_EXTRA_FLAGS&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;schedules&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;triggers&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# variables:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#   LOG_LEVEL: debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running Renovate
&lt;/h2&gt;

&lt;p&gt;With everything in place, you can trigger a pipeline in your &lt;code&gt;renovate-runner&lt;/code&gt; repository.&lt;/p&gt;

&lt;p&gt;Once started, Renovate will take over and handle the full update cycle for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It launches the Renovate container&lt;/li&gt;
&lt;li&gt;Authenticates using your configured token&lt;/li&gt;
&lt;li&gt;Scans the defined repositories&lt;/li&gt;
&lt;li&gt;Detects dependency files such as &lt;code&gt;Puppetfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resolves current versions and checks for new releases or tags&lt;/li&gt;
&lt;li&gt;Creates merge requests whenever updates are available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, your role shifts from &lt;em&gt;maintainer&lt;/em&gt; to &lt;em&gt;reviewer&lt;/em&gt;:&lt;br&gt;
you simply go through the generated merge requests and decide what to merge.&lt;/p&gt;

&lt;p&gt;To make this process truly hands-off, you should also configure a scheduled pipeline in GitLab.&lt;br&gt;
This ensures Renovate runs regularly and keeps your dependencies continuously up to date — without any manual triggering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication: &lt;code&gt;RENOVATE_TOKEN&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Renovate requires a Personal Access Token (&lt;code&gt;RENOVATE_TOKEN&lt;/code&gt;) to interact with your repositories.&lt;/p&gt;

&lt;p&gt;Required permissions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;api&lt;/li&gt;
&lt;li&gt;write_repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is highly recommended to create this as a group access token, so Renovate can access all relevant repositories — especially important if your Puppetfile includes Git-based modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Container Image
&lt;/h2&gt;

&lt;p&gt;Renovate provides official container images, but they come with some trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large size&lt;/li&gt;
&lt;li&gt;Based on Ubuntu&lt;/li&gt;
&lt;li&gt;Higher number of known vulnerabilities (CVEs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vox Pupuli provides a leaner alternative:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller footprint&lt;/li&gt;
&lt;li&gt;Based on Alpine&lt;/li&gt;
&lt;li&gt;Significantly fewer vulnerabilities
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;IMAGE                                 ID             DISK USAGE   CONTENT SIZE   EXTRA
ghcr.io/renovatebot/renovate:latest   0334065a0093       1.87GB          416MB
ghcr.io/voxpupuli/renovate:latest     e354b2af781c        726MB          132MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vulnerability Scan (grype)
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;grype ghcr.io/renovatebot/renovate:latest

 ✔ Scanned for vulnerabilities     [297 vulnerability matches]
   ├── by severity: 1 critical, 10 high, 971 medium, 137 low, 15 negligible
   └── by status:   14 fixed, 1120 not-fixed, 837 ignored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;grype ghcr.io/voxpupuli/renovate:latest

 ✔ Scanned for vulnerabilities     [19 vulnerability matches]
   ├── by severity: 0 critical, 15 high, 4 medium, 0 low, 0 negligible
   └── by status:   13 fixed, 6 not-fixed, 0 ignored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;With Renovate in place, dependency management becomes predictable and automated.&lt;br&gt;
Instead of chasing updates manually, you get a steady stream of structured merge requests — complete with context and changelogs.&lt;br&gt;
In other words: less chaos, more control.&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: ไฮโล UFABET แทงไฮโลออนไลน์พื้นบ้าน อัตราจ่ายสูง จบไว ได้เงินจริง</title>
    <link href="https://puppetblog.nl/hilo/"/>
    <id>https://puppetblog.nl/?p=64</id>
    <updated>2026-03-17T04:20:45+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;หากพูดถึงเกมเดิมพันที่อยู่คู่กับสังคมไทยมาอย่างยาวนาน &lt;strong&gt;ไฮโล (Hilo)&lt;/strong&gt; คือชื่อแรกที่ทุกคนนึกถึง และในวันนี้ &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; ได้ยกระดับเกมพื้นบ้านนี้เข้าสู่ระบบออนไลน์ที่ทันสมัยที่สุด คุณไม่จำเป็นต้องไปหาที่เล่นตามงานวัดหรือบ่อนเถื่อนอีกต่อไป เพราะเรายกโต๊ะไฮโลมาตรฐานสากลมาไว้บนมือคุณ พร้อมดีลเลอร์สดที่ทำการเขย่าลูกเต๋าให้เห็นกันแบบวินาทีต่อวินาที มั่นใจได้ในความโปร่งใสและผลตอบแทนที่สูงกว่าที่เคยสัมผัส&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-460&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ไฮโล-ufabet-1.webp&quot; alt=&quot;ไฮโล UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ไฮโล-ufabet-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/03/ไฮโล-ufabet-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;สนุกกับ ไฮโล ออนไลน์ที่ UFABET แตกต่างจากที่อื่นอย่างไร?&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเลือกเล่น &lt;strong&gt;ไฮโล&lt;/strong&gt; กับยูฟ่าเบทมอบประสบการณ์ที่เหนือชั้นกว่าการเล่นทั่วไป:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot; start=&quot;1&quot;&gt;
&lt;li&gt;&lt;strong&gt;ความโปร่งใสที่ตรวจสอบได้:&lt;/strong&gt; เราใช้โหลแก้วใสในการเขย่าลูกเต๋า และมีการถ่ายทอดสดแบบ Real-time มุมกล้องคมชัด เห็นลูกเต๋าชัดเจนทุกเม็ด ป้องกันปัญหาการใช้แม่เหล็กหรือการต้มตุ๋นทุกรูปแบบ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ตัวเลือกการเดิมพันที่หลากหลาย:&lt;/strong&gt; นอกจาก สูง-ต่ำ หรือ เต็ง-โต๊ด แบบเดิมๆ แล้ว ในระบบออนไลน์ยังมีตัวเลือกการแทงแบบระบุแต้มรวม หรือแทงตองที่มีอัตราจ่ายสูงถึง 150 เท่า!&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ไม่มีการหักค่าน้ำเยอะ:&lt;/strong&gt; อัตราจ่ายของเราเป็นธรรมและให้ราคาดีที่สุด ช่วยให้ผู้เล่นได้รับกำไรแบบเต็มเม็ดเต็มหน่วย&lt;/li&gt;
&lt;/ol&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ไฮโล-ufabet-2.webp&quot; alt=&quot;ไฮโล UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;วิธีการเล่น ไฮโล เบื้องต้นและการทำกำไรจากตัวเลข&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;สำหรับมือใหม่ที่ต้องการเริ่มต้นทำกำไรจากเกมลูกเต๋า กติกาพื้นฐานนั้นง่ายมาก:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;การแทง สูง-ต่ำ:&lt;/strong&gt; เป็นที่นิยมที่สุด โดยแต้มรวม 3-10 คือ &amp;#8220;ต่ำ&amp;#8221; และ 11-18 คือ &amp;#8220;สูง&amp;#8221;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;การแทงเต็งเลข:&lt;/strong&gt; คือการทายว่าลูกเต๋า 1 ใน 3 ลูกจะออกเลขอะไร (1-6)&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;การแทงโต๊ด:&lt;/strong&gt; คือการทายเลข 2 ตัวที่จะออกพร้อมกันในตานั้นๆ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เทคนิคการวิเคราะห์:&lt;/strong&gt; ในหน้าจอเดิมพันจะมี &amp;#8220;ตารางสถิติย้อนหลัง&amp;#8221; ให้คุณได้วิเคราะห์แนวโน้มของแต้มในรอบที่ผ่านมา เพื่อช่วยในการตัดสินใจวางเดิมพันในตาถัดไป&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;รูปแบบการแทง&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;อัตราจ่าย (เท่า)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ความน่าจะเป็น&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;สูง &amp;#8211; ต่ำ&lt;/td&gt;
&lt;td&gt;1 : 1&lt;/td&gt;
&lt;td&gt;สูงมาก&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;เต็งเลข&lt;/td&gt;
&lt;td&gt;1 : 1 (ต่อลูก)&lt;/td&gt;
&lt;td&gt;ปานกลาง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;แทงโต๊ด (เลขคู่)&lt;/td&gt;
&lt;td&gt;1 : 5&lt;/td&gt;
&lt;td&gt;ปานกลาง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;แทงตอง (ระบุเลข)&lt;/td&gt;
&lt;td&gt;1 : 150&lt;/td&gt;
&lt;td&gt;ต่ำ (รางวัลใหญ่)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ไฮโล-ufabet-3.webp&quot; alt=&quot;ไฮโล UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ข้อดีของการเล่น ไฮโล ผ่านระบบคาสิโนสด&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเล่น &lt;strong&gt;ไฮโล&lt;/strong&gt; ในหมวดคาสิโนสดของ &lt;strong&gt;UFABET&lt;/strong&gt; ช่วยเพิ่มอรรถรสในการเดิมพัน:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;บรรยากาศจริง:&lt;/strong&gt; คุณจะได้เห็นดีลเลอร์ตัวจริงทำการเขย่าลูกเต๋า ได้ยินเสียง และเห็นภาพสดๆ จากคาสิโน&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เปิดให้บริการ 24 ชม.:&lt;/strong&gt; ไม่ว่าคุณจะอยากลุ้นตอนไหน เพียงแค่ &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; ก็สามารถร่วมสนุกได้ทันที&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ระบบการเงินมั่นคง:&lt;/strong&gt; เมื่อชนะเดิมพัน ยอดเงินจะถูกปรับเข้าบัญชีเครดิตของคุณโดยอัตโนมัติ และสามารถทำรายการถอนได้ทันทีผ่านระบบออโต้&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ไฮโล-ufabet-4.webp&quot; alt=&quot;ไฮโล UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;FAQ: รวมคำถามที่พบบ่อยสำหรับนักเดิมพัน ไฮโล&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. แทงไฮโลออนไลน์ขั้นต่ำกี่บาท?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ที่ &lt;strong&gt;UFABET&lt;/strong&gt; คุณสามารถเริ่มต้นวางเดิมพันไฮโลขั้นต่ำเพียง 10 บาทเท่านั้นครับ เหมาะสำหรับทั้งผู้เล่นที่ต้องการทดลองสูตรและผู้เล่นมืออาชีพ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. ผลลัพธ์ลูกเต๋าเชื่อถือได้แค่ไหน?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ระบบของเราได้รับมาตรฐานสากล มีการตรวจสอบอุปกรณ์ทุกรอบ และเป็นการถ่ายทอดสดจากบ่อนจริง ท่านสามารถตรวจสอบความโปร่งใสได้จากหน้าจอเดิมพันตลอดเวลาครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. มีสูตรไฮโลแจกฟรีไหม?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ในหน้าเว็บไซต์ของเรามีบทความแนะนำเทคนิคการเดินเงินและการอ่านสถิติเบื้องต้นให้สมาชิกได้ศึกษาครับ รายละเอียดเพิ่มเติมสามารถดูได้ที่ส่วน &lt;strong&gt;FAQ&lt;/strong&gt; หรือบทความแนะนำกลยุทธ์ครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ไฮโล-ufabet-5.webp&quot; alt=&quot;ไฮโล UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;บทสรุป: สัมผัสความมันส์ระดับตำนานไปกับ ไฮโล UFABET&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เปลี่ยนเกมพื้นบ้านให้กลายเป็นโอกาสสร้างรายได้ที่มั่นคงไปกับ &lt;strong&gt;ไฮโล&lt;/strong&gt; ออนไลน์บนเว็บตรง &lt;strong&gt;UFABET&lt;/strong&gt; ด้วยระบบที่ทันสมัย ปลอดภัย และจ่ายเงินจริง 100% ทำให้เราเป็นอันดับ 1 ในใจนักเดิมพันชาวไทยเสมอมา&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;สมัครสมาชิกวันนี้ เพื่อร่วมลุ้นระทึกไปกับเสียงลูกเต๋าและคว้าเงินรางวัลก้อนโตไปพร้อมกับเราครับ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: บาคาร่า UFABET เว็บตรงอันดับ 1 รวมค่ายดัง เล่นง่าย ถอนได้จริง</title>
    <link href="https://puppetblog.nl/baccarat/"/>
    <id>https://puppetblog.nl/?p=65</id>
    <updated>2026-03-17T04:20:23+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;หากพูดถึงเกมที่เป็นราชาแห่ง &lt;strong&gt;กาสิโน&lt;/strong&gt; คงหนีไม่พ้น &lt;strong&gt;บาคาร่า (Baccarat)&lt;/strong&gt; เกมไพ่ที่ได้รับความนิยมสูงสุดทั่วโลก ด้วยรูปแบบการเล่นที่รวดเร็ว เข้าใจง่าย และมีโอกาสชนะสูง ที่ &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; เราได้รวบรวมโต๊ะบาคาร่าจากค่ายชั้นนำระดับโลกมาไว้ให้บริการสมาชิกในรูปแบบ &amp;#8220;คาสิโนสด&amp;#8221; (Live Casino) ที่ส่งตรงบรรยากาศจริงจากบ่อนกาสิโนมาถึงหน้าจอคุณ มั่นใจได้ในความโปร่งใสและระบบการเงินที่มั่นคงที่สุด&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-449&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/บาคาร่า-ufabet-1.webp&quot; alt=&quot;บาคาร่า UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/บาคาร่า-ufabet-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/03/บาคาร่า-ufabet-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ทำไมต้องเล่น บาคาร่า กับ UFABET? ประสบการณ์ที่เหนือกว่าใคร&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเลือกเล่นบาคาร่าออนไลน์กับเว็บตรงอย่าง &lt;strong&gt;ยูฟ่าเบท&lt;/strong&gt; มอบความได้เปรียบให้คุณในหลายด้าน:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot; start=&quot;1&quot;&gt;
&lt;li&gt;&lt;strong&gt;รวมค่ายยักษ์ใหญ่:&lt;/strong&gt; เรามีพาร์ทเนอร์ระดับโลกอย่าง &lt;strong&gt;SA Gaming, Sexy Baccarat, Evolution Gaming&lt;/strong&gt; และ &lt;strong&gt;Venus Casino&lt;/strong&gt; ให้คุณสลับเปลี่ยนบรรยากาศการเล่นได้ไม่มีเบื่อ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ระบบถ่ายทอดสดที่เสถียรที่สุด:&lt;/strong&gt; สัญญาณภาพคมชัดระดับ Full HD เห็นทุกรายละเอียดการแจกไพ่ชัดเจน ไม่มีภาพสะดุดหรือดีเลย์&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ค่าคอมมิชชั่นสูง:&lt;/strong&gt; สมาชิกจะได้รับค่าคอมมิชชั่นคืน 0.7% ในทุกยอดการวางเดิมพันบาคาร่า ซึ่งถือเป็นเรทที่สูงที่สุดในตลาด&lt;/li&gt;
&lt;/ol&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/บาคาร่า-ufabet-2.webp&quot; alt=&quot;บาคาร่า UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;จุดเด่นของเกม บาคาร่า ออนไลน์: สนุก ลุ้นระทึก ทุกวินาที&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เสน่ห์ของ &lt;strong&gt;บาคาร่า&lt;/strong&gt; คือความง่ายที่ไม่ซับซ้อน แต่แฝงไปด้วยความตื่นเต้น:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;กติกาเข้าใจง่าย:&lt;/strong&gt; เพียงเลือกเดิมพันระหว่าง ฝั่งผู้เล่น (Player), ฝั่งเจ้ามือ (Banker) หรือ เสมอ (Tie) ใครมีแต้มใกล้เคียง 9 มากที่สุดจะเป็นผู้ชนะ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เดิมพันขั้นต่ำเพียง 10 บาท:&lt;/strong&gt; ไม่ว่าจะมีทุนเท่าไหร่ก็สามารถร่วมสนุกและปั้นผลกำไรได้จริง&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ความรวดเร็ว:&lt;/strong&gt; ในแต่ละตาใช้เวลาเพียง 30-45 วินาที ทำให้คุณสามารถทำกำไรได้อย่างรวดเร็วในเวลาสั้นๆ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/บาคาร่า-ufabet-3.webp&quot; alt=&quot;บาคาร่า UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;เทคนิคการเล่น บาคาร่า เบื้องต้นสำหรับมือใหม่&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เพื่อให้การเล่นบาคาร่าของคุณมีโอกาสชนะมากขึ้น ควรศึกษาแนวทางเบื้องต้นดังนี้:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;การอ่านเค้าไพ่:&lt;/strong&gt; สังเกตสถิติย้อนหลังในตาราง (Roadmap) เพื่อดูแนวโน้มว่าไพ่ออกมาในลักษณะใด เช่น เค้าไพ่มังกร หรือเค้าไพ่ปิงปอง&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;การบริหารเงินทุน:&lt;/strong&gt; ควรกำหนดงบประมาณในการเล่นในแต่ละวัน และกำหนดเป้าหมายกำไรที่ต้องการ เมื่อได้ตามเป้าแล้วควรหยุดพักเพื่อรักษาเงินกำไรไว้&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เลือกฝั่งเจ้ามือ:&lt;/strong&gt; ในระยะยาว การเดิมพันฝั่งเจ้ามือ (Banker) มีค่า House Edge ที่ต่ำกว่าฝั่งผู้เล่นเล็กน้อย (แม้จะมีค่าตง 5% ก็ตาม)&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ค่ายเกมแนะนำ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;จุดเด่น&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;สไตล์ดีลเลอร์&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sexy Baccarat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;มีความสนุกสนาน เป็นกันเอง&lt;/td&gt;
&lt;td&gt;ดีลเลอร์สาวสวยสวมบิกินี่&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SA Gaming&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;มาตรฐานสูง ระบบสากล&lt;/td&gt;
&lt;td&gt;ดีลเลอร์มืออาชีพ ชุดสุภาพ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Evolution Gaming&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;มีมุมกล้องที่หลากหลาย 4K&lt;/td&gt;
&lt;td&gt;สไตล์ยุโรป หรูหรา&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;FAQ: รวมคำถามที่พบบ่อยเกี่ยวกับ บาคาร่า ออนไลน์&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. บาคาร่าออนไลน์เล่นแล้วได้เงินจริงไหม?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ได้เงินจริงแน่นอนครับ หากคุณเลือกเล่นกับ &lt;strong&gt;UFABET&lt;/strong&gt; เว็บตรงที่ไม่ผ่านเอเย่นต์ เรามีความมั่นคงทางการเงินสูง ชนะหลักแสนหรือหลักล้านก็ถอนได้ทันที&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. สูตรบาคาร่าที่มีแจกทั่วไปใช้ได้จริงหรือเปล่า?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; สูตรหรือโปรแกรมคำนวณเป็นเพียงตัวช่วยในการตัดสินใจจากสถิติครับ การเล่นที่ได้ผลที่สุดคือการมีสติและการอ่านเค้าไพ่ด้วยตัวเองประกอบกับการเดินเงินที่เหมาะสม&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. สามารถทดลองเล่น บาคาร่า ก่อนได้ไหม?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ทางเรามีระบบทดลองเล่นในบางค่าย หรือคุณสามารถเข้าไปดูบรรยากาศในห้องเดิมพันจริงก่อนวางเงินเดิมพันได้ครับ รายละเอียดเพิ่มเติมสามารถสอบถามแอดมินหรือดูที่หน้า &lt;strong&gt;FAQ&lt;/strong&gt; ของระบบได้เลย&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/บาคาร่า-ufabet-4.webp&quot; alt=&quot;บาคาร่า UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;บทสรุป: มั่นใจทุกการวางเดิมพัน บาคาร่า ไปกับ UFABET&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;หากคุณกำลังมองหาเว็บสำหรับเล่น &lt;strong&gt;บาคาร่า&lt;/strong&gt; ที่ปลอดภัย ภาพสวย และจ่ายเงินจริง &lt;strong&gt;UFABET&lt;/strong&gt; คือคำตอบที่ดีที่สุด เราพร้อมให้บริการคุณด้วยมาตรฐานระดับโลกตลอด 24 ชั่วโมง&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เริ่มต้นความรวยวันนี้ สมัครสมาชิกกับเราและสัมผัสความสนุกของบาคาร่าที่ดีที่สุดในเอเชียไปพร้อมกับเราครับ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: หวยออนไลน์ UFABET แทงง่าย จ่ายหนัก อัตราจ่ายสูงที่สุดในไทย</title>
    <link href="https://puppetblog.nl/lottery/"/>
    <id>https://puppetblog.nl/?p=66</id>
    <updated>2026-03-17T04:20:04+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;สำหรับนักเสี่ยงโชคที่กำลังมองหาช่องทางในการซื้อหวยที่คุ้มค่าและปลอดภัยที่สุด &lt;strong&gt;หวยออนไลน์&lt;/strong&gt; บนแพลตฟอร์ม &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; คือคำตอบที่คุณต้องการ เราให้บริการรับแทงหวยครบวงจรด้วยระบบที่ทันสมัย ใช้งานง่ายผ่านมือถือ และที่สำคัญที่สุดคือเรามีอัตราการจ่ายที่สูงกว่าหวยใต้ดินทั่วไป พร้อมระบบการจัดการที่มีความมั่นคงทางการเงินระดับสากล ทำให้คุณมั่นใจได้ว่าทุกตัวเลขที่คุณแทงจะได้รับค่าตอบแทนที่ยุติธรรมและจ่ายจริงทุกบาททุกสตางค์&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-444&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/หวยออนไลน์-ufabet-1.webp&quot; alt=&quot;หวยออนไลน์ UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/หวยออนไลน์-ufabet-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/03/หวยออนไลน์-ufabet-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ทำไมต้องแทง หวยออนไลน์ กับ UFABET? มั่นคง ปลอดภัย 100%&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การซื้อหวยผ่านระบบออนไลน์ของ &lt;strong&gt;ยูฟ่าเบท&lt;/strong&gt; มีข้อดีที่เหนือกว่าการซื้อผ่านเจ้ามือทั่วไปอย่างชัดเจน:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot; start=&quot;1&quot;&gt;
&lt;li&gt;&lt;strong&gt;อัตราจ่ายสูงที่สุด:&lt;/strong&gt; เราให้อัตราจ่ายเลขท้าย 3 ตัว และ 2 ตัว ในระดับที่สูงที่สุดในตลาด ช่วยให้คุณได้รับกำไรที่มากขึ้นจากเงินลงทุนเท่าเดิม&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ไม่มีเลขอั้น (หรือน้อยมาก):&lt;/strong&gt; เราเปิดรับแทงเกือบทุกตัวเลข รวมถึงเลขเด็ดเลขดังที่เจ้ามือทั่วไปมักจะปิดรับ ทำให้คุณไม่พลาดโอกาสในการทำกำไร&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ระบบตรวจหวยอัตโนมัติ:&lt;/strong&gt; เมื่อผลรางวัลออก ระบบจะทำการตรวจรางวัลให้คุณทันที และปรับยอดเงินเข้าบัญชีเครดิตอย่างรวดเร็วโดยไม่ต้องรอนาน&lt;/li&gt;
&lt;/ol&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/หวยออนไลน์-ufabet-2.webp&quot; alt=&quot;หวยออนไลน์ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ประเภท หวยออนไลน์ ที่เปิดให้บริการ: สนุกได้ทุกวันไม่มีหยุด&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;ที่ &lt;strong&gt;UFABET&lt;/strong&gt; เราไม่ได้มีแค่หวยรัฐบาลไทยเท่านั้น แต่เรายังมีหวยประเภทอื่นๆ ให้คุณได้ลุ้นรวยได้ตลอดทั้งวัน:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;หวยรัฐบาลไทย:&lt;/strong&gt; เปิดรับแทงล่วงหน้าก่อนวันหวยออก พร้อมอัตราจ่ายที่เร้าใจ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;หวยฮานอย (หวยเวียดนาม):&lt;/strong&gt; มีให้เล่นทุกวัน ทั้งฮานอยปกติ ฮานอยพิเศษ และฮานอย VIP&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;หวยลาว (ลาวพัฒนา):&lt;/strong&gt; ออกรางวัลสัปดาห์ละ 3 วัน (จันทร์, พุธ, ศุกร์) เป็นที่นิยมอย่างมากในหมู่คอหวยไทย&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;หวยหุ้น:&lt;/strong&gt; รวมหวยหุ้นจากทั่วโลก เช่น หุ้นไทย, นิเคอิ, ฮั่งเส็ง, และดาวโจนส์&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;หวยยี่กี:&lt;/strong&gt; หวยที่ออกรางวัลทุกๆ 15-30 นาที เหมาะสำหรับคนที่ชอบลุ้นรางวัลตลอดทั้งวัน&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ประเภทรางวัล&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;อัตราจ่ายโดยประมาณ (UFABET)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3 ตัวบน (ตรง)&lt;/td&gt;
&lt;td&gt;บาทละ 900 &amp;#8211; 950&lt;/td&gt;
&lt;td&gt;สูงกว่าหวยใต้ดินทั่วไป&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 ตัวโต๊ด&lt;/td&gt;
&lt;td&gt;บาทละ 120 &amp;#8211; 150&lt;/td&gt;
&lt;td&gt;ลุ้นง่าย ได้เงินดี&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 ตัวบน / ล่าง&lt;/td&gt;
&lt;td&gt;บาทละ 90 &amp;#8211; 95&lt;/td&gt;
&lt;td&gt;จ่ายหนัก มั่นใจได้&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;วิ่งบน / ล่าง&lt;/td&gt;
&lt;td&gt;ตามเรทมาตรฐาน&lt;/td&gt;
&lt;td&gt;ความเสี่ยงต่ำ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/หวยออนไลน์-ufabet-4.webp&quot; alt=&quot;หวยออนไลน์ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ข้อดีของการซื้อหวยผ่านระบบ UFABET&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ความสะดวกสบาย:&lt;/strong&gt; แทงผ่านสมาร์ทโฟนได้ทุกที่ทุกเวลา ไม่ต้องส่งโพยกระดาษให้ยุ่งยาก&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ความปลอดภัยของข้อมูล:&lt;/strong&gt; ข้อมูลการซื้อหวยของคุณจะถูกเก็บเป็นความลับสูงสุดด้วยระบบการเข้ารหัสที่ปลอดภัย&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ประวัติการแทงชัดเจน:&lt;/strong&gt; คุณสามารถตรวจสอบโพยย้อนหลังได้ทุกใบ มีหลักฐานการซื้อที่ชัดเจนในระบบ&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;FAQ: รวมคำถามที่พบบ่อยเกี่ยวกับ หวยออนไลน์&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. หวยออนไลน์ ของ UFABET ปิดรับแทงกี่โมง?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; สำหรับหวยรัฐบาลไทย เราปิดรับแทงในวันหวยออกเวลา 15:20 น. ส่วนหวยประเภทอื่นๆ เช่น ฮานอย หรือลาว จะปิดรับก่อนเวลาผลออกประมาณ 15-30 นาทีครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. หากถูกรางวัลเงินจะเข้าบัญชีเมื่อไหร่?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ระบบจะทำการปรับยอดเงินรางวัลให้ทันทีหลังจากที่ผลรางวัลอย่างเป็นทางการประกาศออกมา (โดยปกติไม่เกิน 5-10 นาที) คุณสามารถตรวจสอบยอดเงินได้ในหน้าประวัติการทำรายการ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. มีขั้นต่ำในการแทงหวยหรือไม่?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; เริ่มต้นเพียง 1 บาทเท่านั้นครับ เหมาะสำหรับนักเสี่ยงโชคทุกระดับทุน สามารถกระจายความเสี่ยงแทงได้หลายตัวเลขตามต้องการ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;4. มีเลขอั้นเยอะไหม?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; เราพยายามเปิดรับทุกตัวเลขให้มากที่สุดครับ หากมีเลขใดที่มีการแทงเข้ามาหนาแน่นจริงๆ ระบบอาจจะมีการปรับอัตราจ่ายลงเล็กน้อย (เลขจ่ายครึ่ง) ซึ่งจะมีการแจ้งให้ทราบล่วงหน้าในหน้าแทงหวยครับ หรือดูรายละเอียดได้ที่ส่วน &lt;strong&gt;FAQ&lt;/strong&gt; ของเมนูหวย&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/หวยออนไลน์-ufabet-3.webp&quot; alt=&quot;หวยออนไลน์ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;บทสรุป: มั่นใจทุกตัวเลข เลือกแทง หวยออนไลน์ กับ UFABET&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเล่น &lt;strong&gt;หวยออนไลน์&lt;/strong&gt; จะไม่ใช่เรื่องที่น่ากังวลอีกต่อไปหากคุณเลือกใช้บริการกับ &lt;strong&gt;UFABET&lt;/strong&gt; เว็บตรงที่มีรากฐานทางการเงินที่แข็งแกร่งและมีระบบการทำงานที่เป็นมืออาชีพ เราพร้อมมอบประสบการณ์การเสี่ยงโชคที่ดีที่สุด จ่ายหนัก จ่ายจริง และดูแลคุณตลอด 24 ชั่วโมง&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เริ่มต้นเส้นทางเศรษฐีของคุณได้วันนี้ สมัครสมาชิกกับเราและเตรียมรับโชคจากตัวเลขที่คุณมั่นใจได้เลยครับ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: คาสิโนสด UFABET สัมผัสประสบการณ์เดิมพันระดับโลก ส่งตรงจากบ่อนจริง 24 ชม.</title>
    <link href="https://puppetblog.nl/live-casino/"/>
    <id>https://puppetblog.nl/?p=67</id>
    <updated>2026-03-17T04:17:20+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;ก้าวเข้าสู่มิติใหม่ของการเดิมพันออนไลน์กับ &lt;strong&gt;คาสิโนสด (Live Casino)&lt;/strong&gt; ที่ &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; เราได้ยกระดับมาตรฐานการเล่นเกม &lt;strong&gt;กาสิโน&lt;/strong&gt; ให้มีความสมจริงเหมือนคุณได้เข้าไปนั่งอยู่ที่โต๊ะเดิมพันในคาสิโนระดับโลกจริงๆ ด้วยเทคโนโลยีการถ่ายทอดสดที่ทันสมัยที่สุด คุณจะได้พบกับดีลเลอร์สาวสวยมืออาชีพที่คอยให้บริการแจกไพ่และหมุนวงล้อแบบเรียลไทม์ มั่นใจได้ในความโปร่งใสและยุติธรรมในทุกตาการเดิมพัน&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-439&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/คาสิโนสด-UFABET-1.webp&quot; alt=&quot;คาสิโนสด UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/คาสิโนสด-UFABET-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/03/คาสิโนสด-UFABET-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ทำไมต้องเล่น คาสิโนสด กับ UFABET? ความสมจริงที่เหนือระดับ&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเลือกใช้บริการ &lt;strong&gt;คาสิโนสด&lt;/strong&gt; กับเรา มีข้อดีที่นักเดิมพันระดับมืออาชีพต่างไว้วางใจ:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot; start=&quot;1&quot;&gt;
&lt;li&gt;&lt;strong&gt;ถ่ายทอดสดระบบ 4K:&lt;/strong&gt; สัญญาณภาพคมชัดระดับ Full HD และ 4K ไม่มีการดีเลย์หรือกระตุก ให้คุณเห็นทุกรายละเอียดการแจกไพ่ชัดเจน 100%&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;รวมค่ายยักษ์ใหญ่:&lt;/strong&gt; เราเป็นพันธมิตรกับค่ายชั้นนำระดับสากล เช่น SA Gaming, Sexy Baccarat, Evolution Gaming และ BG Casino เลือกเล่นได้ทุกค่ายในเว็บเดียว&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เล่นได้ทุกที่ทุกเวลา:&lt;/strong&gt; ระบบถูกออกแบบมาให้รองรับการเล่นผ่านมือถือได้อย่างลื่นไหล ไม่ว่าคุณจะอยู่ที่ไหนก็สามารถเข้าถึงโต๊ะเดิมพันระดับโลกได้ทันที&lt;/li&gt;
&lt;/ol&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/คาสิโนสด-UFABET-2.webp&quot; alt=&quot;คาสิโนสด UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;สมัครสมาชิก และ เข้าสู่ระบบ เลือกห้อง คาสิโนสด ที่คุณชื่นชอบ&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;คุณสามารถเริ่มต้นความตื่นเต้นได้ง่ายๆ เพียงไม่กี่ขั้นตอน:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;สมัครสมาชิก:&lt;/strong&gt; สมัครผ่านระบบอัตโนมัติที่หน้าเว็บไซต์ของเรา ใช้เวลาไม่ถึง 1 นาทีในการกรอกข้อมูลและรับ User&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เข้าสู่ระบบ:&lt;/strong&gt; เมื่อได้รับรหัสแล้ว ให้ทำการ &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; และไปที่เมนู &amp;#8220;คาสิโนสด&amp;#8221; คุณจะพบกับรายชื่อค่ายเกมและห้องเดิมพันมากมาย ทั้งบาคาร่า, รูเล็ต, ไฮโล (ซิกโบ) และเกมโชว์สุดพิเศษ&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;มั่นใจทุกธุรกรรม: ฝากเงิน และ ถอนเงิน อัตโนมัติ รวดเร็วภายใน 10 วินาที&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;ความมั่นคงทางการเงินคือหัวใจหลักของ &lt;strong&gt;UFABET&lt;/strong&gt; เพื่อให้การเล่น &lt;strong&gt;คาสิโนสด&lt;/strong&gt; ของคุณราบรื่นที่สุด:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ฝากเงิน ทันใจ:&lt;/strong&gt; ระบบ AI อัจฉริยะช่วยให้ยอดเงินของคุณถูกเติมเข้าเครดิตภายใน 10-20 วินาทีหลังจากทำรายการ ไม่ต้องส่งสลิปให้แอดมินตรวจสอบ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ถอนเงิน ชัวร์ 100%:&lt;/strong&gt; เมื่อคุณได้กำไรจากการเดิมพัน สามารถกด &lt;strong&gt;ถอนเงิน&lt;/strong&gt; ได้ทันทีโดยไม่มีขั้นต่ำ ระบบจะโอนเงินเข้าบัญชีธนาคารที่คุณลงทะเบียนไว้อย่างรวดเร็วและแม่นยำ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/คาสิโนสด-UFABET-3.webp&quot; alt=&quot;คาสิโนสด UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;โปรโมชั่น สุดคุ้มสำหรับคอ คาสิโนสด และ กาสิโน ออนไลน์&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เรามอบความคุ้มค่าให้สมาชิกอย่างต่อเนื่องด้วยข้อเสนอพิเศษ:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;โปรโมชั่น ต้อนรับสมาชิกใหม่:&lt;/strong&gt; รับเครดิตโบนัสเพิ่มทันทีเมื่อทำการฝากเงินครั้งแรกหลัง &lt;strong&gt;สมัครสมาชิก&lt;/strong&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;คอมมิชชั่นคืนยอดเสีย:&lt;/strong&gt; สำหรับสาย &lt;strong&gt;คาสิโนสด&lt;/strong&gt; เรามีระบบคืนค่าคอมมิชชั่นและคืนเงินยอดเสียรายวัน เพื่อให้คุณมีทุนกลับมาแก้มือและสร้างกำไรได้ใหม่ในวันถัดไป&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ประเภทเกมในคาสิโนสด&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ค่ายแนะนำ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;จุดเด่น&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;บาคาร่าสด&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sexy Baccarat&lt;/td&gt;
&lt;td&gt;ดีลเลอร์สาวสวยในชุดบิกินี่ พร้อมเพลงสนุกๆ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;รูเล็ตออนไลน์&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Evolution Gaming&lt;/td&gt;
&lt;td&gt;มีระบบ Multi-Camera เห็นลูกเหล็กชัดทุกมุม&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ซิกโบ (ไฮโล)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SA Gaming&lt;/td&gt;
&lt;td&gt;ระบบเขย่าลูกเต๋าที่โปร่งใส ตรวจสอบง่าย&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;เกมโชว์ (Game Shows)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Evolution Gaming&lt;/td&gt;
&lt;td&gt;ประสบการณ์ใหม่ของการเสี่ยงโชคที่สนุกและตื่นเต้น&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;FAQ: รวมคำถามที่พบบ่อยเกี่ยวกับ คาสิโนสด UFABET&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. เล่น คาสิโนสด มีโอกาสโดนโกงไหม?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ไม่มีแน่นอนครับ &lt;strong&gt;UFABET&lt;/strong&gt; เป็นเว็บตรงมาตรฐานสากล ทุกเกมในคาสิโนสดเป็นการถ่ายทอดสดแบบ Real-time มีนาฬิกาบอกเวลาสดในหน้าจอ ท่านสามารถตรวจสอบได้ทุกขั้นตอนครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. ขั้นต่ำในการวางเดิมพัน คาสิโนสด คือเท่าไหร่?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; เริ่มต้นเพียง 10 บาทเท่านั้นครับ (ขึ้นอยู่กับแต่ละค่ายและแต่ละโต๊ะ) เหมาะสำหรับทั้งผู้เล่นมือใหม่และระดับวีไอพี&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. หากเน็ตหลุดขณะวางเดิมพันต้องทำอย่างไร?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ระบบจะยึดตามผลการออกรางวัลจริงในรอบนั้นๆ หากท่านวางเดิมพันสำเร็จแล้วและผลชนะ ระบบจะเติมเงินเข้าบัญชีให้ท่านโดยอัตโนมัติเมื่อท่าน &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; อีกครั้งครับ หรือศึกษาเพิ่มเติมได้ที่เมนู &lt;strong&gt;FAQ&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/คาสิโนสด-UFABET-4.webp&quot; alt=&quot;คาสิโนสด UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;บทสรุป: ยกระดับการเดิมพันของคุณไปกับ คาสิโนสด UFABET วันนี้&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;หากคุณกำลังมองหาความสนุกที่เหนือระดับและความมั่นคงทางการเงินสูงสุด &lt;strong&gt;คาสิโนสด&lt;/strong&gt; บนเว็บไซต์ &lt;strong&gt;UFABET&lt;/strong&gt; คือทางเลือกที่ดีที่สุดของคุณ ด้วยระบบที่ลื่นไหล ภาพสวยคมชัด และการบริการที่เป็นเลิศ&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;อย่ารอช้า! &lt;strong&gt;สมัครสมาชิก&lt;/strong&gt; และ &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; เพื่อสัมผัสบรรยากาศคาสิโนระดับพรีเมียมได้จากที่บ้านคุณ พร้อมรับ &lt;strong&gt;โปรโมชั่น&lt;/strong&gt; สุดพิเศษที่เราเตรียมไว้ให้คุณวันนี้ครับ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: ซิกโบ UFABET เกมไฮโลออนไลน์ยอดนิยม อัตราจ่ายสูง เล่นสด 24 ชม.</title>
    <link href="https://puppetblog.nl/sicbo/"/>
    <id>https://puppetblog.nl/?p=68</id>
    <updated>2026-03-17T04:15:45+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;หากคุณเป็นแฟนพันธุ์แท้ของเกมลูกเต๋า &lt;strong&gt;ซิกโบ (Sic Bo)&lt;/strong&gt; หรือที่รู้จักกันดีในชื่อ &amp;#8220;ไฮโล&amp;#8221; คือเกมที่คุณไม่ควรพลาดที่ &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; เรายกระดับการเล่นไฮโลแบบเดิมๆ ให้กลายเป็นประสบการณ์ &lt;strong&gt;กาสิโน&lt;/strong&gt; ระดับสากล ด้วยระบบการถ่ายทอดสดที่คมชัด โปร่งใส และมีรูปแบบการวางเดิมพันที่หลากหลาย พร้อมอัตราการจ่ายเงินรางวัลที่สูงกว่าที่ไหนๆ ทำให้คุณสามารถลุ้นเงินแสนได้ในเพียงชั่วพริบตา&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-434&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ซิกโบ-ufabet-1.webp&quot; alt=&quot;ซิกโบ UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ซิกโบ-ufabet-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/03/ซิกโบ-ufabet-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ทำไมต้องเล่น ซิกโบ กับ UFABET? สนุกปลอดภัยกว่าที่อื่น&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเลือกเล่น &lt;strong&gt;ซิกโบ&lt;/strong&gt; กับเว็บตรงอย่าง ยูฟ่าเบท ช่วยให้คุณมั่นใจในทุกการเดิมพัน:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot; start=&quot;1&quot;&gt;
&lt;li&gt;&lt;strong&gt;ความโปร่งใสสูงสุด:&lt;/strong&gt; เราใช้ระบบถ่ายทอดสด (Live Streaming) ส่งตรงจากคาสิโนจริง เห็นการเขย่าลูกเต๋าในโหลแก้วใสชัดเจนทุกมุมกล้อง ป้องกันการโกง 100%&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;อัตราจ่ายที่คุ้มค่า:&lt;/strong&gt; นอกจากแทง สูง-ต่ำ หรือ เต็ง-โต๊ด แล้ว การแทง &amp;#8220;ตองเลข&amp;#8221; ในระบบของเรามีอัตราจ่ายสูงสุดถึง 150 เท่า!&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;อินเตอร์เฟซใช้งานง่าย:&lt;/strong&gt; หน้ากระดานเดิมพันถูกออกแบบมาให้ดูง่าย รองรับทั้งภาษาไทยและสถิติย้อนหลังเพื่อช่วยในการวิเคราะห์&lt;/li&gt;
&lt;/ol&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ขั้นตอนการ สมัครสมาชิก และ เข้าสู่ระบบ เพื่อร่วมสนุกกับ ซิกโบ&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เริ่มต้นความตื่นเต้นกับเกมลูกเต๋าได้ง่ายๆ ดังนี้:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;สมัครสมาชิก:&lt;/strong&gt; ทำรายการผ่านระบบออโต้เพียงไม่กี่วินาที กรอกข้อมูลเบื้องต้นและบัญชีธนาคารเพื่อเปิด User ใหม่&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เข้าสู่ระบบ:&lt;/strong&gt; ล็อกอินเข้าใช้งานผ่านหน้าเว็บหรือแอปพลิเคชัน จากนั้นไปที่เมนู &lt;strong&gt;กาสิโน&lt;/strong&gt; สด และเลือกค่ายเกมที่คุณชื่นชอบเพื่อเข้าสู่โต๊ะ &lt;strong&gt;ซิกโบ&lt;/strong&gt; ได้ทันที&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ซิกโบ-ufabet-2.webp&quot; alt=&quot;ซิกโบ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ระบบ ฝากเงิน และ ถอนเงิน อัตโนมัติ: รวดเร็วทันใจสำหรับคอไฮโล&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เพราะจังหวะการเดิมพันเป็นเรื่องสำคัญ ระบบการเงินของ &lt;strong&gt;UFABET&lt;/strong&gt; จึงทำงานด้วยความรวดเร็ว:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ฝากเงิน ทันใจ:&lt;/strong&gt; เติมเครดิตผ่านระบบ AI ยอดปรับเข้าทันทีภายใน 10-20 วินาที ไม่ต้องส่งสลิปแอดมินให้เสียเวลา&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ถอนเงิน ชัวร์:&lt;/strong&gt; เมื่อคุณชนะรางวัลใหญ่จากการแทง &lt;strong&gt;ซิกโบ&lt;/strong&gt; สามารถกด &lt;strong&gt;ถอนเงิน&lt;/strong&gt; ได้ทันที ระบบจะดำเนินการโอนเงินเข้าบัญชีคุณอย่างรวดเร็วและครบถ้วนทุกบาท&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;โปรโมชั่น สุดพิเศษและสิทธิประโยชน์สำหรับสมาชิก UFABET&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เรามอบความคุ้มค่าให้สมาชิกมากกว่าใครด้วยสิทธิพิเศษมากมาย:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;โปรโมชั่น ต้อนรับสมาชิกใหม่:&lt;/strong&gt; รับโบนัสเครดิตเพิ่มทันทีเมื่อทำการฝากเงินครั้งแรกหลัง &lt;strong&gt;สมัครสมาชิก&lt;/strong&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;คืนค่าคอมมิชชั่น:&lt;/strong&gt; ทุกยอดการเล่นในเกม &lt;strong&gt;ซิกโบ&lt;/strong&gt; ท่านจะได้รับค่าคอมมิชชั่นคืนสูงสุด 0.7% เพื่อใช้เป็นทุนในการทำกำไรต่อไป&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;กิจกรรมพิเศษ:&lt;/strong&gt; ลุ้นรับรางวัลใหญ่จากกิจกรรมแจกโบนัสรายสัปดาห์สำหรับสมาชิกที่มียอดเล่นสม่ำเสมอ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;รูปแบบการแทงซิกโบ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;อัตราการจ่ายเงิน&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ความน่าสนใจ&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;สูง &amp;#8211; ต่ำ&lt;/td&gt;
&lt;td&gt;1 : 1&lt;/td&gt;
&lt;td&gt;โอกาสชนะสูงถึง 50/50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;แทงเลขเดี่ยว&lt;/td&gt;
&lt;td&gt;1 : 1 (ถึง 3 เท่า)&lt;/td&gt;
&lt;td&gt;ลุ้นง่ายๆ จากลูกเต๋าแต่ละลูก&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;แทงรวมแต้ม (9-12)&lt;/td&gt;
&lt;td&gt;1 : 6&lt;/td&gt;
&lt;td&gt;ผลตอบแทนคุ้มค่า&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;แทงตอง (ระบุเลข)&lt;/td&gt;
&lt;td&gt;1 : 150&lt;/td&gt;
&lt;td&gt;รางวัลใหญ่ที่สุดในเกม&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ซิกโบ-ufabet-3.webp&quot; alt=&quot;ซิกโบ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;FAQ: รวมคำถามที่พบบ่อยเกี่ยวกับเกม ซิกโบ ออนไลน์&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. เกม ซิกโบ กับ ไฮโลไทย แตกต่างกันอย่างไร?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; กติกาพื้นฐานเหมือนกันครับ แต่ &lt;strong&gt;ซิกโบ&lt;/strong&gt; ในระบบออนไลน์จะมีรูปแบบการวางเดิมพันที่ละเอียดกว่าและอัตราจ่ายที่สูงกว่าไฮโลพื้นบ้านทั่วไปมาก&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. สามารถเล่น ซิกโบ ผ่านมือถือได้หรือไม่?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ได้แน่นอนครับ ระบบของเรารองรับการเล่นผ่านมือถือทุกรุ่น ทั้ง iOS และ Android ภาพสวยลื่นไหลไม่แพ้ในคอมพิวเตอร์&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. ขั้นต่ำในการวางเดิมพัน ซิกโบ คือเท่าไหร่?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; เริ่มต้นเพียง 10-20 บาทต่อตานะครับ (ขึ้นอยู่กับค่ายเกมที่เลือก) รายละเอียดเพิ่มเติมสามารถดูได้ที่เมนู &lt;strong&gt;FAQ&lt;/strong&gt; ของระบบครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/ซิกโบ-ufabet-4.webp&quot; alt=&quot;ซิกโบ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;บทสรุป: มั่นใจทุกการเดิมพัน ซิกโบ ไปกับ UFABET เว็บตรงอันดับ 1&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;หากคุณต้องการความตื่นเต้นและผลตอบแทนที่รวดเร็ว เกม &lt;strong&gt;ซิกโบ&lt;/strong&gt; บน &lt;strong&gt;UFABET&lt;/strong&gt; คือคำตอบที่ดีที่สุด ด้วยมาตรฐานเว็บตรงที่มั่นคง โปร่งใส และระบบ &lt;strong&gt;ฝากเงิน-ถอนเงิน&lt;/strong&gt; ที่รวดเร็วที่สุดในปี 2026&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;อย่ารอช้า! &lt;strong&gt;สมัครสมาชิก&lt;/strong&gt; และ &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; วันนี้ เพื่อรับ &lt;strong&gt;โปรโมชั่น&lt;/strong&gt; สุดพิเศษและเริ่มต้นทำกำไรกับเกมลูกเต๋าระดับโลกไปพร้อมกับเราครับ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: ยิงปลา UFABET เกมยอดฮิตเล่นง่าย กราฟิกสวย ปลาตายไว ได้เงินจริง</title>
    <link href="https://puppetblog.nl/fishing/"/>
    <id>https://puppetblog.nl/?p=69</id>
    <updated>2026-03-17T04:15:27+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;หากคุณกำลังมองหาเกมที่เปลี่ยนความแม่นยำให้กลายเป็นผลกำไร เกม &lt;strong&gt;ยิงปลา&lt;/strong&gt; คือตัวเลือกอันดับ 1 ที่ &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; เราภูมิใจนำเสนอ ด้วยรูปแบบเกมที่เล่นง่ายไม่ซับซ้อน กราฟิกสีสันสดใสระดับ HD และเอฟเฟกต์สุดตระการตา ทำให้เกมนี้ครองใจผู้เล่นทุกเพศทุกวัย ที่สำคัญคือระบบของเราปรับเปลี่ยนให้ &amp;#8220;ปลาตายง่าย&amp;#8221; และ &amp;#8220;บอสแตกบ่อย&amp;#8221; เพื่อให้สมาชิกได้รับผลตอบแทนที่คุ้มค่าที่สุด&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-320&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-ยิงปลา-1.webp&quot; alt=&quot;ยิงปลา UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-ยิงปลา-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-ยิงปลา-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ทำไมต้องเล่นเกม ยิงปลา กับ UFABET? สนุกกว่าที่เคยสัมผัส&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเลือกเล่นเกมยิงปลาในหมวด &lt;strong&gt;กาสิโน&lt;/strong&gt; ของเรา มีจุดเด่นที่ทำให้เราแตกต่างจากเว็บอื่น:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot; start=&quot;1&quot;&gt;
&lt;li&gt;&lt;strong&gt;รวมค่ายเกมชั้นนำ:&lt;/strong&gt; เราคัดสรรค่ายเกมยิงปลาที่ดีที่สุด เช่น Joker Gaming, JILI และ Spadegaming มาไว้ในที่เดียว&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ราคากระสุนยืดหยุ่น:&lt;/strong&gt; เริ่มต้นราคากระสุนเพียงไม่กี่สตางค์ ไปจนถึงการใช้กระสุนพลังทำลายล้างสูงเพื่อล่าบอสรางวัลใหญ่&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;โบนัสพิเศษในเกม:&lt;/strong&gt; มีไอเทมเสริมมากมาย เช่น ปืนเลเซอร์, ระเบิดแช่แข็งปลา หรือปืนสายฟ้า เพื่อช่วยให้คุณล่าแต้มได้ง่ายขึ้น&lt;/li&gt;
&lt;/ol&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-ยิงปลา-2.webp&quot; alt=&quot;ยิงปลา UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;สมัครสมาชิก และ เข้าสู่ระบบ เริ่มต้นล่ารางวัลใต้ท้องทะเล&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;คุณสามารถเข้าสู่โลกใต้ทะเลเพื่อล่าสมบัติได้ในไม่กี่วินาที:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;สมัครสมาชิก:&lt;/strong&gt; สมัครง่ายผ่านระบบออโต้ เพียงกรอกเบอร์โทรศัพท์และบัญชีธนาคารเพื่อรับรหัสผ่าน&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เข้าสู่ระบบ:&lt;/strong&gt; ล็อกอินผ่านหน้าเว็บหรือแอปพลิเคชัน จากนั้นเลือกเมนูเกม &lt;strong&gt;ยิงปลา&lt;/strong&gt; คุณจะพบกับห้องเดิมพันหลายระดับ ตั้งแต่ห้องสำหรับมือใหม่ไปจนถึงห้องระดับเซียน (High Roller)&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ระบบ ฝากเงิน และ ถอนเงิน อัตโนมัติ: เติมกระสุนไว ไม่ขัดจังหวะการยิง&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;จังหวะที่ปลาบอสกำลังจะตายคือนาทีทอง ระบบการเงินของเราจึงถูกออกแบบมาให้เร็วที่สุด:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ฝากเงิน ทันใจ:&lt;/strong&gt; เครดิตปรับยอดภายใน 10-20 วินาทีผ่านระบบ AI ไม่ต้องส่งสลิปให้แอดมิน เพื่อให้คุณเติมกระสุนได้ต่อเนื่อง&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ถอนเงิน ชัวร์ 100%:&lt;/strong&gt; เมื่อเก็บแต้มจากปลาตัวใหญ่ได้จนพอใจ สามารถกด &lt;strong&gt;ถอนเงิน&lt;/strong&gt; ได้ทันที ระบบจะโอนกำไรเข้าบัญชีท่านอย่างแม่นยำและปลอดภัย&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-ยิงปลา-3.webp&quot; alt=&quot;ยิงปลา UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;โปรโมชั่น สุดพิเศษและเกม กาสิโน อื่นๆ ในระบบ UFABET&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เรามอบความคุ้มค่าให้สมาชิกสายยิงปลามากกว่าใคร:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;โปรโมชั่น ต้อนรับสมาชิกใหม่:&lt;/strong&gt; รับเครดิตเพิ่มสำหรับการเติมเงินครั้งแรกเพื่อใช้เป็นทุนกระสุน&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;คืนเงินยอดเสีย:&lt;/strong&gt; เล่นไม่เข้าไม่ต้องกังวล เรามีระบบคืนเงินยอดเสียรายวันเพื่อเป็นทุนให้คุณกลับมาแก้ตัว&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ความหลากหลาย:&lt;/strong&gt; นอกจากเกมยิงปลา ท่านยังสามารถใช้กระเป๋าเครดิตเดียวกันสลับไปเล่น &lt;strong&gt;กาสิโน&lt;/strong&gt; สด หรือสล็อตได้ทันทีโดยไม่ต้องโยกเงิน&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ประเภทปลาในเกม&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ความยากในการยิง&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;อัตราตัวคูณรางวัล&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ปลาขนาดเล็ก&lt;/td&gt;
&lt;td&gt;ง่ายมาก&lt;/td&gt;
&lt;td&gt;x2 &amp;#8211; x10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ปลาขนาดกลาง&lt;/td&gt;
&lt;td&gt;ปานกลาง&lt;/td&gt;
&lt;td&gt;x20 &amp;#8211; x50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ปลาบอส / มังกร&lt;/td&gt;
&lt;td&gt;ยาก (แต่คุ้ม)&lt;/td&gt;
&lt;td&gt;x100 &amp;#8211; x500+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;FAQ: รวมคำถามที่พบบ่อยเกี่ยวกับเกม ยิงปลา&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. เกมยิงปลาใช้เงินขั้นต่ำเท่าไหร่?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; เริ่มต้นขั้นต่ำเพียงนัดละ 0.1 บาทเท่านั้นครับ (ขึ้นอยู่กับแต่ละเกม) ทำให้ผู้เล่นทุนน้อยก็สามารถร่วมสนุกได้&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. มีเทคนิคยิงปลายังไงให้ได้เงินจริง?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; แนะนำให้เลือกยิงปลาที่ว่ายออกมาจากขอบจอ และไม่ควรยิงค้างใส่ปลาที่กำลังจะหลุดจากจอครับ ท่านสามารถศึกษาเทคนิคเพิ่มเติมได้ในหน้า &lt;strong&gt;FAQ&lt;/strong&gt; ของเรา&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. เล่นผ่านมือถือภาพจะกระตุกไหม?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ระบบของเราเสถียรมากครับ รองรับการเล่นผ่านมือถือทุกรุ่นอย่างลื่นไหล เพียงแค่มีอินเทอร์เน็ตที่เสถียรท่านก็สนุกได้ทุกที่&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-ยิงปลา-4.webp&quot; alt=&quot;ยิงปลา UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;บทสรุป: สัมผัสประสบการณ์ล่าสมบัติใต้ทะเลกับ ยิงปลา UFABET วันนี้&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เปลี่ยนเวลาว่างให้กลายเป็นกำไรไปกับเกม &lt;strong&gt;ยิงปลา&lt;/strong&gt; ที่ดีที่สุดบน &lt;strong&gt;UFABET&lt;/strong&gt; เว็บตรงที่มีความมั่นคงทางการเงินและระบบที่ทันสมัยที่สุด&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;อย่ารอช้า! &lt;strong&gt;สมัครสมาชิก&lt;/strong&gt; และ &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; วันนี้ เพื่อรับ &lt;strong&gt;โปรโมชั่น&lt;/strong&gt; สุดพิเศษและเริ่มต้นผจญภัยใต้ท้องทะเลไปพร้อมกับเราครับ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: เกมไพ่ UFABET รวมทุกเกมยอดฮิต บาคาร่า เสือมังกร เล่นสด 24 ชม.</title>
    <link href="https://puppetblog.nl/card-game/"/>
    <id>https://puppetblog.nl/?p=70</id>
    <updated>2026-03-17T04:15:02+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;สำหรับนักเดิมพันที่หลงใหลในความคลาสสิกและตื่นเต้น &lt;strong&gt;เกมไพ่&lt;/strong&gt; คือหมวดหมู่ที่ได้รับความนิยมสูงสุดตลอดกาลที่ &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; เราได้รวบรวมสุดยอดเกมไพ่จากทั่วทุกมุมโลกมาไว้ในรูปแบบคาสิโนสด (Live Casino) ที่ส่งตรงจากสตูดิโอระดับโลก มั่นใจได้ในความโปร่งใส ทุกการแจกไพ่สามารถตรวจสอบได้แบบเรียลไทม์ พร้อมดีลเลอร์สาวสวยที่คอยให้บริการท่านตลอดวัน&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-427&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/เกมไพ่-UFABET-1.webp&quot; alt=&quot;เกมไพ่ UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/เกมไพ่-UFABET-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/03/เกมไพ่-UFABET-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ทำไมต้องเล่น เกมไพ่ กับ UFABET? สัมผัสประสบการณ์ระดับพรีเมียม&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเข้าเล่น &lt;strong&gt;เกมไพ่&lt;/strong&gt; ในหมวด &lt;strong&gt;กาสิโน&lt;/strong&gt; ของเรามีความโดดเด่นที่เหนือกว่าใคร:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot; start=&quot;1&quot;&gt;
&lt;li&gt;&lt;strong&gt;ความหลากหลายของค่ายดัง:&lt;/strong&gt; เราคัดสรรค่ายยักษ์ใหญ่เช่น SA Gaming, Sexy Baccarat และ Evolution Gaming มาให้ท่านเลือกเล่นในบัญชีเดียว&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ถ่ายทอดสดแบบ Full HD:&lt;/strong&gt; ระบบสตรีมมิ่งที่เสถียรที่สุดในไทย ช่วยให้ท่านเห็นทุกรายละเอียดการแจกไพ่ ไม่มีภาพค้างหรือดีเลย์ให้เสียจังหวะ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เดิมพันขั้นต่ำเพียง 10 บาท:&lt;/strong&gt; ไม่ว่าท่านจะมีทุนมากหรือน้อย ก็สามารถร่วมสนุกและทำกำไรจากเกมไพ่ยอดนิยมได้ทันที&lt;/li&gt;
&lt;/ol&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/เกมไพ่-UFABET-2.webp&quot; alt=&quot;เกมไพ่ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ขั้นตอนการ สมัครสมาชิก และ เข้าสู่ระบบ เพื่อเข้าสู่โลกของ เกมไพ่&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เริ่มต้นทำกำไรได้ง่ายๆ เพียงไม่กี่ขั้นตอน:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;สมัครสมาชิก:&lt;/strong&gt; สมัครง่ายผ่านระบบออโต้หน้าเว็บไซต์ เพียงกรอกข้อมูลพื้นฐานและบัญชีธนาคารเพื่อรับรหัสเข้าใช้งาน&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เข้าสู่ระบบ:&lt;/strong&gt; หลังจากได้รับ User แล้ว สามารถทำการ &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; ได้ทันทีผ่านมือถือหรือคอมพิวเตอร์ จากนั้นเลือกหมวดหมู่ &amp;#8220;คาสิโนสด&amp;#8221; เพื่อพบกับอาณาจักร &lt;strong&gt;เกมไพ่&lt;/strong&gt; ที่ท่านชื่นชอบ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/เกมไพ่-UFABET-3.webp&quot; alt=&quot;เกมไพ่ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ระบบธุรกรรมเพื่อคอ เกมไพ่: ฝากเงิน และ ถอนเงิน รวดเร็วทันใจ&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;ความรวดเร็วทางการเงินคือสิ่งที่ &lt;strong&gt;UFABET&lt;/strong&gt; ให้ความสำคัญที่สุด เพื่อให้การเดิมพันของท่านลื่นไหล:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ฝากเงิน อัตโนมัติ:&lt;/strong&gt; เติมเครดิตได้ทันทีผ่านระบบ AI รองรับทุกธนาคารและวอลเล็ต ยอดปรับไวภายใน 10-20 วินาที ไม่ต้องแจ้งสลิป&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ถอนเงิน ทันที:&lt;/strong&gt; เมื่อสะสมกำไรจาก &lt;strong&gt;เกมไพ่&lt;/strong&gt; ได้ตามต้องการ ท่านสามารถทำรายการ &lt;strong&gt;ถอนเงิน&lt;/strong&gt; ได้ด้วยตัวเอง ระบบจะโอนเงินเข้าบัญชีท่านโดยตรงอย่างรวดเร็วและปลอดภัย&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/เกมไพ่-UFABET-4.webp&quot; alt=&quot;เกมไพ่ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;โปรโมชั่น สุดพิเศษสำหรับสมาชิกใหม่และเซียน เกมไพ่&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เรามี &lt;strong&gt;โปรโมชั่น&lt;/strong&gt; ที่ออกแบบมาเพื่อเพิ่มโอกาสชนะให้สมาชิกโดยเฉพาะ:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;โบนัสต้อนรับสมาชิกใหม่:&lt;/strong&gt; รับเครดิตเพิ่มทันทีจากการฝากครั้งแรกหลัง &lt;strong&gt;สมัครสมาชิก&lt;/strong&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ค่าคอมมิชชั่นคืนยอดเสีย:&lt;/strong&gt; ทุกยอดการเล่นในหมวดไพ่ เรามีระบบคืนเงินคอมมิชชั่นให้สูงสุด 0.7% เพื่อให้ท่านมีทุนไว้ทำกำไรต่อในรอบถัดไป&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;ประเภทเกมไพ่ยอดนิยม&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;จุดเด่นของเกม&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;อัตราการจ่าย (สูงสุด)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;บาคาร่า (Baccarat)&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;กติกาเข้าใจง่าย จบเกมไว&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1 : 11 (สำหรับแต้มคู่)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;เสือมังกร (Dragon Tiger)&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;วัดดวงด้วยไพ่ใบเดียว&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1 : 8 (สำหรับเสมอ)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;ป๊อกเด้งออนไลน์&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;เกมคลาสสิกที่คนไทยคุ้นเคย&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1 : 5 (สำหรับห้าเด้ง)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;FAQ คำถามที่พบบ่อยเกี่ยวกับ เกมไพ่ ในระบบ UFABET&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. เกมไพ่ในคาสิโนสดมีการล็อคผลหรือไม่?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ไม่มีการล็อคผลแน่นอนครับ ระบบของเราเป็นการถ่ายทอดสดแบบ Real-time 100% มีนาฬิกาบอกเวลาสดและท่านสามารถตรวจสอบความโปร่งใสได้ทุกรอบการเดิมพัน&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. สามารถเล่น เกมไพ่ ผ่านมือถือได้ไหม?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ได้แน่นอนครับ ระบบของเราออกแบบมาให้รองรับการใช้งานบนสมาร์ทโฟนทุกรุ่นทั้ง iOS และ Android อย่างสมบูรณ์แบบ&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. หากมีปัญหาในการวางเดิมพัน ต้องติดต่อใคร?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ท่านสามารถตรวจสอบแนวทางเบื้องต้นได้ที่หน้า &lt;strong&gt;FAQ&lt;/strong&gt; หรือติดต่อทีมงานซัพพอร์ตผ่านช่องทางที่ระบุไว้ได้ตลอด 24 ชั่วโมงครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/เกมไพ่-UFABET-5.webp&quot; alt=&quot;เกมไพ่ UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;บทสรุป: มั่นใจทุกการเดิมพัน เกมไพ่ ไปกับ UFABET เว็บตรง&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;ก้าวเข้าสู่ประสบการณ์การเดิมพันระดับสากลไปกับ &lt;strong&gt;เกมไพ่&lt;/strong&gt; ที่ดีที่สุดบน &lt;strong&gt;UFABET&lt;/strong&gt; เว็บตรงที่ไม่ผ่านเอเย่นต์ มั่นใจได้ในเรื่องความปลอดภัยและการเงินที่มั่งคั่ง&lt;/p&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;อย่ารอช้า! &lt;strong&gt;สมัครสมาชิก&lt;/strong&gt; และ &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; วันนี้ เพื่อรับ &lt;strong&gt;โปรโมชั่น&lt;/strong&gt; สุดพิเศษและเริ่มต้นทำกำไรกับเกมไพ่ที่ท่านถนัดไปพร้อมกับเราครับ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Puppet Blog: สล็อต UFABET รวมทุกค่ายเกมดัง แตกง่าย จ่ายจริง ระบบออโต้ที่ดีที่สุด</title>
    <link href="https://puppetblog.nl/slots/"/>
    <id>https://puppetblog.nl/?p=71</id>
    <updated>2026-03-17T04:14:42+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p class=&quot;wp-block-paragraph&quot;&gt;หากคุณกำลังมองหาแหล่งรวมเกม &lt;strong&gt;สล็อต&lt;/strong&gt; ที่มีความหลากหลายและมีอัตราการจ่ายรางวัลสูงที่สุดในเอเชีย &lt;strong&gt;&lt;a href=&quot;/&quot;&gt;UFABET&lt;/a&gt;&lt;/strong&gt; คือคำตอบสุดท้าย เราได้รวบรวมค่ายเกมสล็อตชั้นนำระดับโลกมาไว้ในที่เดียว เพื่อให้สมาชิกได้สัมผัสประสบการณ์การปั่นสล็อตที่สนุกสนาน กราฟิกตระการตา และที่สำคัญคือ &amp;#8220;โบนัสแตกง่าย&amp;#8221; พร้อมระบบการจัดการที่รวดเร็วทันใจในทุกขั้นตอน&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-324&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-สล็อต-1.webp&quot; alt=&quot;สล็อต UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-สล็อต-1.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-สล็อต-1-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;ทำไมต้องเล่น สล็อต กับ UFABET? ประสบการณ์ปั่นสล็อตที่เหนือระดับ&lt;/strong&gt;&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;การเลือกเล่นเกมสล็อตกับเว็บตรงอย่าง &lt;strong&gt;ยูฟ่าเบท&lt;/strong&gt; มีข้อดีที่ทำให้นักเดิมพันมืออาชีพไว้วางใจ:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;รวมค่ายดังระดับโลก:&lt;/strong&gt; ไม่ว่าจะเป็น PG Soft, Joker Gaming, Pragmatic Play หรือ Spadegaming เรามีให้เลือกเล่นกว่า 1,000 เกม&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;มาตรฐานสากล:&lt;/strong&gt; เกมทุกเกมได้รับใบเซอร์รับรองมาตรฐาน (RNG) มั่นใจได้ว่าไม่มีการปรับอัตราแพ้ชนะ ทุกการปั่นมีความยุติธรรม 100%&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;แจ็กพอตก้อนโต:&lt;/strong&gt; ด้วยฐานผู้เล่นจำนวนมาก ทำให้ยอดสะสมของเงินรางวัลแจ็กพอตพุ่งสูงขึ้นอย่างรวดเร็ว พร้อมแจกโบนัสใหญ่ให้สมาชิกทุกวัน&lt;/li&gt;
&lt;/ol&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-สล็อต-2.webp&quot; alt=&quot;สล็อต UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;สมัครสมาชิก และ เข้าสู่ระบบ เริ่มต้นปั่น สล็อต ได้ภายใน 1 นาที&lt;/strong&gt;&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เราให้ความสำคัญกับความสะดวกของสมาชิกเป็นอันดับหนึ่ง:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;สมัครสมาชิก:&lt;/strong&gt; คุณสามารถสมัครผ่านระบบออโต้ได้ด้วยตัวเอง เพียงกรอกเบอร์โทรศัพท์และบัญชีธนาคาร ไม่ต้องรอแอดมินอนุมัติ&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;เข้าสู่ระบบ:&lt;/strong&gt; ล็อกอินเข้าเล่นผ่านหน้าเว็บหรือแอปพลิเคชันได้ทันที ระบบมีความเสถียรสูง รองรับการเล่นบนมือถือทุกรุ่น ทั้ง iOS และ Android&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/สล็อต-UFABET-5.webp&quot; alt=&quot;สล็อต UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;ระบบธุรกรรมที่ตอบโจทย์สายปั่น: ฝากเงิน และ ถอนเงิน ไม่มีขั้นต่ำ&lt;/strong&gt;&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เพื่อไม่ให้การล่าโบนัสไทม์ (Bonus Time) ต้องสะดุด ระบบการเงินของเราจึงทำงานด้วยความไวแสง:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ฝากเงิน รวดเร็ว:&lt;/strong&gt; เติมเครดิตได้ทันทีผ่าน Mobile Banking และ True Wallet เครดิตปรับยอดภายใน 10 วินาที&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ถอนเงิน ชัวร์ 100%:&lt;/strong&gt; เมื่อสล็อตแตกหนัก แจ็กพอตลง คุณสามารถกด &lt;strong&gt;ถอนเงิน&lt;/strong&gt; ได้ทันทีโดยไม่จำกัดยอดถอนสูงสุดต่อวัน มั่นใจได้ว่าเว็บตรงจ่ายจริงทุกบิล&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-สล็อต-3.webp&quot; alt=&quot;สล็อต UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;โปรโมชั่น สุดคุ้มสำหรับแฟนพันธุ์แท้ สล็อต และ กาสิโน&lt;/strong&gt;&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;เราจัดเตรียมความคุ้มค่าไว้ซัพพอร์ตนักเดิมพันในทุกๆ วัน:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;โปรโมชั่น สล็อต:&lt;/strong&gt; รับโบนัสฟรีสปิน หรือเครดิตเพิ่มสำหรับการฝากเงินครั้งแรกของวัน&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;คืนเงินยอดเสีย:&lt;/strong&gt; ทุกยอดที่ปั่นสล็อตไม่เข้า เรามีระบบคืนเงินยอดเสียให้ทุกวัน เพื่อให้คุณมีทุนกลับมาแก้ตัวได้ใหม่&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ครบวงจร:&lt;/strong&gt; นอกจากสล็อตแล้ว คุณยังสามารถใช้กระเป๋าเครดิตเดียวกันสลับไปเล่น &lt;strong&gt;กาสิโน&lt;/strong&gt; สด บาคาร่า หรือแทงบอลได้ทันทีโดยไม่ต้องโยกเงิน&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;
&lt;table class=&quot;has-fixed-layout&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ฟีเจอร์ของเกม&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;จุดเด่นของ UFABET&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ผลประโยชน์ที่จะได้รับ&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;อัตราการแตก (RTP)&lt;/td&gt;
&lt;td&gt;สูงกว่า 96% ในทุกเกม&lt;/td&gt;
&lt;td&gt;มีโอกาสชนะรางวัลใหญ่บ่อยขึ้น&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ระบบการเงิน&lt;/td&gt;
&lt;td&gt;ออโต้ (AI Transaction)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ฝากเงิน&lt;/strong&gt; &amp;#8211; &lt;strong&gt;ถอนเงิน&lt;/strong&gt; ไว ไม่รอนาน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;บริการเสริม&lt;/td&gt;
&lt;td&gt;สลับไปเล่น &lt;strong&gt;กาสิโน&lt;/strong&gt; ได้&lt;/td&gt;
&lt;td&gt;ความหลากหลายในการเดิมพัน&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/04/ufabet-สล็อต-4.webp&quot; alt=&quot;สล็อต UFABET&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;FAQ: รวมคำถามที่พบบ่อยเกี่ยวกับ สล็อต UFABET&lt;/strong&gt;&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;1. เกมสล็อตค่ายไหนแตกง่ายที่สุดในตอนนี้?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ค่าย PG Soft และ Pragmatic Play เป็นค่ายที่ได้รับความนิยมสูงมากในหน้าเว็บเรา เนื่องจากมีฟรีเจอร์ซื้อฟรีสปินและตัวคูณที่ดุดัน&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;2. เริ่มต้นเดิมพัน สล็อต ขั้นต่ำกี่บาท?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; สมาชิกสามารถเริ่มต้นเบท (Bet) ได้ตั้งแต่ 1 บาทขึ้นไป เหมาะสำหรับผู้เล่นทุกระดับทุนครับ&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img decoding=&quot;async&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/สล็อต-UFABET-4.webp&quot; alt=&quot;สล็อต UFABET&quot; /&gt;&lt;/figure&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;&lt;strong&gt;3. หากเกิดปัญหาขณะกำลังเล่นแจ็กพอตแตก ต้องทำอย่างไร?&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;ตอบ:&lt;/strong&gt; ระบบของเรามีการบันทึกข้อมูลแบบ Real-time หากเกิดปัญหาอินเทอร์เน็ตขัดข้อง ยอดเงินรางวัลจะถูกเติมเข้าบัญชีให้ท่านโดยอัตโนมัติ หรือติดต่อแอดมินได้ 24 ชม.&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;533&quot; class=&quot;wp-image-422&quot; src=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/สล็อต-UFABET-6.webp&quot; alt=&quot;สล็อต UFABET&quot; srcset=&quot;https://puppetblog.nl/wp-content/uploads/2026/03/สล็อต-UFABET-6.webp 800w, https://puppetblog.nl/wp-content/uploads/2026/03/สล็อต-UFABET-6-768x512.webp 768w&quot; sizes=&quot;(max-width: 800px) 100vw, 800px&quot; /&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;&lt;strong&gt;บทสรุป: สัมผัสความสนุกและลุ้นโบนัสก้อนโตกับ สล็อต UFABET วันนี้&lt;/strong&gt;&lt;/h2&gt;



&lt;p class=&quot;wp-block-paragraph&quot;&gt;ก้าวเข้าสู่โลกแห่งความบันเทิงที่ไม่มีวันหลับใหลไปกับเกม &lt;strong&gt;สล็อต&lt;/strong&gt; ที่ดีที่สุดบน &lt;strong&gt;UFABET&lt;/strong&gt; เว็บตรงที่มีความมั่นคงทางการเงินและระบบที่ทันสมัยที่สุดในไทยอย่ารอช้า! &lt;strong&gt;สมัครสมาชิก&lt;/strong&gt; และ &lt;strong&gt;เข้าสู่ระบบ&lt;/strong&gt; เพื่อคว้าโอกาสเป็นเศรษฐีคนใหม่จากการปั่นสล็อตกับเราวันนี้ พร้อมรับ &lt;strong&gt;โปรโมชั่น&lt;/strong&gt; สุดพิเศษที่รอคุณอยู่เพียบ!&lt;/p&gt;
  </content>
    <author>
      <name>Puppet Blog</name>
      <uri>http://www.puppetblog.nl/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Silent is not idle</title>
    <link href="https://example42.com/blog/2026/03/14/silent-is-not-idle/"/>
    <id>https://example42.com/blog/2026/03/14/silent-is-not-idle</id>
    <updated>2026-03-14T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    Pabawi is Growing Fast: from v0.4 to v0.8 in Two Months

Two months ago I wrote about Pabawi, a web frontend for classic infrastructures. At the time, v0.4.0 had just shipped with Bolt, PuppetDB, PuppetServer and Hiera integrations. It was a working prototype — useful, but rough around the edges.

Today, four releases later, Pabawi is a different animal. This post is a catch-up: what changed, where we’re going, and why I think this project matters more than ever.

What happened in two months

Development moved fast. Here’s what each release brought:

v0.5.0 — Deeper Puppet visibility
Report filtering, Puppet run history visualization with timeline view, and an enhanced expert mode with frontend logging. If you use PuppetDB seriously, this version made Pabawi genuinely useful for day-to-day operations.

v0.6.0 — Foundations
Mostly internal: code consolidation, architecture cleanup, bug fixing. Not glamorous, but necessary. A project that grows this fast needs these stabilization moments.

v0.7.0 — Ansible joins the party
This was a big one. Ansible integration landed, meaning Pabawi is no longer a “Puppet tool” — it’s a multi-tool. If you run a mixed environment (and most people do), you can now see and act on Ansible-managed nodes from the same interface. Also: class-aware Hiera lookups, which makes the Hiera Data Browser significantly more useful.

v0.8.0 — RBAC and SSH
Role-based access control. Multiple users, controlled access, audit trail. This is what takes Pabawi from “personal tool on localhost” to something you can actually deploy for a team. SSH integration also landed, adding direct SSH-based node management and expanding the inventory sources available.

The bigger picture

When I started Pabawi, the goal was simple: I wanted a modern OSS web UI for Puppet and Bolt. The classic infrastructure space — physical servers, VMs, decades of accumulated systems — is enormous, but it’s been left behind by tooling.

Everyone builds for Kubernetes. Everyone builds for cloud-native. But the people managing 200 bare-metal servers running CentOS 7 with Puppet? They get a CLI and a prayer.

Pabawi is trying to fix that. Not by replacing Puppet or Ansible or Bolt — those tools are excellent at what they do. But by giving you a single, modern web interface to work with all of them together.

The vision: one pane of glass for classic infrastructure.

What’s coming

Proxmox integration is in active development. If you manage VMs on Proxmox — and a lot of people do — this will let you see and interact with your virtualization layer alongside your configuration management data.

After that, the roadmap extends further:


  Icinga / CheckMK — bring monitoring context into the same interface
  Terraform — infrastructure provisioning alongside configuration management
  EC2 / Azure — for hybrid environments that span on-prem and cloud
  Choria, scheduled executions, custom dashboards


The pattern is consistent: wherever you have servers you care about, Pabawi should be able to see them and help you act on them.

Try it

Getting started is straightforward. Clone the repo and run the setup script:

git clone https://github.com/example42/pabawi
cd pabawi
./scripts/setup.sh


Or use Docker:

docker run -d \
  --name pabawi \
  -p 127.0.0.1:3000:3000 \
  -v &quot;$(pwd)/pabawi:/pabawi&quot; \
  --env-file &quot;.env&quot; \
  example42/pabawi:latest


Full docs at github.com/example42/pabawi.

Give it a try — and tell me what you think

If Pabawi looks useful for your infrastructure, the best thing you can do right now is try it and tell me what’s missing.

Clone it, run ./scripts/setup.sh, and connect it to your existing Puppet, Bolt or Ansible setup. It should take less than 30 minutes to have something running. If it doesn’t, that’s already useful feedback.

👉 github.com/example42/pabawi

If you find it useful, a GitHub star goes a long way for an open source project. If you find something broken or missing, open an issue — the roadmap is driven by real users.

Want help setting it up? I’m still offering a free shared-screen setup session — 30 minutes, no strings attached. We get it running in your environment, connected to your tools. Reach out on LinkedIn to schedule a call.

On behalf of Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Announcing Pabawi, a web frontend for classic infrastructures</title>
    <link href="https://example42.com/blog/2026/01/16/announcing-pabawi-a-web-frontend-to-classic-infrastructures/"/>
    <id>https://example42.com/blog/2026/01/16/announcing-pabawi-a-web-frontend-to-classic-infrastructures</id>
    <updated>2026-01-16T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    Is there still space in AI and Cloud native days for a new tool to manage classic infrastructures based on physical or virtual servers?

If you handle good old servers, and use Puppet, Bolt or Ansible to configure and manage them, then the answer might be yes, and we may have a nice tool for you.

Pabawi is a new, modern, sleek web frontend to inventory your systems, check their configuration management status, and run actions on them.

Needless to sat that it’s also Open Source.

Version 0.4.0, just released after a few weeks of AI based coding, has integrations with Bolt, Hiera, PuppetDB and PuppetServer.

Current version is expected to be executed by a Puppet developer or user on her/his localhost, featuring:


  Multi-Source Inventory: View and manage nodes from Bolt inventory and PuppetDB
  Command Execution: Run ad-hoc commands on remote nodes with whitelist security
  Task Execution: Execute Bolt tasks with automatic parameter support
  Puppet Integration: Trigger Puppet agent runs with full configuration control
  Package Management: Install and manage packages across your infrastructure
  Execution History: Track all operations with detailed results and re-execution capability
  Dynamic Inventory: Automatically discover nodes from PuppetDB
  Node Facts: View comprehensive system information from Puppet agents
  Puppet Reports: Browse detailed Puppet run reports with metrics and resource changes
  Catalog Inspection: Examine compiled Puppet catalogs and resource relationships
  Event Tracking: Monitor individual resource changes and failures over time
  PQL Queries: Filter nodes using PuppetDB Query Language
  Hiera Data Browser: Explore hierarchical configuration data and key usage analysis
  Puppet code analysis: Analyses Puppet code base, reporting class usage, lint issues, outdated modules in Puppetfile


More robust users management and RBAC, and Ansible and other tools integrations are planned for future versions.

Check the git repository for installation instructions and details.

Screenshots

Here are some screenshots, from a test setup with few nodes. The interface adapts well and is still fast on way larger setups (currently tested with infrastructures up to 350 nodes).

Nodes Inventory



Node inventory with multi-source support, blazing fast search and filtering options

Task Execution



Bolt task execution interface with automatic parameters discovery

Executions Tracking



Execution history with filtering and detailed execution results with re-run capabilities

Node Details



Node detail page with access to facts, reports, events, managed resources and other useful info

Puppet reports



Puppet reports view, with run times and number of affected resources

Free installation service (time limited)

Pabawi is easy to integrate in your existing Puppet / Bolt infrastructure and has contextual setup instructions for each integration, but if you want help in trying it in your infrastructure, example42 offers a launch special, time limited, free installation service, no strings attached.

In a shared screen call we can setup Pabawi together, either using its container image or via npm.

We want to test and validate it in different conditions, collecting users’ feedback and suggestions.

Just contact me on LinkedIN for planning a call: half an hour should be enough to set it up and see how it works.

Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">R.I.Pienaar - www.devco.net: Choria Hierarchical Data</title>
    <link href="https://www.devco.net/posts/2025/11/30/choria_hierarchical_data/"/>
    <id>https://www.devco.net/posts/2025/11/30/choria_hierarchical_data/</id>
    <updated>2025-11-24T23:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;As most are aware, I created the widely used Hiera system in Puppet. I &lt;a href=&quot;https://www.devco.net/archives/2011/06/05/hiera_a_pluggable_hierarchical_data_store.php&quot;&gt;introduced it in 2011&lt;/a&gt;, and it has since become essentially the only way to use Puppet in any meaningful fashion. Given its widespread adoption, I donated the code to Puppet, and it became integrated with Puppet core.&lt;/p&gt;
&lt;p&gt;Unfortunately, during this integration we lost some key values—the command line and the ability to use it in scripts and elsewhere.&lt;/p&gt;
&lt;p&gt;Meanwhile, our world is changing, and we are ever more focussing on small, single purpose compute. I intend to create a new kind of Configuration Management system that is focussed on the small, single, purpose needs. Filling the gap that has always existed - how to manage our applications rather than systems, an area Puppet has always been weak at.&lt;/p&gt;
&lt;p&gt;There is then still the need for hierarchical data and given that I have the flexibility to start completely fresh I am at best taking some inspiration from Hiera.&lt;/p&gt;
&lt;p&gt;So today I&amp;rsquo;ll introduce a new tool called Choria Hierarchical Data - current code name &lt;code&gt;tinyhiera&lt;/code&gt; but that might change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; This post was first posted &lt;a href=&quot;https://choria.io/blog/post/2025/11/25/hierarchical_data/&quot;&gt;on the Choria blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Read on for the details&lt;/p&gt;
&lt;p&gt;The big difference here is that this new system supports just one data file, you express a data structure that is the complete outcome of the query and then configure in the same file how to override and extend that data file.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at it, here I&amp;rsquo;ll show the different sections, in reality it&amp;rsquo;s just one file.&lt;/p&gt;
&lt;p&gt;This is the data we wish to manage:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;nt&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;httpd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;listen_port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lets define the hierarchy for overriding this data; this should be familiar to Puppet users:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hierarchy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;deep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# or first&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;env:{{ lookup(&amp;#39;env&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;role:{{ lookup(&amp;#39;role&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;host:{{ lookup(&amp;#39;hostname&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally, we show the overrides, here we just specify how the data will be extended:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;nt&quot;&gt;overrides&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;env:prod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;WARN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;role:web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;nginx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;listen_port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;host:web01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;TRACE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;listen_port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can now call our CLI tool to resolve this entire structure.&lt;/p&gt;
&lt;p&gt;We query the data setting the &lt;code&gt;role&lt;/code&gt; fact to &lt;code&gt;web&lt;/code&gt;, the data from the &lt;code&gt;role:web&lt;/code&gt; section is merged into the data from the &lt;code&gt;data&lt;/code&gt; section:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ tinyhiera parse data.yaml role=web
{
  &amp;#34;log_level&amp;#34;: &amp;#34;INFO&amp;#34;,
  &amp;#34;packages&amp;#34;: [
    &amp;#34;ca-certificates&amp;#34;,
    &amp;#34;nginx&amp;#34;
  ],
  &amp;#34;web&amp;#34;: {
    &amp;#34;listen_port&amp;#34;: 443,
    &amp;#34;tls&amp;#34;: true
  }
}
$ tinyhiera parse data.yaml role=web --query web.listen_port
443
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;features-and-differences&quot;&gt;Features and Differences&lt;/h2&gt;
&lt;p&gt;This is the basic feature, similar to what you might have used in Puppet except differing in a few areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s all one file and one data structure, it&amp;rsquo;s not key-lookup orientated. The entire outcome is rendered in one query&lt;/li&gt;
&lt;li&gt;Expressions used in overrides and data lookups are a full query language called &lt;a href=&quot;https://expr-lang.org&quot;&gt;expr-lang&lt;/a&gt;, this means we can call many functions, generate data, derive data and easily extend this over time&lt;/li&gt;
&lt;li&gt;The data is typed, if your facts have rich data the results can also be rich data, if &lt;code&gt;value&lt;/code&gt; is a integer, array or map the result will be the same type &lt;code&gt;x=&amp;quot;{{ lookup(&amp;quot;value&amp;quot;) }}&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The CLI has built in system facts, can take facts on the CLI, read them from JSON and YAML files, parse your environment variables as facts or all at the same time&lt;/li&gt;
&lt;li&gt;The CLI can emit data as environment variables for use in scripts&lt;/li&gt;
&lt;li&gt;There is a Golang library to use it in your own code&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;environment-variable-output&quot;&gt;Environment Variable Output&lt;/h2&gt;
&lt;p&gt;We want this to be usable in scripts, the easiest might be dig into the data and just get a single value:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;PORT=$(tinyhiera parse data.yaml role=web --query web.listen_port)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But this involves many calls to the command, we can instead emit all the data as variables.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ tinyhiera parse data.yaml role=web --env
HIERA_LOG_LEVEL=INFO
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can see the data is emitted as environment variables that you can just source into your shell script, obviously in this use case it benefits to have flat data.&lt;/p&gt;
&lt;h2 id=&quot;built-in-facts&quot;&gt;Built-in facts&lt;/h2&gt;
&lt;p&gt;We have many ways to get facts into it such as files, environment variables, JSON and YAML files. The CLI also includes it&amp;rsquo;s own mini fact source called &lt;code&gt;system&lt;/code&gt; which will return facts about the system it is running on.&lt;/p&gt;
&lt;p&gt;To view the facts fully resolved, we can run the following, I am removing much of the details here but you can see this offers enough to make most required decisions:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ tinyhiera facts --system-facts
{
  &amp;#34;host&amp;#34;: {
    &amp;#34;info&amp;#34;: {
      &amp;#34;hostname&amp;#34;: &amp;#34;grime.local&amp;#34;,
      &amp;#34;uptime&amp;#34;: 1843949,
      &amp;#34;bootTime&amp;#34;: 1762258234,
      &amp;#34;procs&amp;#34;: 711,
      &amp;#34;os&amp;#34;: &amp;#34;darwin&amp;#34;,
      &amp;#34;platform&amp;#34;: &amp;#34;darwin&amp;#34;,
      &amp;#34;platformFamily&amp;#34;: &amp;#34;Standalone Workstation&amp;#34;,
      &amp;#34;platformVersion&amp;#34;: &amp;#34;26.1&amp;#34;,
      &amp;#34;kernelVersion&amp;#34;: &amp;#34;25.1.0&amp;#34;,
      &amp;#34;kernelArch&amp;#34;: &amp;#34;arm64&amp;#34;
    }
  },
  &amp;#34;memory&amp;#34;: {
    &amp;#34;swap&amp;#34;: { },
    &amp;#34;virtual&amp;#34;: { }
  },
  &amp;#34;network&amp;#34;: {
    &amp;#34;interfaces&amp;#34;: [ ]
  },
  &amp;#34;partition&amp;#34;: {
    &amp;#34;partitions&amp;#34;: [ ],
    &amp;#34;usage&amp;#34;: [ ]
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;long-term-view&quot;&gt;Long-term view&lt;/h2&gt;
&lt;h3 id=&quot;autonomous-agents&quot;&gt;Autonomous Agents&lt;/h3&gt;
&lt;p&gt;The goal here is to incorporate this into various other places, first we pull it into a &lt;a href=&quot;https://choria.io/docs/autoagents/&quot;&gt;Choria Autonomous Agents&lt;/a&gt;, these agents own the full lifecycle of an application:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy dependencies&lt;/li&gt;
&lt;li&gt;Configure the application&lt;/li&gt;
&lt;li&gt;Run the application&lt;/li&gt;
&lt;li&gt;Monitor the application&lt;/li&gt;
&lt;li&gt;Restart the application on failure&lt;/li&gt;
&lt;li&gt;Orchestrate rolling upgrades&lt;/li&gt;
&lt;li&gt;Present APIs for interacting with the management layer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Agents can fetch data from a Key-Value store, but it&amp;rsquo;s kind of all the same data. With Hiera integrated into the KV feature, we get:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;watch_tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;10s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;success_transition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;regional_update&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;state_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;RUN, RESTART]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;NATS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hiera_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here the autonomous agent will check KV every 10 seconds for data, fully resolve it using Hiera and save the resulting data into the Autonomous Agent internal data store. If the KV data chanfes or the referenced facts change to the extent that the data change, the machine will update the stored data and fire the &lt;code&gt;regional_update&lt;/code&gt; event.&lt;/p&gt;
&lt;p&gt;This way we can create role-orientated specific data all in the same Key-Value key.&lt;/p&gt;
&lt;h3 id=&quot;configuration-management&quot;&gt;Configuration Management&lt;/h3&gt;
&lt;p&gt;I want to create a new CM system that takes the model we are used to in Puppet but brings it to scripts and reusable APIs.&lt;/p&gt;
&lt;p&gt;The script use case would essentially be standalone commands:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ marionette package ensure zsh --version 1.2.3
$ marionette service ensure ssh-server --enable --running
$ marionette service info httpd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This should be familiar to Puppet users, we&amp;rsquo;re basically pulling the resources and RAL into standalone commands.  The commands will be fully idempotent like Puppet and support multiple Operating Systems.&lt;/p&gt;
&lt;p&gt;For integration with other languages, you can also deal with JSON-in-JSON-out style:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;# Can be driven by JSON in and returns JSON out instead
$ echo &amp;#39;{&amp;#34;name&amp;#34;:&amp;#34;zsh&amp;#34;, &amp;#34;version&amp;#34;:&amp;#34;1.2.3&amp;#34;}&amp;#39;|marionette package apply
{
....
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I do not want to create something huge like Puppet, but just basically enough that can enable the package-config-service trio pattern, ultimately a manifest would look like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;l&quot;&gt;cat &amp;lt;&amp;lt;EOF&amp;gt;manifest.yaml&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;myapp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;myapp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hierarchy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;deep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;platform:{{ lookup(&amp;#39;host.info.platform&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;hostname:{{ lookup(&amp;#39;host.info.hostname&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;overrides&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;platform:darwin&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;MyApp-1.2.3.dmg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;$ marionette apply manifest.json --system-facts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pull this into Autonomous Agents and you have self-contained, self-managing applications similar to Kubernetes Operators but anywhere.&lt;/p&gt;
&lt;p&gt;Use this in scripts, simple setups, during container creation or orchestration, or even in CI pipelines.&lt;/p&gt;
&lt;p&gt;We should even be able to compile this manifest, or a full autonomous agent, into a single, static, binary that you can just run as &lt;code&gt;./setup&lt;/code&gt; or &lt;code&gt;./manage&lt;/code&gt; and the entire lifecycle is managed with zero dependencies.&lt;/p&gt;
&lt;h2 id=&quot;availability-and-status&quot;&gt;Availability and Status&lt;/h2&gt;
&lt;p&gt;You can get the command on &lt;a href=&quot;https://github.com/choria-io/tinyhiera&quot;&gt;GitHub choria-io/tinyhiera&lt;/a&gt;, we are pretty far along but expect some more breaking changes including a potential name change.&lt;/p&gt;
  </content>
    <author>
      <name>R.I.Pienaar - www.devco.net</name>
      <uri>http://www.devco.net/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: The new gem server in town</title>
    <link href="https://overlookinfratech.com/2025/10/06/the-new-gem-server-in-town/"/>
    <id>https://overlookinfratech.com/2025/10/06/the-new-gem-server-in-town</id>
    <updated>2025-10-06T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;If you’re in the Ruby world you may have been following the RubyGems shenanigans
of the last few weeks. They suffered a poorly communicated corporate led rug-pull,
not all that dissimilar to &lt;a href=&quot;/2024/11/08/sequestered-source/&quot;&gt;the games Perforce has played&lt;/a&gt;
over the last year or so. Amusingly, they also claimed it was for security
purposes without providing any actual rationale supporting that claim.&lt;/p&gt;

&lt;p&gt;As you can guess, this caused a lot of consternation, especially in projects like
&lt;a href=&quot;https://voxpupuli.org/openvox&quot;&gt;OpenVox&lt;/a&gt; based on Ruby and in companies like
Overlook InfraTech whose livelihood depends on Ruby.&lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
  &lt;p&gt;You may want to read that last bit again. This heavily impacts companies whose
&lt;strong&gt;&lt;em&gt;livelihoods depend on Ruby.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ah. Now that’s the root of the problem, isn’t it? There aren’t enough companies
financially supporting the projects they’ve built their own businesses on top of.
For example, Puppetserver would not be possible without JRuby and yet
Puppet/Perforce didn’t bother helping the project through &lt;a href=&quot;https://blog.jruby.org/2024/07/independence-day&quot;&gt;its funding
crisis&lt;/a&gt;
until &lt;a href=&quot;/2025/02/28/paying-their-fair-share/&quot;&gt;they were publicly named and shamed&lt;/a&gt;
and it became a PR problem for them.&lt;/p&gt;

&lt;p&gt;We don’t believe in doing business that way and we now have an opportunity to
live up to our ideals. The original RubyGems team has &lt;a href=&quot;https://www.fullstackruby.dev/ruby-infrastructure/2025/10/06/theres-a-new-gem-server-in-town/&quot;&gt;announced a community led
and governed gem server&lt;/a&gt;.
This means that we now have the opportunity to help build a new ecosystem that’s
not supported and dominated by a single company out of touch with its community.&lt;/p&gt;

&lt;blockquote class=&quot;mastodon-embed&quot; data-embed-url=&quot;https://ruby.social/@getajobmike/115254758995805006/embed&quot; style=&quot;background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;&quot;&gt; &lt;a href=&quot;https://ruby.social/@getajobmike/115254758995805006&quot; target=&quot;_blank&quot; style=&quot;align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Oxygen, Ubuntu, Cantarell, &#39;Fira Sans&#39;, &#39;Droid Sans&#39;, &#39;Helvetica Neue&#39;, Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 79 75&quot;&gt;&lt;path d=&quot;M63 45.3v-20c0-4.1-1-7.3-3.2-9.7-2.1-2.4-5-3.7-8.5-3.7-4.1 0-7.2 1.6-9.3 4.7l-2 3.3-2-3.3c-2-3.1-5.1-4.7-9.2-4.7-3.5 0-6.4 1.3-8.6 3.7-2.1 2.4-3.1 5.6-3.1 9.7v20h8V25.9c0-4.1 1.7-6.2 5.2-6.2 3.8 0 5.8 2.5 5.8 7.4V37.7H44V27.1c0-4.9 1.9-7.4 5.8-7.4 3.5 0 5.2 2.1 5.2 6.2V45.3h8ZM74.7 16.6c.6 6 .1 15.7.1 17.3 0 .5-.1 4.8-.1 5.3-.7 11.5-8 16-15.6 17.5-.1 0-.2 0-.3 0-4.9 1-10 1.2-14.9 1.4-1.2 0-2.4 0-3.6 0-4.8 0-9.7-.6-14.4-1.7-.1 0-.1 0-.1 0s-.1 0-.1 0 0 .1 0 .1 0 0 0 0c.1 1.6.4 3.1 1 4.5.6 1.7 2.9 5.7 11.4 5.7 5 0 9.9-.6 14.8-1.7 0 0 0 0 0 0 .1 0 .1 0 .1 0 0 .1 0 .1 0 .1.1 0 .1 0 .1.1v5.6s0 .1-.1.1c0 0 0 0 0 .1-1.6 1.1-3.7 1.7-5.6 2.3-.8.3-1.6.5-2.4.7-7.5 1.7-15.4 1.3-22.7-1.2-6.8-2.4-13.8-8.2-15.5-15.2-.9-3.8-1.6-7.6-1.9-11.5-.6-5.8-.6-11.7-.8-17.5C3.9 24.5 4 20 4.9 16 6.7 7.9 14.1 2.2 22.3 1c1.4-.2 4.1-1 16.5-1h.1C51.4 0 56.7.8 58.1 1c8.4 1.2 15.5 7.5 16.6 15.6Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt; &lt;div style=&quot;color: #787588; margin-top: 16px;&quot;&gt;Post by @getajobmike@ruby.social&lt;/div&gt; &lt;div style=&quot;font-weight: 500;&quot;&gt;View on Mastodon&lt;/div&gt; &lt;/a&gt; &lt;/blockquote&gt;
&lt;script data-allowed-prefixes=&quot;https://ruby.social/&quot; async=&quot;&quot; src=&quot;https://ruby.social/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;They don’t currently have a way to sponsor the project, so we are using GitHub
Sponsors to throw down &lt;a href=&quot;https://github.com/orgs/overlookinfra/sponsoring&quot;&gt;$100USD monthly for each of the current developer team&lt;/a&gt;.
We encourage you and/or your employers to do the same by clicking the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{sponsor}&lt;/code&gt;
icon on each of &lt;a href=&quot;https://github.com/orgs/gem-coop/people&quot;&gt;these GitHub profiles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s to a better and more resilient Ruby ecosystem going forward!&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Camptocamp Blog: Platform Engineering at CNS Munich</title>
    <link href="https://dev.to/camptocamp-ops/platform-engineering-at-cns-munich-20f6"/>
    <id>https://dev.to/camptocamp-ops/platform-engineering-at-cns-munich-20f6</id>
    <updated>2025-08-26T14:33:58+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This year Cloud Native Summit (CNS) in Munich gathered adopters and technologists from open source and cloud native communities.&lt;/p&gt;

&lt;p&gt;Compared to last year&#39;s Kubernetes Communities Days (KCD), the agenda dedicated many slots to Platform Engineering. This discipline can be seen as a way to fill gaps between different stakeholders of an Internal Developer Platform (IDP). Last year I wrote an article on this topic at the KCD Munich. This year I would like to share fresh ideas or principles from some talks given during the conference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying problems before solutions
&lt;/h2&gt;

&lt;p&gt;Day 1 opened with a presentation of &quot;Product thinking&quot; approach made by &lt;em&gt;Stéphane Di Cesare&lt;/em&gt; (Deutsche Kreditbank) and &lt;em&gt;Dominik Schmidle&lt;/em&gt; (Giant Swarm). They reminded some core principles and techniques of &quot;Platform Engineering&quot; to manage the link between problem space and solution space. &lt;/p&gt;

&lt;p&gt;Day 1 also closed with a panel discussion on &quot;Platform Engineering&quot; animated by &lt;em&gt;Max Körbächer&lt;/em&gt; (Liquid Reply),  focused on the adoption of IDPs by end users and within projects.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=g-sZwa52DNE&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=1&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 1 Platform Thinking&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=MpU-vo4K7BQ&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 2 Panel Discussion&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AI workloads on K8s
&lt;/h2&gt;

&lt;p&gt;&quot;Use platform engineering for AI workloads on K8s&quot;, that was the motto of the presentation given by &lt;em&gt;Mario-Leander Reimer&lt;/em&gt; (QAware GmbH). He  tackled emerging challenges for deploying Agentic AI workloads and introduced an example of what a platform should cover including quality plane and compliance plane. His talk is part of an open source initiative launched at QAware this year to mature the concepts of a dedicated control plane: the agentic layer.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=pg2DKYc9n_o&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=24&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 3 Architecting and Building a K8s-based AI Platform&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Composable platforms on Kubernetes
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Hossein Salahi&lt;/em&gt; (Liquid Reply) presented a reference implementation to streamline the delivery of services for developers. Some of the challenges were to link dev and ops, to manage services in configuration, to orchestrate infrastructure deployment and to manage access. For that purpose the open source tools Kratix codify a contract between dev and ops. Combined with Backstage, this abstraction allows services to be packaged and delivered to developers.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aLdgVrnMxcs&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=10&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 4  Modular Platform Engineering with Kratix and Backstage&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce cognitive workloads
&lt;/h2&gt;

&lt;p&gt;Interestingly the problem of cognitive workloads due to some platform complexity was a recurring theme. But &lt;em&gt;Michel Murabito&lt;/em&gt; (Mia Platform) positioned platform engineering as a way to reduce cognitive workloads. He highlighted essential building blocks and features that can reduce mental burden. &lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=iSMk7a62wUc&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=39&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 5 Creating a smooth Developer Experience&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Involved user as contributor
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Lian Li&lt;/em&gt; (lianmakesthings) told us a story of what can go wrong when building a platform based on real examples and some facts. She advocated some receipts that can improve communication among teams in an organisation and  explain some benefits of having contributing users in the loop.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=4CcNPHT_-nA&amp;amp;t=1597s&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 6 Many Cooks, One Platform: Balancing Ownership and Contribution for the Perfect Broth&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Convergence
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Evelyn Osman&lt;/em&gt; (enmacc) discussed the importance of convergence when building a platform in order to avoid fragmentation in teams and solutions. She shared some important steps that foster innovations and better meet user expectations.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=EztpUoi0hgU&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=27&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 7 Convergence on Platforms&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is not an exhaustive list of what was discussed, but somehow it made it clear that Platform Engineering approach can help picking the right solution in the open source landscape. Some enablers already provide a strong integration with Kubernetes to develop and publish new services on top of it. And depending on the organization, some shift in practices also might be required to follow the evolution of the platform at build and runtime. The conference was a great place to exchange with the community on technical and organization challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kcdmunich.de/schedule/&quot; rel=&quot;noopener noreferrer&quot;&gt;Agenda of the conference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/xavier_rakotomamonjy/platform-engineering-at-kcd-munich-25i1&quot;&gt;Platform Engineering at KCD Munich&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://landscape.cncf.io/?group=cnai&quot; rel=&quot;noopener noreferrer&quot;&gt;CNCF landscape for AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.agentic-layer.ai/&quot; rel=&quot;noopener noreferrer&quot;&gt;Agentic Layer by QAware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kratix.io&quot; rel=&quot;noopener noreferrer&quot;&gt;kratix.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
  </content>
    <author>
      <name>Camptocamp Blog</name>
      <uri>https://dev.to/camptocamp-ops</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: 🧩 Puppet Module Update Process</title>
    <link href="https://dev.to/betadots/puppet-module-update-process-3f54"/>
    <id>https://dev.to/betadots/puppet-module-update-process-3f54</id>
    <updated>2025-07-11T07:59:30+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;h2&gt;
  
  
  🔍 1. Identify and Analyze the Module
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Extract a module name from the &lt;code&gt;Puppetfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Search for the module on &lt;a href=&quot;https://forge.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet Forge&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Compare available versions and identify the latest one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📝 2. Review Changes in the Module
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Follow the &lt;strong&gt;Project URL&lt;/strong&gt; on the Forge page to the GitHub repository.&lt;/li&gt;
&lt;li&gt;Check recent changes under &lt;strong&gt;Releases&lt;/strong&gt; or in the &lt;strong&gt;Changelog&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forge.puppet.com/modules/puppet/systemd/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;Forge: puppet/systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voxpupuli/puppet-systemd/releases&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voxpupuli/puppet-systemd/blob/master/CHANGELOG.md&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ 3. Watch for Breaking Changes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Possible breaking changes:

&lt;ul&gt;
&lt;li&gt;Removal of support for EOL software&lt;/li&gt;
&lt;li&gt;API changes&lt;/li&gt;
&lt;li&gt;Renamed parameters or variables&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Note: Not every breaking change will affect your setup.

&lt;ul&gt;
&lt;li&gt;Example: Dropping EL6 support likely doesn’t concern you.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 4. Evaluate Other Changes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Linked pull requests (PRs) may offer additional insights.&lt;/li&gt;
&lt;li&gt;Check whether changes are understandable and whether breaking changes apply to your environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌱 5. Integrate into Development Branches
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If the module is considered ready for update:

&lt;ul&gt;
&lt;li&gt;Integrate into a &lt;strong&gt;feature&lt;/strong&gt; or &lt;strong&gt;development&lt;/strong&gt; branch.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌲 6. Control Repo Branch Structure
&lt;/h2&gt;

&lt;p&gt;Typically present:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;development&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;production&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Optionally: &lt;code&gt;staging&lt;/code&gt; before &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Additionally: 0–n &lt;strong&gt;feature branches&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 7. Testing in Development Environment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test the updated module in a suitable environment (VM, container).&lt;/li&gt;
&lt;li&gt;Observe how it interacts with other modules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Possible findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dependencies on specific module versions&lt;/li&gt;
&lt;li&gt;New facts writing additional data to PuppetDB&lt;/li&gt;
&lt;li&gt;New or modified parameters requiring Hiera data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔄 8. Forward the Change
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Once all adjustments are complete:

&lt;ul&gt;
&lt;li&gt;Pass the change to the next branch for further testing or rollout preparation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🤖 9. Automation with Renovate Bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This process is quite detailed and time-consuming.&lt;/li&gt;
&lt;li&gt;With &lt;a href=&quot;https://www.mend.io/renovate/&quot; rel=&quot;noopener noreferrer&quot;&gt;Renovate Bot&lt;/a&gt;, collecting and reviewing relevant updates becomes much easier.&lt;/li&gt;
&lt;li&gt;Renovate can be integrated into GitLab or GitHub.&lt;/li&gt;
&lt;li&gt;It works similarly to GitHub’s built-in &lt;a href=&quot;https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically&quot; rel=&quot;noopener noreferrer&quot;&gt;Dependabot&lt;/a&gt;, but is more flexible and configurable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧰 10. Automation with VoxBox
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You can also try using the Vox Pupuli VoxBox to list all dependencies for the entire control repo.

&lt;ul&gt;
&lt;li&gt;This doesn&#39;t work with every setup.&lt;/li&gt;
&lt;li&gt;If there are private modules in the Puppetfile, the person running the command must have access to them.&lt;/li&gt;
&lt;li&gt;Example command:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;podman run -it --rm -v $PWD:/repo:Z ghcr.io/voxpupuli/voxbox:latest r10k:dependencies&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;More useful information can be found in the &lt;a href=&quot;https://github.com/voxpupuli/container-voxbox&quot; rel=&quot;noopener noreferrer&quot;&gt;Vox Pupuli VoxBox documentation&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: 🧩 Puppet Modul Update Prozess</title>
    <link href="https://dev.to/betadots/puppet-modul-update-prozess-3mek"/>
    <id>https://dev.to/betadots/puppet-modul-update-prozess-3mek</id>
    <updated>2025-07-11T07:58:40+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;h2&gt;
  
  
  🔍 1. Modul identifizieren und analysieren
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Einen Modulnamen aus dem &lt;code&gt;Puppetfile&lt;/code&gt; entnehmen.&lt;/li&gt;
&lt;li&gt;In der &lt;a href=&quot;https://forge.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet Forge&lt;/a&gt; nach dem Modul suchen.&lt;/li&gt;
&lt;li&gt;Verfügbare Versionen vergleichen und die neueste identifizieren.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📝 2. Änderungen im Modul prüfen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Über den Link zur &lt;strong&gt;Project URL&lt;/strong&gt; auf der Forge-Seite zum GitHub-Repository wechseln.&lt;/li&gt;
&lt;li&gt;Dort unter &lt;strong&gt;Releases&lt;/strong&gt; oder im &lt;strong&gt;Changelog&lt;/strong&gt; die letzten Änderungen prüfen:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forge.puppet.com/modules/puppet/systemd/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;Forge: puppet/systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voxpupuli/puppet-systemd/releases&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voxpupuli/puppet-systemd/blob/master/CHANGELOG.md&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ 3. Auf Breaking Changes achten
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mögliche Breaking Changes:

&lt;ul&gt;
&lt;li&gt;Entfernung der Unterstützung für EOL-Software&lt;/li&gt;
&lt;li&gt;Änderungen an der API&lt;/li&gt;
&lt;li&gt;Umbenennung von Variablen&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Hinweis: Nicht jede Breaking Change ist für jedes Setup relevant.

&lt;ul&gt;
&lt;li&gt;Beispiel: Der Entfall von EL6-Support betrifft euch vermutlich nicht.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 4. Sonstige Änderungen bewerten
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Auch verlinkte Pull Requests (PRs) können Aufschluss geben.&lt;/li&gt;
&lt;li&gt;Prüfen, ob Änderungen nachvollziehbar sind und keine Breaking Changes enthalten, die euch betreffen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌱 5. Aufnahme in Entwicklungszweige
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wenn das Modul als updatefähig eingeschätzt wird:

&lt;ul&gt;
&lt;li&gt;Aufnahme in einen &lt;strong&gt;Feature-&lt;/strong&gt; oder &lt;strong&gt;Development-Branch&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌲 6. Branch-Struktur im Control-Repo
&lt;/h2&gt;

&lt;p&gt;Typischerweise vorhanden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;development&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;staging&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;production&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Zusätzlich: 0–n &lt;strong&gt;Feature-Branches&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 7. Tests in Entwicklungsumgebung
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test des neuen Moduls in geeigneter Umgebung (VM, Container)&lt;/li&gt;
&lt;li&gt;Beobachtung des Zusammenspiels mit anderen Modulen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mögliche Erkenntnisse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abhängigkeit zu anderen Modul-Versionen&lt;/li&gt;
&lt;li&gt;Neue Facts, die zusätzliche Daten in die PuppetDB schreiben&lt;/li&gt;
&lt;li&gt;Neue oder geänderte Parameter, die Hiera-Daten erfordern&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔄 8. Change weiterreichen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wenn alle Anpassungen erledigt sind:

&lt;ul&gt;
&lt;li&gt;Übergabe des Changes in den nächsten Branch zur weiteren Prüfung oder Vorbereitung für den Rollout.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🤖 9. Automatisierung mit Renovate Bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Dieser Prozess ist sehr kleinteilig und kann zeitaufwendig sein.&lt;/li&gt;
&lt;li&gt;Mit Hilfe von &lt;a href=&quot;https://www.mend.io/renovate/&quot; rel=&quot;noopener noreferrer&quot;&gt;Renovate Bot&lt;/a&gt; lässt sich das Sichten und Sammeln der relevanten Informationen deutlich erleichtern.&lt;/li&gt;
&lt;li&gt;Renovate ist ein Bot, der sich in GitLab oder GitHub integrieren lässt.&lt;/li&gt;
&lt;li&gt;Er funktioniert ähnlich wie der GitHub-eigene &lt;a href=&quot;https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically&quot; rel=&quot;noopener noreferrer&quot;&gt;Dependabot&lt;/a&gt;, ist aber deutlich flexibler und konfigurierbarer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧰 10. Automatisierung mit VoxBox
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Man kann auch versuchen mit der Vox Pupuli Voxbox die Abhängigkeiten für das gesamte Control-Repo anzeigen zu lassen.

&lt;ul&gt;
&lt;li&gt;Das klappt nicht bei jedem Setup.&lt;/li&gt;
&lt;li&gt;Wenn im Puppetfile private Module sind, muss der jenige der es ausführt, Zugriff auf diese haben.&lt;/li&gt;
&lt;li&gt;Beispielbefehl:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;podman run -it --rm -v $PWD:/repo:Z ghcr.io/voxpupuli/voxbox:latest r10k:dependencies&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Weitere nützliche Informationen gibt es in der &lt;a href=&quot;https://github.com/voxpupuli/container-voxbox&quot; rel=&quot;noopener noreferrer&quot;&gt;Vox Pupuli VoxBox Dokumentation&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: So much for being developer first</title>
    <link href="https://overlookinfratech.com/2025/06/12/so-much-for-being-developer-first/"/>
    <id>https://overlookinfratech.com/2025/06/12/so-much-for-being-developer-first</id>
    <updated>2025-06-12T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Well, it appears that Perforce has taken another step down the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Enshittification&quot;&gt;enshittification&lt;/a&gt; journey. For
some strange reason, it appears that they don’t want you writing Puppet modules
unless you’re a paying customer. That’s right, they killed the open source PDK
and sequestered the source away, just like they did with Puppet. They would like
you to pay for the privilege of creating content for them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/pdk_enshittified.jpeg&quot; alt=&quot;PDK release announcement&quot; class=&quot;img right w30&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is a very strange strategic move on their part. Their ecosystem is almost
entirely supported by community authors building and maintaining Puppet modules.
A full 96% of their paying customers use modules supported by Vox Pupuli, many
of which are open source users. They barely even maintain their own modules in
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppetlabs&lt;/code&gt; namespace. Most of the contributions there have for years come
from community members using the &lt;a href=&quot;https://web.archive.org/web/20250321181447/https://www.puppet.com/ecosy
stem/contribute/trusted-contributors&quot;&gt;Trusted Contributors
program&lt;/a&gt; that I built when I was still there.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;On a related note, it also appears that they’ve shut down the Trusted
Contributors program as it’s no longer mentioned on their website.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;In reality, they don’t even have a dedicated modules team anymore!&lt;/strong&gt; One
with any sense at all would think that given this lack that they’d want to
encourage people to build and maintain quality content in the ecosystem rather
than putting barriers in place to make that harder to do. 🤔&lt;/p&gt;

&lt;p&gt;My only assumption can be that the Puppet Core sales aren’t so hot and they need
something, anything, to juice the numbers a bit.&lt;/p&gt;

&lt;h2 id=&quot;but-does-it-actually-matter&quot;&gt;But does it actually matter?&lt;/h2&gt;

&lt;p&gt;In all reality, many of us never really liked using the PDK. It’s really big
and inflexible. The customization options are confusing and tedious. It
generates way too many random config files for services that most people never
used, unless you customize it, which again is super awkward. And maybe worst of
all, it was designed primarily for the use of Puppet’s modules team (it still
existed back then) rather than being designed for community developers. For
example, until a few years ago, it configured PR testing and publishing
workflows with GitHub actions that assumed that you were using Honeycomb for
profiling and hardcoded the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppetlabs&lt;/code&gt; namespace.&lt;/p&gt;

&lt;h2 id=&quot;vox-pupuli-developer-tools&quot;&gt;Vox Pupuli developer tools&lt;/h2&gt;

&lt;p&gt;Luckily, Vox Pupuli has been working on replacements for a while. We believe
that developer tools should be small and purpose-built rather than trying to jam
pack all the things for all the people into a single massively overloaded tool.&lt;/p&gt;

&lt;p&gt;Rather than using the exact same command used to&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;create a module from a template&lt;/li&gt;
  &lt;li&gt;run unit testing in CI pipeline&lt;/li&gt;
  &lt;li&gt;manage installed Puppet versions&lt;/li&gt;
  &lt;li&gt;build and publish modules&lt;/li&gt;
  &lt;li&gt;drop into the debugger REPL that you probably didn’t even know existed,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vox Pupuli’s toolkit includes smaller tools that do one or two things
well instead of being mediocre at trying to do everything.&lt;/p&gt;

&lt;p&gt;For example, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modulesync&lt;/code&gt; tool makes it easy to create, manage, and update
a group of modules based on a template. No more broken dependencies because you
haven’t remembered to update one of your modules for the last four years. All
your modules, whether that’s one or one hundred, will be in a consistent state
and tested against your current infrastructure.&lt;/p&gt;

&lt;p&gt;Or you can get zero-install access to a &lt;a href=&quot;https://github.com/voxpupuli/container-voxbox?tab=readme-ov-file#availab
le-rake-tasks&quot;&gt;whole suite of module testing and
management tasks with
voxbox&lt;/a&gt;. Update your module’s metadata, validate all of its dependencies,
and publish a new version all with a few &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;podman&lt;/code&gt; commands.&lt;/p&gt;

&lt;h2 id=&quot;the-vox-pupuli-devkit-sdk&quot;&gt;The Vox Pupuli Devkit SDK&lt;/h2&gt;

&lt;p&gt;Now that easy access to the PDK is disappearing, we’ve put together a project
to collect all these tools and more into a coherent suite that’s usable,
discoverable, and maintainable. We’ll build the missing parts along the way. For
example, I’m currently working on an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;asdf&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mise&lt;/code&gt; plugin to easily manage
OpenVox versions using an industry standard tool rather than something bespoke
that’s constrained to very specific and limited workflows.&lt;/p&gt;

&lt;p&gt;We don’t have a timeline on this yet. But luckily, there’s not much development
on the PDK except to make it harder for you to use. You can very likely simply
continue to use the version you already have installed until we have more news
for you.&lt;/p&gt;

&lt;p&gt;Watch this space and we’ll soon have information for you about the new Devkit SDK!&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: OpenVox InfraTales - MacOS Signing and Notarization</title>
    <link href="https://overlookinfratech.com/2025/05/12/macos-signing/"/>
    <id>https://overlookinfratech.com/2025/05/12/macos-signing</id>
    <updated>2025-05-12T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Hi everyone! Nick here. I figured it was time I finally write a blog post. If you’ve seen me around on the &lt;a href=&quot;https://voxpupuli.slack.com&quot;&gt;Vox Pupuli Slack&lt;/a&gt;, you know that I’ve been deep in the trenches of OpenVox build code for some time now, getting the various bits to a secure and stable place where it’s easily accessible to community contributors.&lt;/p&gt;

&lt;p&gt;Today’s story is about wrestling with MacOS’s Gatekeeper so we can release a production-ready &lt;a href=&quot;https://downloads.voxpupuli.org/mac/openvox8/15/arm64/&quot;&gt;MacOS agent build&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;no-unsigned-code-ever&quot;&gt;No unsigned code, ever&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/posts/no_unsigned_code.png&quot; alt=&quot;No unsigned code, ever&quot; class=&quot;img left&quot; height=&quot;300px&quot; /&gt;
Perforce recently dropped a LinkedIn post about their latest Puppet Core release, bragging about how secure and enterprise-ready Puppet Core is. The one that stuck out to me: &lt;em&gt;Fully signed binaries and agents - no unsigned code, ever&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They’re right - unsigned binaries are like showing up to a formal dinner party in a &lt;a href=&quot;https://www.frenchiely.com/products/dog-crab-halloween-costume&quot;&gt;crab costume&lt;/a&gt;. In OpenVox-land, all of our Linux binaries are fully signed. However, just like how Perforce’s internal dev builds are not always signed, our &lt;em&gt;prerelease, non-final&lt;/em&gt; MacOS and Windows agents were unsigned (Windows remains so until we navigate the extortion racket that is paying for an Extended Validation code signing certificate from a vendor).&lt;/p&gt;

&lt;p&gt;I applaud Perforce on their journey towards creating the safest, most secure Puppet environment possible, so I thought I’d write this little guide to help them take the next step. In order to reach the utopia of a fully signed and notarized MacOS agent that Gatekeeper is happy with, we have to not only sign the right things, but notarize it as well.&lt;/p&gt;

&lt;div style=&quot;clear: both;&quot; /&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/macos_unsigned.png&quot; alt=&quot;Gatekeeper warning on an unsigned installer&quot; class=&quot;img right w30&quot; height=&quot;200px&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;gatekeeper-the-ever-vigilant-security-guard-of-macos&quot;&gt;Gatekeeper: The ever vigilant security guard of MacOS&lt;/h2&gt;

&lt;p&gt;Apple’s &lt;a href=&quot;https://support.apple.com/guide/security/gatekeeper-and-runtime-protection-sec5599b66df/web&quot;&gt;Gatekeeper&lt;/a&gt; ensures that the software you download and run is trusted and safe. If your software isn’t both signed and notarized in the correct way, it will flat out refuse to open it (without extra shenanigans), warning that you potentially downloaded a radioactive piece of malware and you should be ashamed of yourself.&lt;/p&gt;

&lt;p&gt;To make Gatekeeper happy, all you used to have to do in the halcyon days of pre-Sonoma was to sign the specific binary entrypoints into your app with an “application developer identity” (certificate + private key). Let’s dive into the GitHub mines and see how this is currently done.&lt;/p&gt;

&lt;h2 id=&quot;warning-there-be-dragons-in-these-mines&quot;&gt;Warning: There be dragons in these mines&lt;/h2&gt;

&lt;p&gt;The way that Perforce does this signing process for their current MacOS agents (or at least they did before taking everything private, but I don’t believe it has changed) is by sending the three executable files in the puppet-agent package to a signing server, which signs those files, and then sends them back to continue the build process. You can see how this is defined in the &lt;a href=&quot;https://github.com/puppetlabs/puppet-agent/blob/2ed175ddae769662ccb7872d7041aca2c5d30211/configs/projects/puppet-agent.rb#L24-L31&quot;&gt;puppet-agent repo&lt;/a&gt;. This utilizes Vanagon’s &lt;a href=&quot;https://github.com/puppetlabs/vanagon/blob/main/lib/vanagon/utilities/extra_files_signer.rb&quot;&gt;ExtraFilesSigner&lt;/a&gt; to &lt;a href=&quot;https://github.com/puppetlabs/vanagon/blob/701cccd065d8ebac128fe262873c94020be983db/lib/vanagon/utilities/extra_files_signer.rb#L28-L33&quot;&gt;lay down a bash file to run the command, rsync the file over, run the command, then rsync the file back&lt;/a&gt;. This process is embedded in Vanagon’s &lt;a href=&quot;https://github.com/puppetlabs/vanagon/blob/701cccd065d8ebac128fe262873c94020be983db/lib/vanagon/platform/osx.rb#L44-L87&quot;&gt;MacOS platform build process&lt;/a&gt;. This code ends up with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; that contains a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; installer, which contains all the files, including those three signed binaries. Finally, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; file must be signed with a different “installer developer identity”. Perforce &lt;a href=&quot;https://github.com/puppetlabs/packaging/blob/15da180e9574e4582c096b0537334627d3f0f814/lib/packaging/sign/dmg.rb#L43-L70&quot;&gt;does this&lt;/a&gt; by mounting the created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt;, signing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; with that identity, then recreating the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; file with that signed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;signing-and-notarization-everyones-favorite-migraines&quot;&gt;Signing and Notarization: Everyone’s favorite migraines&lt;/h2&gt;

&lt;p&gt;While this worked fine before, as of MacOS 15 Sonoma, the requirements are much more stringent. Not only must you sign the these binaries, but you must sign &lt;em&gt;every single binary, dylib, and bundle file&lt;/em&gt;, no exceptions. And on top of that, you have to sign all of them specifying it should use the &lt;a href=&quot;https://developer.apple.com/documentation/security/hardened-runtime&quot;&gt;hardened runtime&lt;/a&gt; via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--options runtime&lt;/code&gt; flag to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codesign&lt;/code&gt; tool, as well a secure timestamp via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--timestamp&lt;/code&gt; flag. Doesn’t matter if the files are already signed in a different manner. MacOS 15 has a refined palate and will only consume the &lt;a href=&quot;https://www.youtube.com/watch?v=G__PVLB8Nm4&quot;&gt;finest artisinal binaries&lt;/a&gt;. Yes, it’s probably for the best, but I digress.&lt;/p&gt;

&lt;p&gt;Additionally, you must notarize either the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; file. This involves sending your file to Apple, where their system scans for any hidden nasties and verifies everything that should be signed, is signed in the correct way. Once approved, you then must staple the notarization to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; so Gatekeeper can stop judging us.&lt;/p&gt;

&lt;h2 id=&quot;bringing-a-first-pass-of-order-to-chaos&quot;&gt;Bringing a first pass of order to chaos&lt;/h2&gt;

&lt;p&gt;Right now in OpenVox, unlike Linux agents where we use containers to build them, we’re building the MacOS agent in a &lt;a href=&quot;https://github.com/OpenVoxProject/planning/wiki/How-OpenVox-builds-work#macos-builds&quot;&gt;VM that is configured in a particular way&lt;/a&gt; with the appropriate bits and bobs installed. This is similar to how Perforce does it, except that this particular VM template also includes the bits needed for signing and notarization. This is not highly portable and accessible to people yet, but we wanted to get this agent into your hands as soon as possible. Soon, we’ll be moving the process into GitHub Actions and hopefully finding a way to build it locally without a Mac as well. We’ll take this awkward Ford Pinto and turn it into a Ferrari. Or at least a Honda Accord.&lt;/p&gt;

&lt;p&gt;For this first pass at MacOS agent signing and notarizing, I moved the process &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03&quot;&gt;entirely into Vanagon&lt;/a&gt;. While this may not be where we want to leave it since it reduces potential future flexibility, it makes sense for now since the only MacOS thing we sign is the agent, and this keeps the signing code from being spread across three different repos. This also streamlines the process, since we can do the signing and notarizing as part of the build, rather than having to unpack the build, sign the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt;, then repackage it into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This new code &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R68-R75&quot;&gt;hard-codes where to look for binaries&lt;/a&gt; that need signing within the agent package. This is not at all ideal, and is ripe for future improvements like moving this definition back into the vanagon project itself. But because it isn’t obvious which files need signing (i.e. binaries don’t all live in the same place, don’t all have a particular suffix), this is an easy way for us to utilize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find -exec&lt;/code&gt; to sign all the pieces. Another improvement made here is that at every step, we verify the signing/notarizing worked correctly. This gives us an extra layer of confidence that the delivered agent will not shout obscenities at you.&lt;/p&gt;

&lt;p&gt;As before, we &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R87&quot;&gt;sign the pkg file&lt;/a&gt;, but we do it before building the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt;. Then, we build the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; and &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R87&quot;&gt;sign that&lt;/a&gt;, after which we &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R101&quot;&gt;notarize&lt;/a&gt; using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notarytool&lt;/code&gt; in Xcode. Usually, the notary process takes less than 2 minutes. On our initial submissions, it took days as they were being manually reviewed. Now that the system recognizes our submission, it is much faster. Once notarization is approved, we &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R102&quot;&gt;staple&lt;/a&gt; that notarization to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; and then &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R103&quot;&gt;check with Gatekeeper&lt;/a&gt; that it isn’t going to complain.&lt;/p&gt;

&lt;h2 id=&quot;the-bureaucracy-d-u-n-s-numbers-and-overlook-infratech-inc&quot;&gt;The Bureaucracy: D-U-N-S numbers and Overlook InfraTech, Inc.&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/posts/background_tasks.png&quot; alt=&quot;Gatekeeper warning on an unsigned installer&quot; class=&quot;img right w30&quot; /&gt;
Figuring out this whole process was quite a bear. Apple’s documentation mostly assumes you are developing with the Xcode GUI app, and not trying to do everything from the command line. It isn’t terribly clear exactly what needs to be signed and notarized and how without some more digging and trial and error.&lt;/p&gt;

&lt;p&gt;Another thing that isn’t terribly ideal is that in order to have a developer account tied to an organization, that organization has to be a full fledged legal entity with a &lt;a href=&quot;https://www.dnb.com/en-us/smb/duns/duns-lookup.html&quot;&gt;D-U-N-S number&lt;/a&gt;. If you ever start a business, you’ll find that you need a franky ridiculous amount of different identification numbers that tell people your business is, in fact, a real business, and this number is just one of many. Vox Pupuli doesn’t have this, so the agent is signed with the Overlook InfraTech, Inc. account. This has the side effect that our company name shows as the one that owns the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pxp-agent&lt;/code&gt; background tasks. This should absolutely say Vox Pupuli and hopefully one of these days we can change that.&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts-lets-work-together&quot;&gt;Final thoughts: Let’s work together&lt;/h2&gt;

&lt;p&gt;To my knowledge, Perforce does not have their MacOS 15 agent notarized, nor signed with MacOS 15’s newer stringent requirements (repeating my disclaimer: I do not know for sure, as none of us has visibility into their current build process or access to their current agents without signing a EULA which prevents you from inspecting any of the code anyway). I’d love to submit some PRs to their repos, but unfortunately, they have seemingly completely abandoned them in favor of their internal forks. That’s a bummer, because &lt;em&gt;security is a team sport&lt;/em&gt;, and we all benefit when the ecosystem is safe. Nevertheless, I invite them to take a look at our work here to assist in getting their MacOS agents notarized, and I’d be more than happy to work with their engineers to assist. Thrilled, in fact. And I promise I’m not being sarcastic about that.&lt;/p&gt;

&lt;p&gt;If you’re curious for more details about how we did this or how we do any of the OpenVox things, drop by the Vox Pupuli Slack or IRC, both of which you can find in the &lt;em&gt;Connect&lt;/em&gt; menu of &lt;a href=&quot;https://voxpupuli.org&quot;&gt;voxpupuli.org&lt;/a&gt;. I’m always happy to chat, even while I’m debugging why a binary isn’t signing properly at 2AM. If you have any other topics you’d be interested in hearing more about in this blog, drop me a line any time.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: The OSU Open Source Lab needs your help today</title>
    <link href="https://overlookinfratech.com/2025/05/01/open-source-lab-needs-your-help-today/"/>
    <id>https://overlookinfratech.com/2025/05/01/open-source-lab-needs-your-help-today</id>
    <updated>2025-05-01T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Have you been looking at the OpenVox project and wondered what you could do to
help out? What if I told you about an opportunity to help us, and also hundreds
of other OSS projects that you probably rely on? The &lt;a href=&quot;https://osuosl.org&quot;&gt;OSU Open Source Lab&lt;/a&gt;
is OpenVox’s primary package host and helped us get the project off the
ground right from the start. They help hundreds of open source projects with
critical infrastructure needs that many projects (like us) would not have been
able to afford on their own.&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;🔔 tl;dr; &lt;a href=&quot;https://osuosl.org/blog/osl-future/&quot;&gt;the Open Source Lab needs your help&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not only that, but they have been training the next generation of FOSS DevOps
and system infrastructure engineers for YEARS. Many of the first Puppet community
practitioners came up through the OSL. They contributed many of the first Puppet
modules. The venerable &lt;a href=&quot;https://www.oreilly.com/library/view/pro-puppet-second/9781430260400/&quot;&gt;Pro Puppet&lt;/a&gt;
second edition was written by OSL students in collaboration with PuppetLabs
developers, including myself.&lt;/p&gt;

&lt;p&gt;OSL has played a HUGE part in Puppet’s story and helped Puppet, Vox Pupuli, and
OpenVox get to where they are today. I’d wager that your company uses OSL resources
on a regular basis and may not even realize it. Take a &lt;a href=&quot;https://osuosl.org/communities/&quot;&gt;look at the list&lt;/a&gt;
and you’re certain to find something business critical.&lt;/p&gt;

&lt;p&gt;However, with the current crisis in academic funding in the USA, they are in
critical need of funding and without your help, they may cease to exist. See
&lt;a href=&quot;https://osuosl.org/blog/osl-future/&quot;&gt;this blog post for more information&lt;/a&gt; and
go talk to your boss today! Don’t hesitate to
&lt;a href=&quot;https://osuosl.org/donate/&quot;&gt;drop them some cash yourself if you can spare it&lt;/a&gt;.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Camptocamp Blog: Talos Linux: a new standard for on-premises Kubernetes clusters?</title>
    <link href="https://dev.to/camptocamp-ops/talos-linux-a-new-standard-for-on-premises-kubernetes-clusters-283i"/>
    <id>https://dev.to/camptocamp-ops/talos-linux-a-new-standard-for-on-premises-kubernetes-clusters-283i</id>
    <updated>2025-04-22T17:44:21+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;A few weeks ago, I was at KubeCon Europe 2025 in London and I had the opportunity to attend a presentation that tackled the monumental challenge of migrating 35 Kubernetes clusters in an air-gapped environment from nodes deployed with a mix of kubeadm/Ansible/Puppet to &lt;a href=&quot;https://www.talos.dev&quot; rel=&quot;noopener noreferrer&quot;&gt;Talos Linux&lt;/a&gt; nodes deployed using &lt;a href=&quot;https://cluster-api.sigs.k8s.io&quot; rel=&quot;noopener noreferrer&quot;&gt;Cluster API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While the presentation was quite interesting (you can get the slides &lt;a href=&quot;https://kccnceu2025.sched.com/event/1tx78/day-2000-migration-from-kubeadmansible-to-clusterapitalos-a-swiss-banks-journey-clement-nussbaumer-postfinance&quot; rel=&quot;noopener noreferrer&quot;&gt;here&lt;/a&gt; and watch the session recording on &lt;a href=&quot;https://www.youtube.com/watch?v=uQ_WN1kuDo0&amp;amp;list=PLj6h78yzYM2MP0QhYFK8HOb8UqgbIkLMc&amp;amp;index=253&quot; rel=&quot;noopener noreferrer&quot;&gt;CNCF&#39;s YouTube Channel&lt;/a&gt;), I want to dive more into the Talos Linux project and its features.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Talos Linux?
&lt;/h2&gt;

&lt;p&gt;Talos Linux is a modern Linux distribution purpose-built for running Kubernetes clusters. Some noteworthy characteristics of Talos Linux are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immutable&lt;/strong&gt;: Talos Linux is designed to be immutable and always runs from a SquashFS image. This means that the operating system is read-only and cannot be modified at runtime. This immutability provides a strong security posture and means that there is no need to worry about unintended changes to the operating system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal&lt;/strong&gt;: Talos Linux is a minimal operating system that only includes the components necessary to run Kubernetes. All the OS is built from the ground-up and no unnecessary components are included. This minimalism reduces the attack surface and improves performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ephemeral and Declarative&lt;/strong&gt;: Talos Linux nodes are ephemeral and everything written to disk is reconstructible. It is also declarative, meaning that the desired state of the system is defined in a configuration file and gRPC API, which is perfect for someone that loves automation and reproducibility, like myself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt;: As a consequence of its design, Talos Linux provides enhanced security features, ensuring that the system remains robust against various threats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agnostic&lt;/strong&gt;: Talos Linux is cloud-agnostic, allowing it to run on various cloud providers and on-premises environments without vendor lock-in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, and I&#39;m quoting the official documentation, &lt;em&gt;&quot;Talos is meant to do one thing: maintain a Kubernetes cluster, and it does this very, very well.&quot;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My thoughts
&lt;/h2&gt;

&lt;p&gt;I have been following the Talos Linux project for a while, and I was gladly surprised to see a Swiss-bank like PostFinance being on the forefront of adopting such modern solutions like Talos Linux and Cluster API.&lt;/p&gt;

&lt;p&gt;I think Talos Linux will be a key player in the Kubernetes ecosystem, especially for organizations looking for an on-premises solution that&#39;s secure, efficient and easy to manage.&lt;/p&gt;

&lt;p&gt;The fact that Talos is declarative and immutable might seem like a drawback at first for someone used to the &lt;em&gt;old ways&lt;/em&gt; of managing infrastructure with Ansible or Puppet, but I believe that this is the future of managing Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;I want my nodes to behave like pods that I can easily create, destroy, and replace. Besides, I don&#39;t want to deal with the overhead of managing the operating system. I already have enough to deal with the on-premises infrastructure for the network and storage and the Kubernetes cluster itself, so why not offload the management of the operating system to a purpose-built distribution like Talos?&lt;/p&gt;

&lt;p&gt;With &lt;a href=&quot;https://www.siderolabs.com/platform/saas-for-kubernetes/&quot; rel=&quot;noopener noreferrer&quot;&gt;Omni&lt;/a&gt;, Sidero Lab&#39;s SaaS platform for managing Talos Linux clusters, I believe Sidero Labs have a good revenue model to continue developing Talos Linux. As a fan of open-source, we are all aware of the challenges of maintaining a project like Talos Linux, and I believe that having a SaaS platform to manage Talos Linux clusters is a good way to ensure the project&#39;s sustainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Talos Linux vs. other solutions
&lt;/h2&gt;

&lt;p&gt;Red Hat OpenShift is a well-known solution in large enterprises. However, more than a Kubernetes distribution, it is a complete platform that includes a lot of features and components, including CI/CD tools, monitoring, etc. It is also expensive.&lt;/p&gt;

&lt;p&gt;On the other hand, Talos Linux shines with its simplicity and minimalism, which brings more flexibility and allows teams to choose their solution to complete the platform as they see fit.&lt;/p&gt;

&lt;p&gt;RKE2 is another Kubernetes distribution that focuses on simplicity and security, making it a strong contender for organizations looking for a lightweight solution. However, it still requires an underlying operating system that you need to operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;While at KubeCon, I had the opportunity to visit the Sidero Labs&#39; booth and talk to the team behind Talos Linux. I thank the team for a warm welcome and great conversations about the project.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb8gy1k9411do5c5a3grc.jpg&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb8gy1k9411do5c5a3grc.jpg&quot; alt=&quot;Sidero Booth&quot; width=&quot;800&quot; height=&quot;1066&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Go further
&lt;/h2&gt;

&lt;p&gt;I wanted to keep this blog post short and not too technical, but if you want to learn more about Talos Linux, I recommend checking out the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.talos.dev/v1.9/introduction/what-is-talos/&quot; rel=&quot;noopener noreferrer&quot;&gt;What is Talos?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/siderolabs/talos&quot; rel=&quot;noopener noreferrer&quot;&gt;Talos Linux GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.talos.dev/v1.9/introduction/quickstart/&quot; rel=&quot;noopener noreferrer&quot;&gt;Quickstart a Talos Linux cluster with Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.talos.dev/v1.9/learn-more/philosophy/&quot; rel=&quot;noopener noreferrer&quot;&gt;Philosophy of Talos Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/kubernetes/comments/16v0j8x/talos_linux_a_modern_linux_distribution_purpose/&quot; rel=&quot;noopener noreferrer&quot;&gt;Interesting Reddit thread with some comments from Sidero Labs&#39; employees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
  </content>
    <author>
      <name>Camptocamp Blog</name>
      <uri>https://dev.to/camptocamp-ops</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: A whirlwind conversation on trademarks</title>
    <link href="https://overlookinfratech.com/2025/03/20/a-whirlwind-conversation-on-trademarks/"/>
    <id>https://overlookinfratech.com/2025/03/20/a-whirlwind-conversation-on-trademarks</id>
    <updated>2025-03-20T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Quite a few people over the last few months have helpfully pointed out the name
collisions in the &lt;a href=&quot;https://voxpupuli.org/openvox&quot;&gt;OpenVox&lt;/a&gt; Puppet fork. If you didn’t
recognize it, there’s also a VoIP/PBX provider called &lt;a href=&quot;https://www.openvoxtech.com&quot;&gt;OpenVox&lt;/a&gt;.
Surely that’s got to be a violation of some kind of law, right? Even Google gets
it very wrong! See how our information is interleaved in this autogenerated profile?
&lt;img src=&quot;/assets/posts/openvox_profile.png&quot; alt=&quot;The profile Google shows on the right side of the page when you search for an entity it thinks it knows how to describe.&quot; class=&quot;img right w30&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Hold up though. Not so fast because it’s not quite that simple. Lemme tell you a
quick story about a name that I’m pretty familiar with: Bolt.&lt;/p&gt;

&lt;h2 id=&quot;the-bolt-story&quot;&gt;The Bolt story&lt;/h2&gt;

&lt;p&gt;In August of 2017, Puppet began work on a new project intended to be an agent-less
orchestration tool for automating the manual work it takes to maintain your
infrastructure. Sure, it started life as an Ansible competitor, but it grew to
be pretty neat. For the first time, you could trivially and seamlessly interleave
shell scripts and Puppet code and just push that configuration out to nodes in
your infrastructure as needed.&lt;/p&gt;

&lt;p&gt;Imagine my surprise when I visited the Slack booth at KubeCon the following year
and saw their new application library called… none other than Bolt. It had
gone through several name iterations, including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slapp&lt;/code&gt; which is my personal
favorite, but Bolt is what they settled on.  I asked if they knew about our tool
and they nodded and brushed off my concerns. Obviously, I raised it to our Legal
team and was surprised when they also brushed it off.&lt;/p&gt;

&lt;p&gt;So then I started noticing all the things named Bolt.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;In the Pacific Northwest where I lived, we had the &lt;a href=&quot;https://en.wikipedia.org/wiki/BoltBus&quot;&gt;Bolt Bus&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;There were the &lt;a href=&quot;https://electrek.co/2022/08/16/bolt-mobility-abandoned-electric-bikes-element-lev/&quot;&gt;Bolt e-bike rentals&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;There’s the &lt;a href=&quot;https://bolt.eu/en/&quot;&gt;Bolt rideshare and rental company&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;There’s the silly &lt;a href=&quot;https://www.imdb.com/title/tt0397892/&quot;&gt;Bolt movie&lt;/a&gt; about “superhero” dog.&lt;/li&gt;
  &lt;li&gt;There’s an &lt;a href=&quot;https://www.bolt.com&quot;&gt;ecommerce platform&lt;/a&gt; named Bolt&lt;/li&gt;
  &lt;li&gt;There’s a &lt;a href=&quot;https://bolt.new&quot;&gt;mobile app tool&lt;/a&gt; named Bolt&lt;/li&gt;
  &lt;li&gt;There’s a &lt;a href=&quot;https://boltcms.io&quot;&gt;CMS&lt;/a&gt; named Boltcms&lt;/li&gt;
  &lt;li&gt;There’s an &lt;a href=&quot;https://bolt.io&quot;&gt;investment platform&lt;/a&gt; named Bolt.&lt;/li&gt;
  &lt;li&gt;There’s a &lt;a href=&quot;https://www.boltlock.com&quot;&gt;gear lock brand&lt;/a&gt; named BOLT&lt;/li&gt;
  &lt;li&gt;There’s a &lt;a href=&quot;https://boltthreads.com&quot;&gt;biotech company&lt;/a&gt; named Bolt&lt;/li&gt;
  &lt;li&gt;There’s an &lt;a href=&quot;https://bolttech.io&quot;&gt;insurance company&lt;/a&gt; named Bolt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aaaaand I’m tired of typing out that list, so I’m just gonna stop at that.&lt;/p&gt;

&lt;h2 id=&quot;what-purpose-does-trademark-serve&quot;&gt;What purpose does trademark serve?&lt;/h2&gt;

&lt;p&gt;Clearly there’s more to trademark enforcement than just the name itself. As it
turns out, that’s only a small part of it. At this point, I’ll take a real quick
aside to remind you that I’m an open source software nerd, not a lawyer. And I’m
&lt;em&gt;certainly not your lawyer&lt;/em&gt; so none of this is legal advice. In any case, the
concept of trademark law isn’t really to give you an asset to own, it’s to
protect consumers in the market from brand confusion. You’ll notice that none of
those companies I listed are named Bolt and also do infrastructure
orchestration.&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;A trademark is a word, phrase, or logo that identifies the source of goods or
services. Trademark law protects a business’ commercial identity or brand by
discouraging other businesses from adopting a name or logo that is “confusingly
similar” to an existing trademark. &lt;strong&gt;The goal is to allow consumers to easily
identify the producers of goods and services and avoid confusion&lt;/strong&gt;.&lt;br /&gt;
– &lt;a href=&quot;https://en.wikipedia.org/wiki/United_States_trademark_law&quot;&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s a commonly used rubric called the &lt;a href=&quot;https://www.mekiplaw.com/the-dupont-factors-an-easy-guide/#:~:text=II.%20THE%20DUPONT%20FACTORS&quot;&gt;DuPont Factors&lt;/a&gt;
which can help identify whether there is a probable infringement. The rubric has
13 criteria, but they boil down roughly to&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Similarity of the marks.&lt;/li&gt;
  &lt;li&gt;Similarity of goods/services.&lt;/li&gt;
  &lt;li&gt;Similarity of trade channels.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So that explains why we aren’t concerned with the fact that there’s a product
out there named OpenVox but is in a totally unrelated industry.  In other words,
someone looking for an infrastructure management system isn’t likely to
accidentally purchase a PBX system, even if the name is the same.&lt;/p&gt;

&lt;h2 id=&quot;what-about-the-filenames&quot;&gt;What about the filenames?&lt;/h2&gt;

&lt;p&gt;But there’s another point that comes up frequently that’s not quite as clear.
That’s the name and paths of the executables and the configuration files and
such within the OpenVox packages. At this point, they’re exactly the same as the
original Puppet packages. In other words, to configure OpenVox, you edit the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet.conf&lt;/code&gt; file and to run the agent you invoke &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet agent -t&lt;/code&gt;. We negotiated
this point very heavily with Perforce in order to not cause community disruption.&lt;/p&gt;

&lt;p&gt;There are a few non-legally-defensible reasons why Perforce might agree to this
usage and a couple reasons that are legally defensible. First off, let’s look at
why Perforce might be motivated to allow this usage. These are not reasons that
would stand up in court, if it came to that, but are ways in which Perforce
benefits from it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;They get a little bit of free marketing. Every time someone types
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet agent -t&lt;/code&gt; they’re reminded of where OpenVox came from.&lt;/li&gt;
  &lt;li&gt;If someone using the system &lt;em&gt;were&lt;/em&gt; to be confused by the name, it would likely
be to Perforce’s benefit. In other words, if someone trialled OpenVox and typed
‘puppet’ a whole bunch, there is a small chance that when they submit an order
to their purchasing department that they might accidentally request a Puppet
purchase. It would never go the other direction.&lt;/li&gt;
  &lt;li&gt;If we had to change all the commands and paths today, then the ecosystem would
be forced to write complex abstractions or explanations with every module, tool,
tutorial, README, guide, etc. It’s more likely that they’d take the easy way
out and choose one or the other, thereby fragmenting the ecosystem.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last point deserves more explanation. OpenVox and Puppet by themselves aren’t
terribly useful. Their value comes from the modules that enable the management of
Apache, MySQL, SELinux, etc. So the modules and the tools that make building modules
easier are of utmost importance. All these modules and tools and the documentation
around them are currently written with the expectation of the existing paths.&lt;/p&gt;

&lt;p&gt;If we changed these paths, then every single part of the ecosystem would have to
account for the path change. It’s a comparatively high lift to abstract the name
and paths out, whether it’s in code or docs, so it’s likely that most people will
just choose a side and then search and replace ‘openvox’ for ‘puppet’. This is why
Puppet modules and GitHub repos are all still in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppetlabs&lt;/code&gt; namespace, for example.&lt;/p&gt;

&lt;p&gt;There are two reasons why Perforce would be on the losing end of that schism.
First is that Perforce is now attempting to sell something infinitely more expensive
than its former price of free. Very few people creating and sharing &lt;em&gt;free open source
content&lt;/em&gt; would choose to support that ecosystem rather than the FOSS alternative.
The second reason is that 96% of Puppet’s enterprise customers use Vox Pupuli modules
and, for all practical purposes, 100% of them use Vox Pupuli’s dev tools since
they’re vendored into the PDK. Vox Pupuli is obviously choosing to support OpenVox
even if that has to be at the exclusion of Puppet itself.&lt;/p&gt;

&lt;p&gt;There are also legally defensible reasons. Even if Perforce weren’t convinced by
the reasons already stated, these would almost certainly lead to them losing a
court case if it came to it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;By the time that someone has researched, and purchased (or not), and downloaded,
and installed, and configured the system, it’s highly unlikely that they would be
confused about whether they were running Puppet or OpenVox. Remember that the goal of
trademark laws are to protect against confusion in the market. Several of the
DuPont factors look at trade channels and the difference between downloading
an OSS tool and talking to Sales is quite stark. If this were the only point
we had to go on, it probably would not be enough. But there are better defenses.&lt;/li&gt;
  &lt;li&gt;Trademark is intended to protect a brand, not functionality. If something is
used for functional purposes, then it cannot under the &lt;em&gt;Functionality Doctrine&lt;/em&gt;
be considered a trademark infringement. The canonical case here is
&lt;a href=&quot;https://en.wikipedia.org/wiki/Sega_v._Accolade&quot;&gt;&lt;em&gt;Sega v. Accolade&lt;/em&gt;&lt;/a&gt; of 1992.
Sega included a “Trademark Security System” which relied on the string “SEGA”
to exist in a known memory address on a game cartridge. If the code were
present, the game system would display a trademark notification and play the
game. If it were not present, then the game wouldn’t play. Accolade added the
code to their games and won the court case when Sega sued them for trademark
infringement because the trademark was used for functionality.&lt;/li&gt;
  &lt;li&gt;Command names and file paths are a functional interface to a piece of software.
In other words, if a tool or system wants to integrate with Puppet, then it will
often do so by configuring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet.conf&lt;/code&gt; or running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet&lt;/code&gt; commands. If we
are to build a Puppet-compatible product, then it has to use the same command
names and paths. Our industry is littered with examples of this going back for
many decades. Think about how many MTAs install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/sbin/sendmail&lt;/code&gt;, for example.
On my Mac, that’s provided by Postfix. The actual Sendmail company, now owned
by my former employer Proofpoint, has had three decades to dispute that use and
has not. Another example is MySQL, owned by what might be the most litigious
company in existence (Oracle.) When MariahDB forked away, it was ten years
before they bothered to replace the admin commands like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqladmin&lt;/code&gt; and even
today they ship symlinks so you can &lt;em&gt;still run that command&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-path-forward&quot;&gt;The path forward&lt;/h2&gt;

&lt;p&gt;We have no concerns about trademark infringement today. The OpenVox PBX system is
in an entirely different industry and won’t be confused with the OpenVox configuration
management system. Perforce and OpenVox are both incentivized for us to keep using
the existing command names and paths. And even if they weren’t, there is significant
legal precedence to defend our use.&lt;/p&gt;

&lt;p&gt;With that said, it would benefit our own brand to have our own command names and
paths. Over the next year or so, we have a roadmap to achieving this safely and
with as little community disruption as possible. We will first build an
integration acceptance test framework so that we can tell quickly when we screw
it up. Then we’ll gradually start replacing commands and paths with our own
versions while leaving symlinks in place so that integrations continue to work.
Then finally, we’ll extract those symlinks into an optional
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openvox-puppet-compat&lt;/code&gt; package that will likely never go away.&lt;/p&gt;

&lt;p&gt;In order to make Puppet extensions universal, we aren’t planning to ever change
the internal file hierarchy that makes up the documented API. And if that ever
does change, we’ll do it in cooperation with Perforce to retain compatibility.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Paying their fair share</title>
    <link href="https://overlookinfratech.com/2025/02/28/paying-their-fair-share/"/>
    <id>https://overlookinfratech.com/2025/02/28/paying-their-fair-share</id>
    <updated>2025-02-28T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;The other day someone shared a Puppet Community Slack post in which a very
senior Perforce sales engineer stated plainly that the reason for their
&lt;a href=&quot;https://overlookinfratech.com/2024/11/08/sequestered-source/&quot;&gt;sequestered source shenanigans&lt;/a&gt;
is that they’re “asking open source users to pay their fair share.” Now, I don’t
want to sound like I’m poking fun at this particular corporate flunky, he’s only
repeating what he’s clearly been told over and over again, but What. The. Fuck.&lt;/p&gt;

&lt;p&gt;Besides making it exceedingly clear that the only thing that Perforce
understands about open source communities is a bunch of people who happen to own
wallets or purses, this also demonstrates a devastating lack of understanding of
their own product. Let’s break it down, shall we?&lt;/p&gt;

&lt;p&gt;What you think of as a big beastly monolithic application called Puppet
Enterprise is mostly a lot of open source tools jammed together with some
proprietary business logic duct tape and baling wire. No surprise there, that’s
how most enterprise apps are built these days.&lt;/p&gt;

&lt;p&gt;The puppetserver application is business logic implemented in the &lt;a href=&quot;https://github.com/sponsors/richhickey&quot;&gt;open source
Clojure runtime&lt;/a&gt;. It drives puppet via
the &lt;a href=&quot;https://github.com/sponsors/headius&quot;&gt;open source jRuby runtime&lt;/a&gt;.
These are packaged up into a jar with a lot of open source components like the
&lt;a href=&quot;https://www.bouncycastle.org/engage/donate/&quot;&gt;open source Bouncy Castle&lt;/a&gt; which
is of course built on top of the &lt;a href=&quot;https://openssl-foundation.org/donate/&quot;&gt;open source OpenSSL
library&lt;/a&gt;. This jar is then run with the
&lt;a href=&quot;https://openjdk.org/guide/#reviewing-and-sponsoring-a-change&quot;&gt;open source openjdk Java
runtime&lt;/a&gt;. To build
it, they use the &lt;a href=&quot;https://www.jenkins.io/donate/&quot;&gt;open source Jenkins&lt;/a&gt; CI/CD
platform. Packages are generated with the &lt;a href=&quot;https://github.com/sponsors/jordansissel&quot;&gt;open source
fpm&lt;/a&gt; tool.&lt;/p&gt;

&lt;p&gt;And that’s only puppetserver. The puppet-agent package uses open source nssm
(now defunct) to run on Windows because they couldn’t be arsed to write their
own service management. PuppetDB is just a business logic wrapper over the &lt;a href=&quot;https://www.postgresql.org/about/donate/&quot;&gt;open
source PostgreSQL&lt;/a&gt; database server
which was chosen because it was the only one with the primitives they needed.
The Puppet Enterprise Console is built on the &lt;a href=&quot;https://emberjs.com/sponsors/&quot;&gt;open source
Ember&lt;/a&gt; framework. I could go on listing the projects
that Puppet depends on, but that would easily triple the length of this article.&lt;/p&gt;

&lt;p&gt;There is very literally not a single part of Puppet Enterprise that stands
alone. More of the PE installer is open source packages than it’s Puppet
Enterprise itself. And yet how much do they contribute back? Do you think they
“pay their fair share?”&lt;/p&gt;

&lt;p&gt;No. It’s zero. When jRuby put out a desperate call for funding, do you think
Perforce chipped in? Have they ever contributed to OpenSSL? Has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@whack&lt;/code&gt; ever
seen a dime for the thousands of packages Puppet built with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fpm&lt;/code&gt;? No. None.
Perforce has never contributed a penny. They’ve just taken and taken.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Update, hey neat! Looks like raising the issue finally got jRuby some support.
Let’s hope that they continue funding their dependencies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote class=&quot;mastodon-embed&quot; data-embed-url=&quot;https://mastodon.social/@headius/114082394611145408/embed&quot; style=&quot;background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;&quot;&gt; &lt;a href=&quot;https://mastodon.social/@headius/114082394611145408&quot; target=&quot;_blank&quot; style=&quot;align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Oxygen, Ubuntu, Cantarell, &#39;Fira Sans&#39;, &#39;Droid Sans&#39;, &#39;Helvetica Neue&#39;, Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 79 75&quot;&gt;&lt;path d=&quot;M74.7135 16.6043C73.6199 8.54587 66.5351 2.19527 58.1366 0.964691C56.7196 0.756754 51.351 0 38.9148 0H38.822C26.3824 0 23.7135 0.756754 22.2966 0.964691C14.1319 2.16118 6.67571 7.86752 4.86669 16.0214C3.99657 20.0369 3.90371 24.4888 4.06535 28.5726C4.29578 34.4289 4.34049 40.275 4.877 46.1075C5.24791 49.9817 5.89495 53.8251 6.81328 57.6088C8.53288 64.5968 15.4938 70.4122 22.3138 72.7848C29.6155 75.259 37.468 75.6697 44.9919 73.971C45.8196 73.7801 46.6381 73.5586 47.4475 73.3063C49.2737 72.7302 51.4164 72.086 52.9915 70.9542C53.0131 70.9384 53.0308 70.9178 53.0433 70.8942C53.0558 70.8706 53.0628 70.8445 53.0637 70.8179V65.1661C53.0634 65.1412 53.0574 65.1167 53.0462 65.0944C53.035 65.0721 53.0189 65.0525 52.9992 65.0371C52.9794 65.0218 52.9564 65.011 52.9318 65.0056C52.9073 65.0002 52.8819 65.0003 52.8574 65.0059C48.0369 66.1472 43.0971 66.7193 38.141 66.7103C29.6118 66.7103 27.3178 62.6981 26.6609 61.0278C26.1329 59.5842 25.7976 58.0784 25.6636 56.5486C25.6622 56.5229 25.667 56.4973 25.6775 56.4738C25.688 56.4502 25.7039 56.4295 25.724 56.4132C25.7441 56.397 25.7678 56.3856 25.7931 56.3801C25.8185 56.3746 25.8448 56.3751 25.8699 56.3816C30.6101 57.5151 35.4693 58.0873 40.3455 58.086C41.5183 58.086 42.6876 58.086 43.8604 58.0553C48.7647 57.919 53.9339 57.6701 58.7591 56.7361C58.8794 56.7123 58.9998 56.6918 59.103 56.6611C66.7139 55.2124 73.9569 50.665 74.6929 39.1501C74.7204 38.6967 74.7892 34.4016 74.7892 33.9312C74.7926 32.3325 75.3085 22.5901 74.7135 16.6043ZM62.9996 45.3371H54.9966V25.9069C54.9966 21.8163 53.277 19.7302 49.7793 19.7302C45.9343 19.7302 44.0083 22.1981 44.0083 27.0727V37.7082H36.0534V27.0727C36.0534 22.1981 34.124 19.7302 30.279 19.7302C26.8019 19.7302 25.0651 21.8163 25.0617 25.9069V45.3371H17.0656V25.3172C17.0656 21.2266 18.1191 17.9769 20.2262 15.568C22.3998 13.1648 25.2509 11.9308 28.7898 11.9308C32.8859 11.9308 35.9812 13.492 38.0447 16.6111L40.036 19.9245L42.0308 16.6111C44.0943 13.492 47.1896 11.9308 51.2788 11.9308C54.8143 11.9308 57.6654 13.1648 59.8459 15.568C61.9529 17.9746 63.0065 21.2243 63.0065 25.3172L62.9996 45.3371Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt; &lt;div style=&quot;color: #787588; margin-top: 16px;&quot;&gt;Post by @headius@mastodon.social&lt;/div&gt; &lt;div style=&quot;font-weight: 500;&quot;&gt;View on Mastodon&lt;/div&gt; &lt;/a&gt; &lt;/blockquote&gt;
&lt;script data-allowed-prefixes=&quot;https://mastodon.social/&quot; async=&quot;&quot; src=&quot;https://mastodon.social/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;To be clear though, that’s not the part of this that’s so offensive. Perforce’s
sponsorship record is pretty standard for the corporate world. Puppet itself,
prior to Perforce, didn’t contribute all that much more. &lt;strong&gt;What &lt;em&gt;is&lt;/em&gt; utterly
offensive is this idea that somehow the very community that built the foundation
that Puppet sits on isn’t “paying their fair share”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let me tell you about “their fair share.” Many of you know that I spent thirteen
years at Puppet and I ran Community for a large part of that. So I can give you
actual real numbers. A full 96% of Puppet’s paid customers uses Vox Pupuli’s
open source community modules. And the remaining 4% would have except their
policies required them to write all their own content.&lt;/p&gt;

&lt;p&gt;When it comes to paid Puppet customers, we should acknowledge how many of them
are customers because of the community. I’ve attended many “vendor appreciation
dinners” where the point of me being there as a community representative was to
make sure that Puppet, Inc continued to remember that investing in community was
required. The single biggest deal Puppet ever closed hinged on a community
interaction that I and some Vox Pupuli members enabled. We talked through a
hypothetical solution to the problem at hand and then I built it on my spare
time as a community member.&lt;/p&gt;

&lt;p&gt;Then if you want, we can get down to raw contributor data and &lt;a href=&quot;https://gist.github.com/binford2k/81fcf15ff62a9bf2c0ed3ad7cec3718e&quot;&gt;I could show you
how many community members contribute to Puppet codebases more even than most
Puppet employees&lt;/a&gt;.
A quick count shows that only 54 of the top 100 were Puppet employees.&lt;/p&gt;

&lt;p&gt;Puppet’s community is the foundation for Puppet Enterprise and the whole
company. When we did the massive upgrade from 3 to 4 and needed all the tooling
to find all the weird edge cases, the community is who pointed out what we
needed to validate and built tools like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;catalog-diff&lt;/code&gt; to give us a starting
point. When we pulled core resources out to modules in Puppet 6 and broke every
testing pipeline in the world, the community worked with Josh to fix it. And
now that Perforce is insisting on shooting its own feet, the community is once
again stepping up to ensure that an open source community actually continues to
exist and help each other out. That community still includes their paid clients
and somehow Perforce continues to lose sight of that fact.&lt;/p&gt;

&lt;p&gt;The fact that the messaging that their representatives share is that open source
community users need to step up and “pay their fair share” is a slap in the face
to the community that has provided the foundation for the entire company to
exist on. Without these people Puppet by Perforce would not exist.&lt;/p&gt;

&lt;p&gt;Puppet by Perforce doesn’t pay their fair share in any way shape or form, and
for them to self-righteously demand that from their users demonstrates how little
they understand the space and how parasitic they are. I wish that they weren’t so
representative of the industry.&lt;/p&gt;

&lt;p&gt;The reality is that open source projects help your company. They accelerate your
GTM (go to market) velocity a TON. And open-sourcing your own product leads to
rapid adoption by open source community users who give you so much free marketing
and product feedback and are the final line of QA before changes hit
your paying customers. All in addition to the code and docs and expertise
contributions. &lt;em&gt;But this doesn’t come without cost to you&lt;/em&gt;. If you use the
benefits of open source software then you have to pay the costs of open source
software. It’s not a lot. It just means that you have to understand that the
value you derive from open source communities is inherently a function of your
product being freely available. When the vultures swoop in and try to alter that
power balance, then you lose the benefits of the open source community.&lt;/p&gt;

&lt;p&gt;And before you demand a “fair share” from your users, make sure that you’re
doing the same. If your company uses open source software then contribute back.
Either sponsor the project or contribute code, resources, or time. And if your
community contributes code, or docs, or troubleshooting, or anything then
freaking appreciate them for it.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: CfgMgmtCamp wrapup</title>
    <link href="https://overlookinfratech.com/2025/02/17/cfgmgmtcamp-wrapup/"/>
    <id>https://overlookinfratech.com/2025/02/17/cfgmgmtcamp-wrapup</id>
    <updated>2025-02-17T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Well, we’re back from Belgium after another successful week of conferences.
First we attended FOSDEM in Brussels, then we hustled down to Ghent for the
CfgMgmtCamp speaker’s dinner. This year is special to me because it’s the first
year that I’ve not gone to represent someone else. It was also special because
the atmosphere was so positive. There were a ton of great talks, more
sponsorship dollars, attendance was up, more people showed up for the optional
third day add-on than ever before, and people responded really well to the talk
lineup. Chef even showed up to try and rebuild their community engagement!&lt;/p&gt;

&lt;p&gt;In other words, it felt a bit like a revival. But I’ll come back to that. (Or you
can &lt;a href=&quot;#insights&quot;&gt;skip directly to it&lt;/a&gt;, just like online recipes!)&lt;/p&gt;

&lt;h2 id=&quot;main-stage-panel-at-fosdem&quot;&gt;Main stage panel at FOSDEM&lt;/h2&gt;

&lt;p&gt;It started on Jan. 21, 10 days before FOSDEM. There were some last minute schedule changes that I won’t
&lt;a href=&quot;https://web.archive.org/web/20250120113301/https://fosdem.org/2025/schedule/event/fosdem-2025-4507-infusing-open-source-culture-into-company-dna-a-conversation-with-jack-dorsey-and-manik-surtani-block-s-head-of-open-source/&quot;&gt;speculate&lt;/a&gt;
about
&lt;a href=&quot;https://web.archive.org/web/20250123225543/https://fosdem.org/2025/schedule/event/fosdem-2025-4507-infusing-open-source-culture-into-company-dna-a-conversation-with-jack-dorsey-and-manik-surtani-block-s-head-of-open-source/&quot;&gt;too much&lt;/a&gt;
&lt;sup&gt;(they’re the same link, just two days apart)&lt;/sup&gt;
and I was invited to participate in a panel discussion about project forks, why
they happen, what they can do to the community and so on. Read more about it and
see the recording right on the
&lt;a href=&quot;https://fosdem.org/2025/schedule/event/fosdem-2025-5258-forked-communities-project-re-licensing-and-community-impact/&quot;&gt;FOSDEM schedule page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What a fun experience! &lt;a href=&quot;https://hachyderm.io/@geekygirldawn&quot;&gt;Dawn&lt;/a&gt; was the
original architect of the Puppet community and was my boss for all of a hot
minute when I moved onto the team. But somehow this is the first time I’ve
shared a stage with her. Everyone on stage was super knowledgeable and I learned
a ton. In particular, Stephen Walli
&lt;a href=&quot;https://medium.com/@stephenrwalli/forked-communities-whose-property-is-it-anyway-2ddee71f2ef1&quot;&gt;explains that&lt;/a&gt;
statistically two criteria predict the success of a community fork&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The community is put into existential crisis by actions of the original IP holder, and&lt;/li&gt;
  &lt;li&gt;One or more respected non-employee members of the community step up to serve as anchors.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This bodes very well for the future of OpenVox.&lt;/p&gt;

&lt;h2 id=&quot;a-busy-week-in-ghent&quot;&gt;A busy week in Ghent&lt;/h2&gt;

&lt;p&gt;The main stage lineup at CfgMgmtCamp was fascinating. First,
&lt;a href=&quot;https://hachyderm.io/@hazelweakly&quot;&gt;Hazel Weakly&lt;/a&gt;
talked about the &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/39GGMS/&quot;&gt;humanity behind software, configuration, and infrastructure
management&lt;/a&gt;. This is an
interesting perspective to me. As I see it, humanity is the unsaid reason
behind the devops movement in the first place but we’ve now shifted far away
from it. It’s no surprise that Puppet’s slogan used to be the human-forward
&lt;em&gt;“freeing you to do what the robots can’t”&lt;/em&gt; and now it’s bland corporate
scare-words &lt;em&gt;“avoid business disruption and minimize downtime.”&lt;/em&gt;&lt;/p&gt;

&lt;iframe width=&quot;280&quot; height=&quot;157&quot; style=&quot;float: right; clear: both; margin: 0.15rem 0.5rem;&quot; src=&quot;https://www.youtube.com/embed/rt66RLqSBKI?si=SG-EXzgELadMEX0O&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;The thematic arc of the morning took us through these ideals we held when
starting the devops movement, to the nitty gritty of OpenTofu’s &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/T7JBSA/&quot;&gt;escape from the
corporate thumbscrews&lt;/a&gt; and
recapturing their own personal power, to the &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/R7CKYH/&quot;&gt;real value of a content
ecosystem&lt;/a&gt; and musing on a
better way to build sustainable ecosystems.&lt;/p&gt;

&lt;iframe width=&quot;280&quot; height=&quot;157&quot; style=&quot;float: right; clear: both; margin: 0.15rem 0.5rem;&quot; src=&quot;https://www.youtube.com/embed/UKqAYhV9bqk?si=aEdqPvh95zTGHztD&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Of course, the rest of the conference was just as good. We had two full days of
two solid Puppet / OpenVox tracks covering all kinds of topics, from the
technical to the speculative to the damage control. On the main stage Adam
talked about the
&lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/WRZL3U/&quot;&gt;hypergraph of the future&lt;/a&gt;.
James kept you on your toes by
&lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/DQUHEL/&quot;&gt;throwing fire again&lt;/a&gt;.
There were a &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/RTV7UM/&quot;&gt;couple&lt;/a&gt;
of &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/NXJTDG/&quot;&gt;talks&lt;/a&gt; about compliance.
Stahnke showed us how he put his EPEL and Puppet knowledge to work
&lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/YUVUTN/&quot;&gt;with NixOS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But there was still a vibe running through. Florian talked about
&lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/FGLTVM/&quot;&gt;toxic corporate environments&lt;/a&gt;
and Nick explained how &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/UWREUC/&quot;&gt;“source available” is such a woefully inadequate
substitute for open source&lt;/a&gt;
and ways to better appreciate your contributors. A lot of talks hinted at
disillusionment with corporatism and the current state of the world. Lunch
conversations debated the merits of various open source licenses and whether or
not the GPL would prevent the licensing smash-and-grab ploys that so many
once-great companies are pulling today. I lost track of how many people thanked
me for my “yes, I’m a dirty hippie” slide.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/vox_dinner.jpeg&quot; alt=&quot;Vox Pupuli Dinner&quot; class=&quot;img left w30&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I even overheard someone at a table nearby talking about their challenges in
forming a tech union. I didn’t catch who or where it was, so if this was you,
I’d love to hear more about it.&lt;/p&gt;

&lt;p&gt;And that’s not even mentioning the overwhelming interest in
&lt;a href=&quot;https://voxpupuli.org/openvox/&quot;&gt;OpenVox&lt;/a&gt;. Our community dinner (traditionally
the &lt;em&gt;Puppet Community&lt;/em&gt; and now the &lt;em&gt;Vox Pupuli Community&lt;/em&gt;) filled the first
floor of the Royal India. This was a nice healthy increase from years past and
brought together several generations of Puppet / OpenVox community
collaborators.&lt;/p&gt;

&lt;p&gt;Some people were there for their first time and some had been there since the
beginning; back when CfgMgmtCamp was still called a Puppet Camp and none of us
had been acquired by private equity yet. But we were all united by two common
interests: 1) keeping a community alive despite the best efforts of Puppet’s
current owner to repeatedly shoot its own feet, and 2) excellent Indian cuisine
enjoyed with friends.&lt;/p&gt;

&lt;p class=&quot;callout right quote&quot;&gt;For context: the conference is two days long, but they book the venue for a
third day to make space for mini-events to tag along and colocate. Puppet has
traditionally used this for a Puppet Community Day; Ansible and Foreman have
similar events.&lt;/p&gt;

&lt;p&gt;The optional third day is where this enthusiasm really shone through though. Not
only were there more people who stayed for the third day than in years past, but
every single person left the Puppet Community Day to join the OpenVox working
session breakout. And as a measure of their commitment, at 5pm there were still
40-50 people in the room still hard at it, which is unheard of. Most people
trickle out to catch their trains during the day.&lt;/p&gt;

&lt;p&gt;We did not get quite as much done as we’d hoped, but everyone had the opportunity to
participate and we set the groundwork for a stable and healthy project. In short,
we adopted a &lt;a href=&quot;https://fedoraproject.org/wiki/Creating_a_Fedora_SIG&quot;&gt;Fedora SIG style governance&lt;/a&gt;
and decided to remain an independent project via
&lt;a href=&quot;https://opencollective.com/vox-pupuli&quot;&gt;OpenCollective&lt;/a&gt; for the time being. As we
mature a bit, we’ll continue investigating the Linux Foundation and other options.&lt;/p&gt;

&lt;p&gt;Notes for the entire third day, including the Puppet Community Day and the
OpenVox working session breakout can be seen &lt;a href=&quot;https://github.com/OpenVoxProject/planning/wiki/2025-Community-Day-at-CfgMgmtCamp&quot;&gt;in our wiki&lt;/a&gt;,
thanks to &lt;a href=&quot;https://fosstodon.org/@genebean&quot;&gt;Gene Liverman&lt;/a&gt; and &lt;a href=&quot;https://github.com/alexjfisher&quot;&gt;Alex Fisher&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;insights&quot;&gt;Insights&lt;/h2&gt;

&lt;p&gt;I think the positive vibes were mainly the result of three different factors.&lt;/p&gt;

&lt;h3 id=&quot;first-the-obvious&quot;&gt;First, the obvious&lt;/h3&gt;

&lt;p&gt;I don’t need to spend much time on this. With the media coverage and the furor
around the clumsy way Perforce is stumbling through its community changes and the
blatant money-grab, it’s no surprise to see excitement in our community. We shall
see how much of that excitement is lasting.&lt;/p&gt;

&lt;h3 id=&quot;whats-old-is-new-again&quot;&gt;What’s old is new again&lt;/h3&gt;

&lt;p&gt;For years, it seemed inevitable; cloud was eating our lunch. Every workload was
becoming cloud-native, whether or not it really fit there, and the idea of managing
configuration of long-lived instances was beginning to feel quaint. But there was
a lot of foreshadowing, especially for those of us who’d been around for a few
boom-bust cycles. A lot of the same problems being solved, a lot of the same challenges
being (re-)discovered, opaque builds, semi-reproducible artifacts. And so much of
it is tied together with not much more than the shell scripts I used to build production
&lt;a href=&quot;https://en.wikipedia.org/wiki/Chroot&quot;&gt;chroots&lt;/a&gt; back in the day.&lt;/p&gt;

&lt;p&gt;The expected savings from cloud-native workflows haven’t really materialized either.
If your workload is suited for, designed for, and optimized for the cloud then it
can often be cheaper. But costs that were trivial at testing or prototyping stages
have a tendency to unexpectedly balloon out of control at scale.&lt;/p&gt;

&lt;p&gt;It’s not that cloud is bad. We’re actually moving a lot of workflows (such as the
&lt;a href=&quot;https://github.com/OpenVoxProject/puppet-runtime/blob/main/.github/workflows/build.yml&quot;&gt;OpenVox build pipelines&lt;/a&gt;) to containers and it’s drastically simplifying what we’re doing. But
now that the craze is dying down, people are realizing that it’s not the be-all
end-all that it was promised to be. There are a ton of workflows well-suited to
cloud &amp;amp; containers, especially transient and stateless tasks. But there are also
a lot of workloads that benefit from being on-prem, or on long-lived stateful
instances, or just don’t need the layers and layers of complexity.&lt;/p&gt;

&lt;p&gt;And for these jobs, good old fashioned configuration management is right here
waiting with its predictable pricing. The pendulum is swinging back to a hybrid
infrastructure and we’ll all benefit from playing in the same sandbox. This year
we saw a resurgence in config management community investment. But we also had
a full Kubernetes track. And I find that very promising.&lt;/p&gt;

&lt;h3 id=&quot;the-industry-the-world-and-everything&quot;&gt;The industry, the world, and everything&lt;/h3&gt;

&lt;p&gt;There’s a lot going on right now. There’s somehow a blustery orange man-baby in
the American White House again. For some unexplained reason, that gives Elon
Musk the ability to go Twitter on the American government and slash essential
departments protecting everything from consumer rights to the environment. “AI”
is unsustainably disrupting industries and exploding the wealth gap. A lot of
companies &amp;amp; industries aren’t fully recovered from COVID yet.&lt;/p&gt;

&lt;p&gt;And right in the middle of that are company after company turning to corporate
vampires and sucking the open source communities that built them dry. Is it any
wonder that people are activating?&lt;/p&gt;

&lt;p&gt;Back in the early 2000’s, I regularly protested with the EFF on the Santa Monica
Pier in support of all sorts of digital rights and privacy protections. Not all
the bystanders knew who the EFF was or what we were talking about. But most did
or were curious to hear about it.&lt;/p&gt;

&lt;p&gt;I’m getting some of the same vibes now. People are realizing that they need to take
their interests into their own hands again. VC money has been cheap and easy for
a very long time. And along with the foosball tables, that meant quite a bit of
budget flexibility. It didn’t matter so much if you spent a few days polishing up
a patch to contribute upstream when the runway was so long.&lt;/p&gt;

&lt;p&gt;That’s been on the downswing for a while, but this last year really brought everything
to a head. The investors and board members have come calling and they want their
pound of flesh. And when it comes down to it, your boss cares more about the bottom
line and the market cap than they do about you or your silly open source ideals.
The days of founding a startup with a pair of dice are done. No more &lt;em&gt;Uber but for
dogwalking&lt;/em&gt; or &lt;em&gt;AirBnb but for retirement homes&lt;/em&gt; or &lt;em&gt;Tinder but for telling jokes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now it’s time for solidarity. People are realizing that the company they once trusted
doesn’t actually have their best interests in mind. They’re seeing through the
permissive license and contributor license agreement blinders. And they’re viscerally
coming to grips with the idea that very few businesses include &lt;em&gt;“provide something
of value”&lt;/em&gt; as an actual business goal.&lt;/p&gt;

&lt;p&gt;And they’re starting to do something about it.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Das neue Puppet Open Source Projekt heißt &quot;OpenVox&quot;</title>
    <link href="https://dev.to/betadots/das-neue-puppet-open-source-projekt-heisst-openvox-2330"/>
    <id>https://dev.to/betadots/das-neue-puppet-open-source-projekt-heisst-openvox-2330</id>
    <updated>2025-01-28T15:00:38+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;&lt;a href=&quot;https://puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet by Perforce&lt;/a&gt; hat &lt;a href=&quot;https://www.puppet.com/blog/open-source-puppet-updates-2025&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://github.com/OpenVoxProject/planning/discussions/9&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://voxpupuli.org&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Vox Pupuli&lt;/code&gt; Puppet Open Source Community&lt;/a&gt; auf &lt;a href=&quot;https://github.com/voxpupuli&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://github.com/puppetlabs&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet Open Source Code&lt;/a&gt; wurde in das &lt;a href=&quot;https://github.com/openvoxproject&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://github.com/voxpupuli/container-puppetserver&quot; rel=&quot;noopener noreferrer&quot;&gt;puppetserver&lt;/a&gt; und &lt;a href=&quot;https://github.com/voxpupuli/container-puppetdb&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://github.com/OpenVoxProject/container-openvoxserver&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenVox Server&lt;/a&gt; und &lt;a href=&quot;https://github.com/OpenVoxProject/container-openvoxdb&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://voxpupuli.org/openvox/install/&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://www.betadots.de&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://overlookinfratech.com/&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://voxpupuli.org/openvox/support/&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;//mailto:info@betadots.de&quot;&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;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: First release, hot off the presses!</title>
    <link href="https://overlookinfratech.com/2025/01/21/first-release-hot-off-the-presses/"/>
    <id>https://overlookinfratech.com/2025/01/21/first-release--hot-off-the-presses</id>
    <updated>2025-01-21T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;It’s been quite a journey, y’all. But we’re excited to announce the first
release of &lt;a href=&quot;https://voxpupuli.org/openvox/&quot;&gt;OpenVox&lt;/a&gt;, the community-maintained
open source implementation of Puppet. OpenVox 8.11 is functionally equivalent to
Puppet and should be a drop-in replacement. Be aware, of course, that even
though you can type the same commands, use all the same modules and extensions,
and configure the same settings, OpenVox is not yet tested to the same standard
that Puppet is.&lt;/p&gt;

&lt;p&gt;Migrating is fairly simple, just replace the packages following instructions on
the &lt;a href=&quot;https://voxpupuli.org/openvox/install/&quot;&gt;handy dandy new install page&lt;/a&gt;.
You’ll notice that they’re still using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt|yum.overlookinfratech.com&lt;/code&gt;
repositories. As we get our infrastructure built out, these will probably be
moved to the voxpupuli.org namespace. Please don’t use these packages on
critical production infrastructures yet, unless you’re comfortable with
troubleshooting and reporting back on the silly errors we’ve made while
rebranding and rebuilding.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;If you’d like professional assistance in the migration, check out our new
&lt;a href=&quot;/services/&quot;&gt;OpenVox migration service&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We consider OpenVox a soft-fork because we intend to maintain downstream
compatibility for as long as we are able. As such, Vox Pupuli is working with
Perforce create a Puppet™️ Standards Steering Committee to set the direction of
features and language evolutions.&lt;/p&gt;

&lt;p&gt;The OpenVox project goals are pretty straightforward and aim to alleviate pain
points observed by the community over the last few years:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Modernizing the OpenVox codebase and ecosystem including supporting current operating systems.&lt;/li&gt;
  &lt;li&gt;Recentering and focusing on community requirements; user needs will drive development.&lt;/li&gt;
  &lt;li&gt;Democratizing platform support by allowing community members to contribute what they need instead of waiting for business requirements to align.&lt;/li&gt;
  &lt;li&gt;Maintaining an active and responsive open source community like the rest of Vox Pupuli’s namespace.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;migration-service&quot;&gt;&lt;strong&gt;Migration Service&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Overlook InfraTech is an actual company with bills to pay and everything, so you
knew there was going to be a plug here eventually. But we’ll keep it short and
tasteful. We have been instrumental to the OpenVox fork right from the very
beginning and we know inside and out what changes to expect. We are best
situated to help you, so if you’d like professional assistance, or even just
someone to look over your (virtual) shoulder during the process, we have a very
&lt;a href=&quot;/services/&quot;&gt;reasonably priced migration service&lt;/a&gt;.
Whether you’re ready to jump in and try it out today, or you’d rather wait a bit
and let the dust settle, we’ll be here to help.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: The Ruby side of Puppet - Part 3 - Custom Types and Providers</title>
    <link href="https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma"/>
    <id>https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma</id>
    <updated>2025-01-20T18:50:29+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is the final post in a three-part serie, covering the concepts and best practices for extending &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; using &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;custom facts&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;custom functions&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;custom types&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7&quot;&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=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7&quot;&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&#39;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=&quot;https://www.puppet.com/docs/puppet/latest/type.html&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;param&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;https://www.puppet.com/docs/puppet/latest/custom_types#custom_types&quot; rel=&quot;noopener noreferrer&quot;&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;&quot;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.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

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

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

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ... the code ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;betadots&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# &amp;lt;- title
&lt;/span&gt;&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app config setting to manage. see app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;namevar: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;The app config setting key to manage. see app_cli conf --help&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app config setting to manage. see app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The config value to set.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a valid value&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;https://www.puppet.com/docs/puppet/latest/custom_types#tandp_properties_and_parameters&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        cli_args =&amp;gt; [&#39;-p&#39;], # persistency
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app config setting to manage. see app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The config value to set.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a valid value&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CLI options to use during command execution.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a an array&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;defaultto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;-p&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/parameter/boolean&#39;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:force&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:boolean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Parameter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;autorequire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pre_run_check&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app.exe&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;App not installed&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        file     =&amp;gt; &#39;/opt/app/etc/app.cfg&#39;,
        cli_args =&amp;gt; [&#39;-p&#39;], # persistency
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# global feature&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# feature :cli, &quot;The cli feature requires some parameters&quot;, :methods =&amp;gt; [:cli]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app config setting to manage. see app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The config value to set.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a valid value&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# The property config_file must be set when using the cli &lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#   option to configure app.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# The cli provider will check for a feature.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:required_features&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w{cli}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The config file to use.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not an absolute path&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;defaultto: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/etc/app.cfg&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CLI options to use during command execution.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a an array&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;defaultto: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;-p&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types cli provider.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;App_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types cli provider reuses shared/common code from app_config.rb.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;../../puppet_x/betadots/app_api.rb&#39;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types cli provider reuses shared/common code from app_config.rb.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/token.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types token provideris an extension to the api provider.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/file.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ini_setting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types token provideris an extension to the api provider.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;table-wrapper-paragraph&quot;&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; &quot;/opt/app/bin/app.exe&quot;&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; &quot;/opt/app/etc/app.conf&quot;&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 &#39;os.family&#39; =&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 &#39;os.family&#39; =&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   app_config: { &#39;enable_logging&#39;:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     ensure   =&amp;gt; present,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     value    =&amp;gt; true,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   }&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types cli provider.&quot;&lt;/span&gt;

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

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

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

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

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

  &lt;span class=&quot;c1&quot;&gt;# setter&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;yaml&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instances&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attributes_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;key: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;ensure: :present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prefetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/services.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;feature&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:refreshable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The provider can restart the service.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:methods&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;refresh&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Only restart if we&#39;re actually running&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;retrieve&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:running&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;restart&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Skipping restart; service is not running&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Type declaration
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;enable_logging&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;require&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/etc/app.cfg&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Type reference
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;enable_logging&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Type declaration using lambda and splat operator
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$config_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Virtual or exported resource
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app_mount&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{facts[&#39;networking&#39;][&#39;fqdn&#39;]}/srv/app_mount&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;tag&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;blog&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Resource collector
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;App_config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;blog&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/resource_api&#39;&lt;/span&gt;

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

      This type provides Puppet with the capabilities to manage our application config
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    EOS&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;features: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;attributes: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ensure: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type:    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Enum[present, absent]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc:    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Whether the setting should be added or removed&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;default: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;present&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type:     &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;String&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc:     &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;The setting to manage&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;behavior: :namevar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Variant[String, Integer, Array]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;The value to set&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/resource_api/simple_provider&#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;open3&#39;&lt;/span&gt;

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

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

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

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

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

  &lt;span class=&quot;c1&quot;&gt;# Delete: remove a config setting&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Deleting &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe rm &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;https://github.com/betadots/workshop-demo-app&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://github.com/betadots/workshop-demo-module/tree/ruby_workshop&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&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=&quot;c&quot;&gt;# Install Puppet module:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-module &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; ruby_workshop modules/app
&lt;span class=&quot;c&quot;&gt;# Type API V1&lt;/span&gt;
puppet resource app_config &lt;span class=&quot;nt&quot;&gt;--modulepath&lt;/span&gt; modules
&lt;span class=&quot;c&quot;&gt;# Type Resource API&lt;/span&gt;
puppet resource app_config2 &lt;span class=&quot;nt&quot;&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;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Die Ruby-Seite von Puppet - Teil 3 - Benutzerdefinierte Typen und Provider</title>
    <link href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp"/>
    <id>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp</id>
    <updated>2025-01-20T18:48:32+00:00</updated>
    <content type="html" xml:lang="en">
    &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=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; mithilfe von &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Fakten&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Funktionen&lt;/a&gt; und &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Typen&lt;/a&gt; sowie &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;Providern&lt;/a&gt; behandelt.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj&quot;&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=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3&quot;&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=&quot;https://www.puppet.com/docs/puppet/latest/type.html&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;param&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;https://www.puppet.com/docs/puppet/latest/custom_types#custom_types&quot; rel=&quot;noopener noreferrer&quot;&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;&quot;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.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

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

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

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ... der Code ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten
    zur Authentifizierung: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;betadots&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# &amp;lt;- Titel
&lt;/span&gt;&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;namevar: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Der Schlüssel des Anwendungsconfig-Elements, das verwaltet werden soll. Siehe app_cli conf --help&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der zu setzende Konfigurationswert.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein gültiger Wert&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;https://www.puppet.com/docs/puppet/latest/custom_types#tandp_properties_and_parameters&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        cli_args =&amp;gt; [&#39;-p&#39;], # Persistenz
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der zu setzende Konfigurationswert.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein gültiger Wert&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CLI-Optionen, die während der Befehlsausführung verwendet werden sollen.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein Array&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;defaultto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;-p&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/parameter/boolean&#39;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:force&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:boolean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Parameter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;autorequire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pre_run_check&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app.exe&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;App nicht installiert&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        file     =&amp;gt; &#39;/opt/app/etc/app.cfg&#39;,
        cli_args =&amp;gt; [&#39;-p&#39;], # Persistenz
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# globales Merkmal&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# feature :cli, &quot;Das CLI-Merkmal erfordert einige Parameter&quot;, :methods =&amp;gt; [:cli]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der zu setzende Konfigurationswert.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein gültiger Wert&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Die Eigenschaft config_file muss gesetzt werden, wenn die CLI&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#   Option zur Konfiguration von App verwendet wird.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Der CLI-Provider wird nach einem Merkmal suchen.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:required_features&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w{cli}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Die zu verwendende Konfigurationsdatei.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein absoluter Pfad&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;defaultto&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/etc/app.cfg&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CLI-Optionen, die während der Befehlsausführung verwendet werden sollen.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein Array&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;defaultto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;-p&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der CLI-Anbieter des app_config-Typs.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;App_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der CLI-Anbieter des app_config-Typs verwendet gemeinsamen Code aus app_config.rb.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;../../puppet_x/betadots/app_api.rb&#39;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der CLI-Anbieter des app_config-Typs verwendet gemeinsamen Code aus app_config.rb.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/token.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der Token-Anbieter des app_config-Typs ist eine Erweiterung des API-Anbieters.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/file.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ini_setting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der Datei-Anbieter des app_config-Typs ist eine Erweiterung des API-Anbieters.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;table-wrapper-paragraph&quot;&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; &quot;/opt/app/bin/app.exe&quot;&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; &quot;/opt/app/etc/app.conf&quot;&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 &#39;os.family&#39; =&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 &#39;os.family&#39; =&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   app_config: { &#39;enable_logging&#39;:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     ensure   =&amp;gt; present,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     value    =&amp;gt; true,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   }&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der CLI-Anbieter des app_config-Typs.&quot;&lt;/span&gt;

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

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

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

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

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

  &lt;span class=&quot;c1&quot;&gt;# setter&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;yaml&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instances&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attributes_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;key: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;ensure: :present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prefetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/services.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;feature&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:refreshable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der Anbieter kann den Dienst neu starten.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:methods&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;refresh&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Nur neu starten, wenn wir tatsächlich laufen&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;retrieve&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:running&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;restart&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Neustart überspringen; Dienst läuft nicht&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Typdeklaration
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;enable_logging&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;require&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/etc/app.cfg&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Typreferenz
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;enable_logging&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Typdeklaration mit Lambda und Splat-Operator
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$config_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Virtuelle oder exportierte Ressource
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;app_mount&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{facts[&#39;networking&#39;][&#39;fqdn&#39;]}/srv/app_mount&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;tag&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;blog&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Ressourcen-Sammler
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;App_config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;blog&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/resource_api&#39;&lt;/span&gt;

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

      Dieser Typ bietet Puppet die Möglichkeit, unsere Anwendungsconfig zu verwalten.
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    EOS&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;features: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;attributes: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ensure: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type:    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Enum[present, absent]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc:    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Ob die Einstellung hinzugefügt oder entfernt werden soll&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;default: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;present&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type:     &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;String&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc:     &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Die Einstellung, die verwaltet werden soll&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;behavior: :namevar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Variant[String, Integer, Array]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Der Wert, der gesetzt werden soll&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/resource_api/simple_provider&#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;open3&#39;&lt;/span&gt;

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

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

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

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

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

  &lt;span class=&quot;c1&quot;&gt;# Delete: Eine Konfigurationseinstellung entfernen&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Lösche &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe rm &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;https://github.com/betadots/workshop-demo-app&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://github.com/betadots/workshop-demo-module/tree/ruby_workshop&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&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=&quot;c&quot;&gt;# Puppet-Modul installieren:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-module &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; ruby_workshop modules/app
&lt;span class=&quot;c&quot;&gt;# Type API V1&lt;/span&gt;
puppet resource app_config &lt;span class=&quot;nt&quot;&gt;--modulepath&lt;/span&gt; modules
&lt;span class=&quot;c&quot;&gt;# Type Resource API&lt;/span&gt;
puppet resource app_config2 &lt;span class=&quot;nt&quot;&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;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: The Ruby side of Puppet - Part 2 - Custom Functions</title>
    <link href="https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7"/>
    <id>https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7</id>
    <updated>2025-01-14T16:04:08+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is the second post in a series of three, covering the concepts and best practices for extending &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; using &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;custom facts&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;custom functions&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;custom types&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7&quot;&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=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma&quot;&gt;Part 3&lt;/a&gt; covers Custom Types and Providers, showing how to extend Puppet&#39;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&#39;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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;server_hash&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;postfix&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;transport&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mta&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssl&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&#39;apache&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;vhost&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;server.domain.tld&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;document_root&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/srv/www/html/server.domain.tld&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssl&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&#39;proxy_pass&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://localhost:8080&#39;&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tomcat&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;application&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;crm&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;service&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;frontend&#39;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# test data&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;server_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;postfix&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;transport&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;mta&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;ssl&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;apache&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;vhost&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;server.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;document_root&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/srv/www/html/server.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;ssl&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;proxy_pass&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;http://localhost:8080&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;tomcat&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;application&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;crm&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;service&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;frontend&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# function definition&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_service_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# testing the function&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read_service_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;tomcat&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&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&#39;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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# &amp;lt;modulepath&amp;gt;/stdlib/&lt;/span&gt;
  &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; lib/
    &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; puppet/
      &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; functions/
        &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; stdlib/
          &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; to_yaml.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# stdlib/lib/puppet/functions/stdlib/to_yaml.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;stdlib::to_yaml&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;table-wrapper-paragraph&quot;&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=&quot;table-wrapper-paragraph&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;resolv&#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

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

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

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

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

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;local_hostname&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gethostname&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;https://www.puppet.com/docs/puppet/8/deferring_functions.html&quot; rel=&quot;noopener noreferrer&quot;&gt;deferred function&lt;/a&gt; which gets executed on the agent.&lt;/p&gt;

&lt;p&gt;Let&#39;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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;betadots::resolver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Stdlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Fqdn&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$local_hostname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;betadots::resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$ip_from_google&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;betadots::resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;www.google.com&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;betadots::local_resolve&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$local_api_ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;api.int.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;https://github.com/puppetlabs/puppetlabs-stdlib&quot; rel=&quot;noopener noreferrer&quot;&gt;Stdlib-&lt;/a&gt; or &lt;a href=&quot;https://github.com/voxpupuli/puppet-extlib&quot; rel=&quot;noopener noreferrer&quot;&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;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Die Ruby-Seite von Puppet - Teil 2 - Benutzerdefinierte Funktionen</title>
    <link href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3"/>
    <id>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3</id>
    <updated>2025-01-14T16:01:03+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Dies ist der zweite Beitrag einer dreiteiligen Serie, in der die Konzepte und Best Practices zum Erweitern von &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; mithilfe von &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Facts&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Funktionen&lt;/a&gt; und &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Typen&lt;/a&gt; und &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;Providern&lt;/a&gt; behandelt werden.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj&quot;&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=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;server_hash&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;postfix&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;transport&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mta&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssl&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&#39;apache&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;vhost&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;server.domain.tld&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;document_root&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/srv/www/html/server.domain.tld&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssl&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&#39;proxy_pass&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://localhost:8080&#39;&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tomcat&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;application&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;crm&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;service&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;frontend&#39;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Test Daten&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;server_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;postfix&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;transport&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;mta&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;ssl&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;apache&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;vhost&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;server.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;document_root&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/srv/www/html/server.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;ssl&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;proxy_pass&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;http://localhost:8080&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;tomcat&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;application&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;crm&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;service&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;frontend&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Funktion definition&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_service_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Testen der Funktion&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read_service_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;tomcat&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# &amp;lt;modulepath&amp;gt;/stdlib/&lt;/span&gt;
  &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; lib/
    &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; puppet/
      &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; functions/
        &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; stdlib/
          &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; to_yaml.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# stdlib/lib/puppet/functions/stdlib/to_yaml.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;stdlib::to_yaml&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;table-wrapper-paragraph&quot;&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=&quot;table-wrapper-paragraph&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;resolv&#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

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

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

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

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

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;local_hostname&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gethostname&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;https://www.puppet.com/docs/puppet/8/deferring_functions.html&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;betadots::resolver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Stdlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Fqdn&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$local_hostname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;betadots::resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$ip_from_google&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;betadots::resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;www.google.com&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;betadots::local_resolve&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$local_api_ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;api.int.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;https://github.com/puppetlabs/puppetlabs-stdlib&quot; rel=&quot;noopener noreferrer&quot;&gt;Stdlib-&lt;/a&gt; oder &lt;a href=&quot;https://github.com/voxpupuli/puppet-extlib&quot; rel=&quot;noopener noreferrer&quot;&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;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: The strength of community</title>
    <link href="https://overlookinfratech.com/2025/01/08/the-strength-of-community/"/>
    <id>https://overlookinfratech.com/2025/01/08/the-strength-of-community</id>
    <updated>2025-01-08T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Everything’s been super hectic with the holidays and so nobody noticed that I
didn’t post the last two weeks, right? I joke, but not really. We’ve been super
busy too and not just with the regular holiday stuff. In between the
celebrations and glühwein, we’ve been working on community building.&lt;/p&gt;

&lt;p&gt;As a community, we decided on the &lt;a href=&quot;https://github.com/openvoxproject&quot;&gt;name&lt;/a&gt; for
our new community maintained Puppet™️ fork and we decided that
&lt;a href=&quot;https://github.com/voxpupuli/plumbing/issues/294&quot;&gt;Vox Pupuli would adopt it&lt;/a&gt;.
Now we’re busy creating the organization governance, porting all the
repositories in, and setting up the pipelines and build tooling to streamline
package publication. You’ll see a lot more information about things soon when
everything’s done and we make the official announcement, but that’s a lot of
work for a few weeks over the holidays when people are usually checked out!&lt;/p&gt;

&lt;p&gt;Our biggest remaining challenge right now is determining release criteria that
balances establishing our own brand presence against ecosystem safety all while
respecting Perforce’s trademarks. I’m talking about agreeing on what names need
to be changed before we can release packages and call them our own. I’m grateful
for David, Jake, Sara, and others from Perforce who have given us loads of their
own time to help us get to a resolution in everyone’s benefit.&lt;/p&gt;

&lt;p&gt;This ability to pull together and work to find solutions, even over strong
disagreements, is one of the indicators of a strong community. I’m confident
that this spirit of collaboration is going to continue as we build the new
Puppet™️ Language Steering Committee to guide our shared future. I’m quite
excited for this steering committee because it’s got representatives from the
entire community, even including Perforce itself. It’s the first time that
community user needs sit at the same table as the business needs do without
anyone having veto power over the other.&lt;/p&gt;

&lt;p&gt;Being at the same table ensures that we don’t diverge too much and that we stay
compatible and interoperable. These efforts, along with others like converging on 
acceptable compatibility names, will help ensure that the same Puppet™️ Forge 
works for all of us, that Vox Pupuli developer tools remain usable on Puppet 
Enterprise, that the PDK builds modules for whatever Puppet™️ implementation 
you happen to be running, and that our entire ecosystem remains collaborative 
and engaging regardless of where we work.&lt;/p&gt;

&lt;p&gt;I’m kind of digging where we are. Some of it may have been born out of conflict
and there were some hurt feelings all around. But being able to talk things over, 
share our needs, and work together to resolve them means that together we are
a community and together we will build an ecosystem that’s stronger than it ever
has been before.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Die Ruby-Seite von Puppet - Teil 1 - Benutzerdefinierte Fakten</title>
    <link href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj"/>
    <id>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj</id>
    <updated>2025-01-01T17:26:54+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Dies ist der erste von drei Beiträgen, die die Konzepte und Best Practices zur Erweiterung von &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; mit Hilfe von &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Fakten&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/8/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Funktionen&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Typen&lt;/a&gt; und &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3&quot;&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=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code hier&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;kernel: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code hier&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code hier&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code hier&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;table-wrapper-paragraph&quot;&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[&#39;PATH&#39;] + [&#39;/sbin&#39;, &#39;/usr/sbin&#39;]&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=&quot;https://www.rubydoc.info/gems/facter&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;name&amp;gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;cmd&amp;gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config_list&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Debian&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/default/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/windows_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:windows_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;osfamily: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;reg query &quot;HKLM\Software\Microsoft\Internet Explorer&quot; /v svcVersion&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;https://docs.microsoft.com/en-us/windows/win32/api/&quot; rel=&quot;noopener noreferrer&quot;&gt;Win32 API calls&lt;/a&gt; seit Ruby 1.9 deprecated sind.&lt;br&gt;
Man kann statt dessen die &lt;a href=&quot;https://github.com/ruby/fiddle&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Custom fact &#39;betadots_application_version&#39; running&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Debian&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/default/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

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

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

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:config_log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:log_level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config log_level&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;betadots_application_version =&amp;gt; {
  version =&amp;gt; &#39;1.2.4&#39;,
  cache_size =&amp;gt; &#39;400M&#39;,
  log_level =&amp;gt; &#39;info&#39;,
}
&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=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts#configuring_facts-fact-precedence&quot; rel=&quot;noopener noreferrer&quot;&gt;Fact precedence&lt;/a&gt; Sektion der &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;Custom facts overview&lt;/a&gt; Seite.&lt;br&gt;
&lt;/p&gt;

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

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

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

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/var/run/application.pid&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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 &quot;teure&quot; 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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# /etc/puppetlabs/facter/facter.conf
facts : {
  blocklist : [ &quot;file system&quot;, &quot;EC2&quot;, &quot;processors.isa&quot; ]
  ttls : [
    { &quot;timezone&quot;: 30 days },
    { &quot;my-fact-group&quot;: 30 days },
  ]
}
fact-groups : {
  my-fact-group : [ &quot;os&quot;, &quot;ssh.version&quot;]
}
&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;facter &lt;span class=&quot;nt&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;facter &lt;span class=&quot;nt&quot;&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=&quot;https://forge.puppet.com/modules/dylanratcliffe/facter_cache/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;Dylan Ratcliffe&#39;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=&quot;https://www.puppet.com/docs/puppet/latest/configuring_facter&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://www.puppet.com/docs/puppet/8/custom_facts.html#structured_data_in_custom_facts&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_file_check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;c:/Program Data/Application/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;c:/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

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

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64328&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;438&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_file_check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;c:/Program Data/Application/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;c:/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

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

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

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

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

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64328&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;438&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_cert_extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;openssl&#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet&#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/ssl/oids&#39;&lt;/span&gt;

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

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

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

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

    &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: The Ruby side of Puppet - Part 1 - Custom Facts</title>
    <link href="https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7"/>
    <id>https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7</id>
    <updated>2025-01-01T17:26:04+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is the first in a series of three posts covering the concepts and best practices for extending &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; using &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;custom facts&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/8/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;custom functions&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;custom types&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7&quot;&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=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&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&#39;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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;kernel: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;table-wrapper-paragraph&quot;&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[&#39;PATH&#39;] + [&#39;/sbin&#39;, &#39;/usr/sbin&#39;]&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=&quot;https://www.rubydoc.info/gems/facter&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;name&amp;gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;cmd&amp;gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config_list&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Debian&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/default/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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&#39;s an example of a Windows custom fact that retrieves the version of Internet Explorer:&lt;br&gt;
&lt;/p&gt;

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

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;reg query &quot;HKLM\Software\Microsoft\Internet Explorer&quot; /v svcVersion&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;https://docs.microsoft.com/en-us/windows/win32/api/&quot; rel=&quot;noopener noreferrer&quot;&gt;Win32 API calls&lt;/a&gt; are deprecated since ruby 1.9.&lt;br&gt;
Please consider using &lt;a href=&quot;https://github.com/ruby/fiddle&quot; rel=&quot;noopener noreferrer&quot;&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&#39;s often useful to include logging within custom facts to help with troubleshooting and development. You can log messages using Puppet&#39;s built-in logging mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Custom fact &#39;betadots_application_version&#39; running&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Debian&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/default/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

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

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

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:config_log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:log_level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config log_level&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;betadots_application_version =&amp;gt; {
  version =&amp;gt; &#39;1.2.4&#39;,
  cache_size =&amp;gt; &#39;400M&#39;,
  log_level =&amp;gt; &#39;info&#39;,
}
&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&#39;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=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts#configuring_facts-fact-precedence&quot; rel=&quot;noopener noreferrer&quot;&gt;Fact precedence&lt;/a&gt; section of the &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;Custom facts overview page&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

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

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

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

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/var/run/application.pid&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# /etc/puppetlabs/facter/facter.conf
facts : {
  blocklist : [ &quot;file system&quot;, &quot;EC2&quot;, &quot;processors.isa&quot; ]
  ttls : [
    { &quot;timezone&quot;: 30 days },
    { &quot;my-fact-group&quot;: 30 days },
  ]
}
fact-groups : {
  my-fact-group : [ &quot;os&quot;, &quot;ssh.version&quot;]
}
&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;facter &lt;span class=&quot;nt&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;facter &lt;span class=&quot;nt&quot;&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=&quot;https://forge.puppet.com/modules/dylanratcliffe/facter_cache/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;Dylan Ratcliffe&#39;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=&quot;https://www.puppet.com/docs/puppet/latest/configuring_facter&quot; rel=&quot;noopener noreferrer&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_file_check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;c:/Program Data/Application/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;c:/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

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

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/opt/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64328&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/etc/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;txt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;438&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_file_check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;c:/Program Data/Application/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;c:/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

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

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

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

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

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

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/opt/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64328&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/etc/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;txt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;438&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&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=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_cert_extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;openssl&#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet&#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/ssl/oids&#39;&lt;/span&gt;

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

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

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

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

    &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&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;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Letting go of control</title>
    <link href="https://overlookinfratech.com/2024/12/19/letting-go-of-control/"/>
    <id>https://overlookinfratech.com/2024/12/19/letting-go-of-control</id>
    <updated>2024-12-19T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Events of the last week have me thinking about something that’s true in a lot of
contexts. A lot of people like to have control of a relationship; control of a
situation; control of the message; or in this case, control of a community. I
get it, realizing that you don’t have control can be unsettling. But in reality,
not having control is the natural order of things and you’ll have a much better
time of it once you realize that.&lt;/p&gt;

&lt;p&gt;Let’s dive in. A controlling relationship is an easy start to the conversation,
since most of us have either been in one or have witnessed one. We may have even been
the offender; it’s very easy to accidentally fall into this role under certain
circumstances. A relationship controller is quite often someone who’s terrified
that the other(s) will leave or hurt them and so they tighten their grip more
and more and more trying to prevent this. We all know the usual outcome; that
grip becomes so tight that it &lt;em&gt;forces&lt;/em&gt; an escape. The inability to let go of
control leads to exactly the outcome that was most feared to begin with.&lt;/p&gt;

&lt;p&gt;The same thing happens when one tries too hard to control a message. I don’t
have to explain it to you. Google the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Streisand_effect&quot;&gt;Barbra Streisand effect&lt;/a&gt; for that.&lt;/p&gt;

&lt;p&gt;As a seasoned community leader, I’m most interested in the context of community
engagement. And in particular, technical open source communities centered around
a product ecosystem — although this fundamentally applies to other community
types as well. See, a lot of Product communities have a power imbalance at their
very core. If the company can gatekeep merges, and packaging, and releases —
then they have an outsized say in where the product goes. If they don’t want to
invest in supporting Apple Silicon, it ain’t gonna happen &lt;em&gt;even if community
members volunteer to do the work.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Many companies mistake this imbalance as having control, even to the point of
treating community members as if they were unpaid employees. I’ll give you a
personal example of that. For most of our history, Puppet had an excellent
community relationship because we remembered that we built ourselves off of
their passion. As money got tight, some of the niceties got strained and some of
the technical engagement started lagging. But we always treated them with
respect and supported them as they grew and evolved the community. When they
created &lt;a href=&quot;https://voxpupuli.org&quot;&gt;Vox Pupuli&lt;/a&gt;, we celebrated and we never stopped
celebrating it. Just before the pandemic did us in, a large part of what turned out
to be Puppet’s final PuppetConf/Puppetize keynote centered Vox Pupuli as the
independent community group that our success hinged on.&lt;/p&gt;

&lt;p&gt;This changed rapidly after the Perforce acquisition. The community started being
treated as liabilities and resources. At one point, the VP of Product at the
time insisted on holding a community Zoom call &lt;em&gt;the very next day or two&lt;/em&gt; to get
feedback on some ideas he had. He said, “of course they’ll do it. They want to
have input on what we sell!”&lt;/p&gt;

&lt;p&gt;I’m sure they do. I collect feedback like that all the time. But community
members who are freely giving their time for something they enjoy in a space
where they feel welcomed and valued &lt;em&gt;do not want to feel obligated to do so!&lt;/em&gt;
That’s called a job and you collect a paycheck for it.&lt;/p&gt;

&lt;p&gt;He didn’t understand when I tried to explain that he didn’t &lt;em&gt;control them&lt;/em&gt;. They
weren’t employees and he couldn’t expect them to prioritize his demands. He
almost fired me when I refused.&lt;/p&gt;

&lt;p&gt;Most companies in this position don’t understand how fragile and tenuous this
power imbalance is. It only lasts so long as you own the roadmap and people
still care enough to put up with your bullshit. As soon as the frustrations
community feels about your behavior outweighs the work it is to just take on the
roadmap themselves, then this power imbalance evaporates in &lt;em&gt;an instant&lt;/em&gt;. And it’s
generally irreversible because you’ve already tipped your hand as to how you’ll
behave when you feel like you have the advantage. Your voice is no longer welcomed.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;&lt;em&gt;A Product company never has control over their open source user community.
They only have an illusion of it and by the time they realize this, it’s
generally too late to recover.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To build a healthier community, they should be actively relinquishing control
and cultivating influence instead. They will get very similar results, they just
have to be more patient, honest, open, transparent,… You know, all the things
that make a relationship work.&lt;/p&gt;

&lt;p&gt;When you realize that you have zero control but as much influence as you are
willing to develop, then you start behaving in ways that build a strong, loyal,
and supportive community that will help see you through your missteps and always
make sure that you have a seat at the table when decisions are being made.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You don’t lie to your community. You don’t always have to tell every single detail, and you can always couch things in softer terms or provide context. But you never lie.&lt;/li&gt;
  &lt;li&gt;You don’t make empty promises or excuses.  You’re clear about what you can or can’t do.&lt;/li&gt;
  &lt;li&gt;You actively listen to their feedback and you care about it. In specific, you hear the pain that they have more than the words they use or the solutions they offer. You never “empty listen” and then throw it in the trash.&lt;/li&gt;
  &lt;li&gt;You take action on that feedback if warranted, but you’re always clear what should be expected.&lt;/li&gt;
  &lt;li&gt;You get really good at hearing what community wants and getting there before they have to ask for it. This gives you more influence in the growth patterns, just like cultivating a garden.&lt;/li&gt;
  &lt;li&gt;You participate in the day-to-day of the community and get to know what people value.&lt;/li&gt;
  &lt;li&gt;You are a role model, not a dictator.&lt;/li&gt;
  &lt;li&gt;You remind rather than enforce. In all the years I participated in and then managed the Puppet community, I rarely had to moderate users more than reminding them of the code of conduct.&lt;/li&gt;
  &lt;li&gt;You meet community where they are and where they want to be. You help them build a better table rather than insisting that they sit at yours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, listen more than you proclaim, realize the value of your community
members and never take them for granted, and help them build the space that they
want — where they can feel welcome, accepted, and valued. Connect with people in
a human way, rather than as a corporation using carefully crafted corporate
doublespeak.&lt;/p&gt;

&lt;p&gt;And let go of this anxiety-driven idea of control. You never had it anyways, and
influence is far more valuable. Influence helps you build a lasting community
and solid open source project in which you have a respected seat at the table.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Contributions of ‘Ghost Engineers’</title>
    <link href="https://overlookinfratech.com/2024/12/05/contributions-of-ghost-engineers/"/>
    <id>https://overlookinfratech.com/2024/12/05/contributions-of-ghost-engineers</id>
    <updated>2024-12-05T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Last week was Thanksgiving for the USA and while I don’t personally celebrate
the holiday or the violent colonization it represents, it does encourage me to
reflect back on some of the things I’m most thankful for. Once again, you won’t
be surprised to hear that it’s people. Coworkers, friends, community members,
industry professionals that I learn from and am inspired by every day. People
that I’m lucky enough to collaborate with, which probably includes many of you
reading this. People in my communities are what I’m thankful for.&lt;/p&gt;

&lt;p&gt;Collaboration is one of the things that I’ve been thinking about a lot over the
last few months while getting this fun little venture off the ground. I think
that most companies don’t prioritize collaboration as much as they should and I
want to avoid that trap. And I’m definitely thinking about macro business
decisions like &lt;em&gt;why did Puppet, inc. invest &lt;strong&gt;so much&lt;/strong&gt; time and resources into
building an orchestration tool from scratch instead of integrating with one of
the many existing options, even Ansible itself?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔆 But even more, I’m thinking about micro level choices, because I think
those are how you define yourself as a people and then that culture bubbles up
into how your business runs, from top to bottom.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many of you know that I’ve been involved in the roller derby world for a very
long time. My partner has been a founding member of several teams and we are in
the home of one of the premiere leagues in the world. One of the things that
continues to draw me to the sport is their sense of community. The big dollars
haven’t invaded yet and so they’re just there to have fun. They’ll beat each
other up on the track and then share an afterparty. When a bout concludes, the
audience will rush to circle the track because every single player will
high-five every single fan who wants it. Then if you pay attention, there’s a
ceremony in which the teams give gifts to each other and award several MVP
awards &lt;em&gt;to the other team&lt;/em&gt;. It’s worth pointing out that these awards don’t go to
the player scoring the most points or other easily trackable stats. They almost
always go to the players who are &lt;em&gt;&lt;strong&gt;the best at holding their team together into
a formidable force&lt;/strong&gt;&lt;/em&gt; as observed by the people playing against them.&lt;/p&gt;

&lt;p&gt;Recently, someone at Stanford who apparently knows very little about teamwork
published a study that claimed to reveal that almost 10% of all software
engineers were lazy and ineffective and didn’t contribute anything to the bottom
line. Their rationale for this proclamation was they contributed few or no lines
of code and the author deemed them “ghost engineers.” 🤨&lt;/p&gt;

&lt;p&gt;A lot of people more qualified than I explained how LOC is maybe the least
useful metric possible so I won’t go there. Instead, I want to look at it more
holistically. This is a symptom of the &lt;em&gt;bleed ‘em dry&lt;/em&gt; stack-ranking mentality
that wants an easily measured metric so that toxic companies can reward those on
top and punish those on the bottom in the misguided idea that it will improve
performance and help the company hit revenue targets. Of course, trivially
measured and manipulated metrics like this have nothing to do with the success
of your company. If I have a choice between doing something to benefit the
business or something that will get me a raise, what do you think I’m going to
pick?&lt;/p&gt;

&lt;p&gt;The other day, &lt;a href=&quot;https://hachyderm.io/@mekkaokereke/113547029714350019&quot;&gt;Mekka Okereke posted a sports metric&lt;/a&gt;
that I’d never heard of and it’s super intriguing. We’ve all heard of the stats
like points scored, number of rebounds or blocks or assists. But he talks about
a new metric that makes me think about those roller derby MVP awards. It’s
called the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plus-minus&lt;/code&gt; and seems to be a simple comparison of how well your
team does when you’re on the court vs. how it does when you’re not playing. 🤯&lt;/p&gt;

&lt;p&gt;Huh. I can see parallels in tech…. He goes on to say that&lt;/p&gt;

&lt;blockquote class=&quot;mastodon-embed&quot; data-embed-url=&quot;https://hachyderm.io/@mekkaokereke/113547086481399723/embed&quot; style=&quot;background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;&quot;&gt; &lt;a href=&quot;https://hachyderm.io/@mekkaokereke/113547086481399723&quot; target=&quot;_blank&quot; style=&quot;align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Oxygen, Ubuntu, Cantarell, &#39;Fira Sans&#39;, &#39;Droid Sans&#39;, &#39;Helvetica Neue&#39;, Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 79 75&quot;&gt;&lt;path d=&quot;M74.7135 16.6043C73.6199 8.54587 66.5351 2.19527 58.1366 0.964691C56.7196 0.756754 51.351 0 38.9148 0H38.822C26.3824 0 23.7135 0.756754 22.2966 0.964691C14.1319 2.16118 6.67571 7.86752 4.86669 16.0214C3.99657 20.0369 3.90371 24.4888 4.06535 28.5726C4.29578 34.4289 4.34049 40.275 4.877 46.1075C5.24791 49.9817 5.89495 53.8251 6.81328 57.6088C8.53288 64.5968 15.4938 70.4122 22.3138 72.7848C29.6155 75.259 37.468 75.6697 44.9919 73.971C45.8196 73.7801 46.6381 73.5586 47.4475 73.3063C49.2737 72.7302 51.4164 72.086 52.9915 70.9542C53.0131 70.9384 53.0308 70.9178 53.0433 70.8942C53.0558 70.8706 53.0628 70.8445 53.0637 70.8179V65.1661C53.0634 65.1412 53.0574 65.1167 53.0462 65.0944C53.035 65.0721 53.0189 65.0525 52.9992 65.0371C52.9794 65.0218 52.9564 65.011 52.9318 65.0056C52.9073 65.0002 52.8819 65.0003 52.8574 65.0059C48.0369 66.1472 43.0971 66.7193 38.141 66.7103C29.6118 66.7103 27.3178 62.6981 26.6609 61.0278C26.1329 59.5842 25.7976 58.0784 25.6636 56.5486C25.6622 56.5229 25.667 56.4973 25.6775 56.4738C25.688 56.4502 25.7039 56.4295 25.724 56.4132C25.7441 56.397 25.7678 56.3856 25.7931 56.3801C25.8185 56.3746 25.8448 56.3751 25.8699 56.3816C30.6101 57.5151 35.4693 58.0873 40.3455 58.086C41.5183 58.086 42.6876 58.086 43.8604 58.0553C48.7647 57.919 53.9339 57.6701 58.7591 56.7361C58.8794 56.7123 58.9998 56.6918 59.103 56.6611C66.7139 55.2124 73.9569 50.665 74.6929 39.1501C74.7204 38.6967 74.7892 34.4016 74.7892 33.9312C74.7926 32.3325 75.3085 22.5901 74.7135 16.6043ZM62.9996 45.3371H54.9966V25.9069C54.9966 21.8163 53.277 19.7302 49.7793 19.7302C45.9343 19.7302 44.0083 22.1981 44.0083 27.0727V37.7082H36.0534V27.0727C36.0534 22.1981 34.124 19.7302 30.279 19.7302C26.8019 19.7302 25.0651 21.8163 25.0617 25.9069V45.3371H17.0656V25.3172C17.0656 21.2266 18.1191 17.9769 20.2262 15.568C22.3998 13.1648 25.2509 11.9308 28.7898 11.9308C32.8859 11.9308 35.9812 13.492 38.0447 16.6111L40.036 19.9245L42.0308 16.6111C44.0943 13.492 47.1896 11.9308 51.2788 11.9308C54.8143 11.9308 57.6654 13.1648 59.8459 15.568C61.9529 17.9746 63.0065 21.2243 63.0065 25.3172L62.9996 45.3371Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt; &lt;div style=&quot;color: #787588; margin-top: 16px;&quot;&gt;Post by @mekkaokereke@hachyderm.io&lt;/div&gt; &lt;div style=&quot;font-weight: 500;&quot;&gt;View on Mastodon&lt;/div&gt; &lt;/a&gt; &lt;/blockquote&gt;
&lt;script data-allowed-prefixes=&quot;https://hachyderm.io/&quot; async=&quot;&quot; src=&quot;https://hachyderm.io/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;This resonates so much with me. I’ve always worked towards being this person,
and I’ve always really valued others with this mindset. It’s a shame that it’s
so rare, but again, tech companies rarely incentivize it.&lt;/p&gt;

&lt;p&gt;But that brings me to the actual point of this ramble. The more we
micro-optimize and track things like lines of code, or commits, or tickets
closed, or time on the clock, or even story points, the more we lose track of
the bigger shared company success goal. And the more we incentivize people to
game the system. It’s easy to write more lines of code or chop up work into a
million tickets and commits. It’s much harder to enable actual value.&lt;/p&gt;

&lt;p&gt;I don’t know the answer. Not yet. And maybe I never will; this is a challenge
people have been grappling with for ages and in a backwards sort of way, it’s
why we have the terrible metrics we do today. But I do know that the answer is
based on building a culture of shared team success rather than on myopically
focusing on individual metrics. &lt;strong&gt;&lt;em&gt;Because these “ghost engineers” are the biggest
contributors to our actual success and it’s about damn time they get some MVP
awards of their own. And recognition and compensation to match.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


</feed>