<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bryan Woolsey</title>
    <description>The latest articles on DEV Community by Bryan Woolsey (@ragnarkon).</description>
    <link>https://dev.to/ragnarkon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F416219%2F467325fb-3178-49e7-aa4f-77ebd9257a01.png</url>
      <title>DEV Community: Bryan Woolsey</title>
      <link>https://dev.to/ragnarkon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ragnarkon"/>
    <language>en</language>
    <item>
      <title>A Criticism of Scrum</title>
      <dc:creator>Bryan Woolsey</dc:creator>
      <pubDate>Sat, 24 Feb 2024 23:09:24 +0000</pubDate>
      <link>https://dev.to/ragnarkon/a-criticism-of-scrum-21al</link>
      <guid>https://dev.to/ragnarkon/a-criticism-of-scrum-21al</guid>
      <description>&lt;p&gt;Okay, look... scrum is just &lt;em&gt;terrible&lt;/em&gt;. It's frustrating, mind-numbing, and absolutely kills productivity.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, on paper scrum seems amazing collaboration framework. And maybe in an ideal world, it is amazing. But down on planet earth, I have yet to be apart of a team or company that actually saw a net-benefit from the scrum framework.&lt;/p&gt;

&lt;p&gt;This topic is something that I've been thinking about off-and-on for much of the past decade as agile frameworks have become more popular. And I debated posting this article knowing full well that scrum or scrum-like collaboration frameworks are used in MANY (probably most) software engineering teams nowadays. Surely it's all in my head, right? Maybe I'm just doing it wrong? Maybe I'm thinking about it wrong? Why would scrum be so popular if it didn't actually work?&lt;/p&gt;

&lt;p&gt;So to come clean, this really is not so much a criticism of the theory or formula of scrum, but instead more of a cry for help on how it's been implemented in practice, at least in my experience. I can't possibly be the only one struggling with this, can I?&lt;/p&gt;

&lt;p&gt;I guess we'll see. Let's dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sprint Towards Mediocrity
&lt;/h2&gt;

&lt;p&gt;Everyone (I assume) who has followed the scrum framework will be familiar with sprints. But in case you are not, sprints are designed to be a fixed duration of time to complete a bunch of tasks, usually 2 weeks long. The goal (I think) is to timebox tasks to a specific period of time to incentivize teams to keep tasks small and achievable. The hope is at the end of a sprint you end up with some sort of deliverable that can actually be demonstrated to others--something to show for your work.&lt;/p&gt;

&lt;p&gt;Prior to a sprint, most scrum teams will have a series of sprint planning and backlog refinement meetings where the team discusses the list of tasks (typically called "User Stories") that need to be completed during sprints. During that discussion, the team is asked to apply an arbitrary number value to that task, usually called "Story Points". Most teams, for whatever reason, use numbers from the Fibonacci sequence: &lt;code&gt;0, 1, 2, 3, 5, 8, 13, 21...&lt;/code&gt; with larger numbers applied to user stories that are more difficult, require more effort, or have a lot of uncertainty attached. Once user stories are assigned a numeric value, the team makes a determination of how many user stories can be completed within a sprint by looking at their average velocity from previous sprints (how many story points worth of work were completed previously), and their upcoming velocity (how many story points they expect to be able to complete). Cool.&lt;/p&gt;

&lt;p&gt;Except here is the kicker: This whole story points business is nonsense. It's like the TV show &lt;em&gt;Whose Line Is It Anyway?&lt;/em&gt;: The rules are made up and the points don't matter. It is extremely rare to complete the same task twice (and if you are, you should probably automate it). So really the question being asked is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hey can you assign an arbitrary point value to a task that you've never done before so we know roughly how long it will take and difficult it will be?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No, not really.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2F17i094yd02rnd155j0o6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="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%2F17i094yd02rnd155j0o6.jpg" alt="Points are Pointless" width="558" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what do engineers do in the face of such a question? Well, in my experience most will answer it in one of two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make up some absurdly high number to ensure they are confident they can complete it within a sprint. This usually triggers the typical 10-15 minute sidebar conversation where everyone on the team reiterates all the things we don't yet know. Seems like a good use of time.&lt;/li&gt;
&lt;li&gt;Give it a "normal" number so the Project Manager and/or Scrum Master just accepts it and moves on to something else without discussion.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Regardless, once the user stories are pointed and assigned out to engineers, the story points attached to those stories tend to be static. It is &lt;em&gt;extremely&lt;/em&gt; rare for a story to be re-pointed after it has been completed to reflect the true complexity. The tasks that were unexpectedly simple were over-estimated, while the tasks that ended up being unexpectedly difficult get taken out of the sprint and assigned to the new sprint. The end result? The team's overall velocity is incredibly inaccurate and not a reflection of the actual amount of work being done. Not only does this hurt the team's performance when reporting up to the people that actually care about that stuff, it also makes planning future sprints a fool's errand. You're assigning arbitrary units of work to tasks you know very little about, all while trying to keep it the overall workload under an imaginary capacity number that is estimated from previous velocity numbers that don't actually reflect the team's true output. Brilliant, just brilliant.&lt;/p&gt;

&lt;p&gt;But that's not even the worst of it. The biggest issue is the combination of sprints and story points breeds mediocrity within a team. No one likes to carry incomplete user stories between sprints. Some teams (or, more accurately, their managers) consider it a failure if user stories do not get completed within a sprint. This does nothing but incentivize scrum teams to over-point their user stories, and/or under-estimate their capacity. In an ideal world, if an engineer finishes with their user stories before the end of a sprint, they should simply pull the next user story off the top of the backlog. In practice, there is a &lt;em&gt;much&lt;/em&gt; larger incentive to work on a side project, or even just idly sit there twiddling their thumbs while waiting for the end of the sprint. After all... you wouldn't want to pump up your velocity numbers. That could cause you to over-commit in the future and burn yourself with a user story that ends up being unexpectedly difficult. Ugh.&lt;/p&gt;

&lt;p&gt;I'll freely admit I don't know a better way to estimate work load and work capacity. But what I do know is that, despite what all of the agile coaches will tell you, story points are really a way to measure how long a task will take. And if engineers were to answer that question honestly they would probably answer it in one of three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"It will take about 15 minutes"&lt;/li&gt;
&lt;li&gt;"It'll take longer than it should"&lt;/li&gt;
&lt;li&gt;"I have no clue how long it will take"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Facts. (As I see them, anyway.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Backlog Shoveling
&lt;/h2&gt;

&lt;p&gt;To your left is a large pile of unfinished "to-do" items, typically known as the backlog. In most scrum teams, this is the domain of the product owner or the project manager who is responsible for acting as the liaison between the engineering teams and the business stakeholders. Their job, roughly speaking, is to maximize the value the team delivers each sprint by ensuring the team is always working on the items that are most important to the stakeholders. They are effectively the person who determines the priority of the to-do list. At most companies, these individuals are typically less technical in nature but much more business-savvy. Which, if you've never seen a group of engineers attempt to communicate with business people, is definitely not a bad thing.&lt;/p&gt;

&lt;p&gt;The rub with this setup is engineers can't always be working on the items that provide direct benefit for the stakeholders. There are many necessary evils in IT that engineers must devote their time and energy to in order to keep the engine churning. It is the omni-present threat of crippling technical debt. Software currency is perhaps the easy example to point at when it comes to technical debt, but it comes in many forms. And it is extremely difficult for engineers to adequately communicate exactly how important it is to address technical debt. The conversations usually goes something like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Engineers&lt;/strong&gt;: "Hey this piece of software is end-of-life, and we really need to have time this sprint to update it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Product Owner&lt;/strong&gt;: &lt;em&gt;"Okay, what happens if we don't update it?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Egrs&lt;/strong&gt;: "Well... nothing right now. But it'll be a problem someday."*&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;PO&lt;/strong&gt;: &lt;em&gt;"When exactly is 'someday'?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Egrs&lt;/strong&gt;: "Hard to say, but it'll be a problem, and we need to address it."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;PO&lt;/strong&gt;: &lt;em&gt;"Will it be a problem in the next sprint or two?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Egrs&lt;/strong&gt;: "Probably not, but someday it'll bite us."&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;This conversation happens every. single. time. there is backlog refinement meeting on all of the user stories that don't clearly provide direct business value. The product owner, who has no idea when "someday" will occur, will naturally lean towards deprioritizing the technical debt user stories in favor of stories that provide more immediate value to the stakeholders &lt;em&gt;because it is their job to do so.&lt;/em&gt; At first the decision seems correct and all is fine &amp;amp; dandy... until one day it isn't. There will be a day when the volcano of technical debt explodes, your team is caught in the lava flow, and you're looking at a solid 6 months of slogging through that molted lava of debt. When it happens, and it will happen, there will be a predictable chorus of I-told-you-so's as everyone takes a step back to wonder why they are running production Ubuntu 14.04 servers in the year 2024 (true story).&lt;/p&gt;

&lt;p&gt;Now obviously this situation is a problem that many organizations encounter regardless of whether they are operating under the scrum framework or not. The temptation to ignore technical debt is real, and it is not a uniquely scrum problem. However, working under a scrum framework means you are continually refining the backlog, which in-turn makes it easier to encounter these issues. The two or three sprints you initially reserved for tackling technical debt tend to disappear as the associated user stories get refined to the bottom of the pile and completely forgotten about, never to see the light of day until the volcano explodes.&lt;/p&gt;

&lt;p&gt;It is not the fault of the product owner, scrum master, or the engineers that these issue pops up. It's ultimately a shared responsibility between all parties to ensure these sorts of issues do not happen. But for whatever reason it is a shared responsibility that seems to be exceedingly easy to ignore while operating under scrum, especially when your backlog reaches the size of "mountain".&lt;/p&gt;

&lt;h2&gt;
  
  
  Stand-ups... or stand-downs?
&lt;/h2&gt;

&lt;p&gt;Part of the scrum formula calls for daily scrums, which most people in my experience refer to as "stand-ups" regardless of whether you are standing or not. The scrum is facilitated by a scrum master to discuss the sprint's progress or discuss blockers that are preventing progress. In theory, this meeting is supposed to last less than 15 minutes, although in my personal experience it typically lasts around 30-40 minutes to give us ample time to once again talk about all the things we don't yet know. I'm guessing the goal of this meeting is to keep everyone engaged with each other's work, ensure progress is being made, and increase team synergy. But in practice it's just a means for the manager to complete his/her daily check-list so they can update superiors on the work being done. Most engineers don't actually listen to what their team members are doing, they just silently plug away at their tasks (or make themselves coffee) with their microphone muted until their name is called by the scrum master.&lt;/p&gt;

&lt;p&gt;Admittedly a "true" stand-up, in which everyone is actually standing in the same room, would largely resolve this problem. But in a world where hybrid and remote work is commonplace, we need something better than these mindless daily scrums. And if no alternative can be found, just let us place an update in the Teams/Slack channel so we can go about our day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not Another Agile Ceremony
&lt;/h2&gt;

&lt;p&gt;The biggest issue I have with scrum (and agile in general) is the sheer amount of meetings. Sprint plannings that can take hours, daily scrums that are rarely 15 minutes or less, post-sprint retrospectives that take hours and often turn into complaining festivals. And worst of all, the increment planning meetings—intended to allow organizations to plan out several months in advance—can &lt;em&gt;easily&lt;/em&gt; chew up several days and suck away your will to do anything remotely productive for an entire week. These meetings got so bad at a previous employer that my boss at the time coined the term "NAAC", short for "Not Another Agile Ceremony". &lt;em&gt;"Oh, sorry, can't talk right now I have to go to another NAAC."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It would be one thing if these meetings were productive. But when you can feel scrum isn't working... when you know your velocity numbers are nonsense, when you start throwing out random meaningless story points so the meeting ends quicker, when you discuss the same unresolved issues in the retrospectives sprint after sprint after sprint, it just becomes an utterly painful exercise. Judging by the level of interaction (or lack thereof), I would guess a good chunk of engineers out there have stopped expecting to gain value from these NAACs, and instead opt to silently sit in the meeting while doing something more productive, whether that be working on unfinished stories from previous sprints, or even finishing up their laundry.&lt;/p&gt;

&lt;p&gt;But don't worry! Bring in the agile coaches to tell us that we're doing it wrong (thanks captain obvious), and let's go to more NAACs to talk about how we're actually supposed to do scrum. Maybe, just maybe, when we've spent more time talking about how to organize the work than it actually takes to do the work, all will be fixed! The punishments will continue until morale improves!!&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Ft0phbnplpkl9ns3ze2cq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="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%2Ft0phbnplpkl9ns3ze2cq.jpg" alt="MORE MEETINGS" width="612" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Save me.)&lt;/p&gt;

&lt;h2&gt;
  
  
  But can it be saved?
&lt;/h2&gt;

&lt;p&gt;Can scrum work? Honestly, I don't know.&lt;/p&gt;

&lt;p&gt;Unless there is some grand conspiracy to reduce productivity industry wide, I imagine that scrum is truly working for a lot of people, and I've just been unlucky with the 5-6 teams that I've been apart over the past decade. Or maybe I'm just expecting too much from scrum. Or maybe we should be using SAFe instead. I honestly have no idea.&lt;/p&gt;

&lt;p&gt;The fundamental issue, I suspect, is switching from a more traditional waterfall-like workflow to one of the many agile workflows requires a fundamental change in how the business is structured and operates. On top of that, it only can truly work if the culture of the company itself changes. And honestly, that's &lt;strong&gt;hard&lt;/strong&gt; to do. These companies are big ships with a lot of built-up inertia. It requires a tremendous amount of leadership to change the direction of that ship. It's just straight-up a hard thing to do, and very few leaders are capable of making it happen. Most leaders seem to prefer the "agile-in-name-only" approach rather than truly putting in the work to fundamentally change the company.&lt;/p&gt;

&lt;p&gt;But regardless of the exact reason, my scrum master announced last week that the team will temporarily be changing to a Kanban workflow after an internal company reorganization, and I was more excited than I care to admit. Not that Kanban is some magical framework--many of the issues I've discussed with Scrum also apply to Kanban. But at least there is no longer a pressure to tightly squeeze everything into 2-week units of work. No more of taking square-peg-shaped units of work and shoving it into a round-hole-shaped sprints. And for that alone, I'm thankful.&lt;/p&gt;



</description>
      <category>scrum</category>
      <category>agile</category>
      <category>management</category>
    </item>
    <item>
      <title>Your own private cloud? A brief look at Antsle</title>
      <dc:creator>Bryan Woolsey</dc:creator>
      <pubDate>Sat, 24 Oct 2020 01:49:12 +0000</pubDate>
      <link>https://dev.to/ragnarkon/your-own-private-cloud-a-brief-look-at-antsle-122k</link>
      <guid>https://dev.to/ragnarkon/your-own-private-cloud-a-brief-look-at-antsle-122k</guid>
      <description>&lt;p&gt;If your browsing habits are anything like mine, you likely have seen online advertising from a company called &lt;a href="https://anstle.com" rel="noopener noreferrer"&gt;Antsle&lt;/a&gt;. But just in case you haven't, allow me to introduce you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://antsle.com" rel="noopener noreferrer"&gt;Antsle&lt;/a&gt; provides products to enable users to run their own "private cloud". Why exactly does that mean? Well... if you cut down to the meat of it, they sell hypervisor appliances running Antsle's web-based management software called antMan.&lt;/p&gt;

&lt;p&gt;I've been keeping my eye on the company and their offerings for a while now, wondering if I could effectively replace my Digital Ocean, GCP, and/or AWS accounts with a cheaper Antsle-based solution for my personal use. Up until now the cost has kept me away, but recently they released their newest appliance called Antsle Nano, a significantly cheaper solution based on the Raspberry Pi 4. So I pulled the trigger and spent the last two months or so playing around with my Antsle Nano to see what it's capable of.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardware
&lt;/h2&gt;

&lt;p&gt;Antsle sells three series of appliances on &lt;a href="https://antsle.com/antsle-appliances/" rel="noopener noreferrer"&gt;their website&lt;/a&gt;: Antsle one, Antsle two, and the new Antsle Nano.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fgp4zqujk77nde25wmupu.png" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fi%2Fgp4zqujk77nde25wmupu.png" alt="Antsle Hardware Lineup" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Antsle one&lt;/strong&gt; is the evolution of Antsle's original product line. They are effectively silent, fan-less servers in a small box that could easily fit on your desk/shelf. The hardware platform itself is based around the Intel Atom or Intel Xeon-D processor (depending on your selection), and is similar to the hardware found in many NAS devices sold by companies such as Synology. Prices range from $1000 up to $6500+ USD. While I haven't personally used an Antsle one device, the hardware choices available make sense for anyone looking to have a tiny, yet reasonably powerful and silent device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Antsle two&lt;/strong&gt; is Antsle's rack mount offering, and is clearly marketed towards business users rather than home users. Much to my surprise (and delight), Antsle two devices run on AMD's EPYC platform rather than the more widely seen Intel Xeon platforms. Given my focus to find a low-end home lab solution, buying an Antsle two device was never really a serious thought of mine. But for those curious, prices run from $3,500 to $50,000+ depending on the configuration.&lt;/p&gt;

&lt;p&gt;And finally... there is the &lt;strong&gt;Antsle Nano&lt;/strong&gt;.  As I mentioned earlier, the Antsle Nano is based on the Raspberry Pi 4. The prototypes shipped out to early adopters were based off the 4GB variant of the RPi 4, but it seems Antsle has decided to use the 8GB variant for the final version. Business users likely will want to stay far away from the Antsle Nano—but for home lab users such as myself... it is a very compelling device.  Antsle's version of the Raspberry Pi 4 ships with a beefy heat sink pre-installed, plus the typical USB-C wall-wart power supply and a 128GB MicroSD card preloaded with the antMan software. $150 is the cost of the device itself, but the software is unfortunately an added cost... more or that below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Software
&lt;/h2&gt;

&lt;p&gt;Ansle's control software is called antMan. antMan is a browser-based solution (with an accompanying API) that allows you as the user to control the underlying hypervisor. You can configure your networks and virtual machines—known as "antlets" in the Antsle world—from the antMan dashboard. The antMan user interface is relatively straight forward and well laid-out. After the first 5-10 minutes, I had no problems finding my way around to do what I needed to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fb444rvqyfksmfgzl9kjw.png" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fi%2Fb444rvqyfksmfgzl9kjw.png" alt="antMan User Interface" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently, there are five different pricing models for antMan, summarized below in my own cheeky manner. Feel free to review &lt;a href="https://antsle.com/pricing/#0" rel="noopener noreferrer"&gt;Antsle's pricing page&lt;/a&gt; for all the details.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Community Edition&lt;/strong&gt; - A free version of antMan limited to three small (think AWS free-tier) antlets. Can’t do much else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Home Lab&lt;/strong&gt; - Comes bundled with any Antsle one purchase. For reasons I do not fully understand, the Home Lab license does not come bundled with the Antsle Nano. Allows you to do most of the usual hypervisor tasks you’d expect, with some additional networking options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Essential&lt;/strong&gt; - $29/mo. The Home Lab capabilities with some added backup features, internal VLANs, and the ability to submit 3 support tickets a year. This license level is the minimum level available for the Antsle Nano.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grow&lt;/strong&gt; - $149/mo. Comes with even more backup functionality, network trunking, more support tickets, and the ability to cluster multiple Antsle units together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale&lt;/strong&gt; - $599/mo. Even more backup/migration functionality. Unlimited support tickets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The operating system behind the curtain
&lt;/h3&gt;

&lt;p&gt;antMan itself runs on top of Antsle's edgeLinux operation system. edgeLinux is essentially a customized version of CentOS 7 that comes packed with all of the usual open source hypervisor technologies that you'd expect to run LXC containers and KVM virtual machines. Docker also comes pre-installed, although no effort is made to expose Docker's functionality in the antMan user interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2F01xmv6jejnt5wvf6h0ff.png" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fi%2F01xmv6jejnt5wvf6h0ff.png" alt="edgeLinux logon prompt" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  antHill
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://anthill.antsle.com/" rel="noopener noreferrer"&gt;antHill&lt;/a&gt; is Antsle's cloud-based management portal.  Antsle will tell your that it is used to ease the initial setup and management of your Antsle unit(s).  But if you read between the lines a little bit... antHill seems to the mechanism Antsle uses to manage your antMan license.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fdc7w9lpjc0klb9ctdtjx.png" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fi%2Fdc7w9lpjc0klb9ctdtjx.png" alt="antHill" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Honestly, there isn't much to say about antHill beyond that. I used it once to activate my license and haven't used it since. Although I will note that it was a rather annoying to have to reconfigure my home network to allow my Antsle to talk to antHill long enough to activate my license.&lt;/p&gt;

&lt;h2&gt;
  
  
  So is it any good?
&lt;/h2&gt;

&lt;p&gt;Good question. For now, my answer is &lt;em&gt;"kinda/sorta... but not really"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you glance at Antsle's advertising, you might expect that you'll be purchasing a fully featured private cloud-in-a-box. I know when I first read about Antsle, I was envisioning a simple platform capable of creating S3-compatible object storage, a ready-to-go container/Kubernetes platform, an easy method to spin up common open source databases (MySQL, PostgreSQL, etc.), and perhaps even a functions-as-a-sevice mechanism similar to AWS Lambda or GCP Functions.&lt;/p&gt;

&lt;p&gt;What you get is small, compact Type 1 hypervisor with a web management portal. Based on my (admittedly limited) personal experience, the hardware itself seems solid, but not something that you couldn't purchase elsewhere from another source. And the software certainly leaves a lot to be desired. Unfortunately for Antlse, the main functionality that antMan provides can already be achieved for free from other hypervisors such as ESXi, Xen, etc.&lt;/p&gt;

&lt;p&gt;Honestly... I think there is a place in the market for Anstle’s products, but I’m struggling to clearly articulate where that place is. Solo, startup, and freelance developers looking for a public cloud replacement will likely be frustrated by antMan's limited feature set. Home lab users are likely to be better served using something like Proxmox. And business users likely have the budget to simply use a public cloud provider.&lt;/p&gt;

&lt;p&gt;Perhaps the biggest downside, at least for me, is that their most attractive device for people looking to build a home lab—the Antsle Nano—does not come bundled with their Home Lab license and instead requires subscription-based license. The discounted launch pricing on the antMan license is appreciated, but just knowing that I’ll have to pay another $348 in a year to keep my device functional is frustrating.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Foucs9pnqnhd6oe2tkraz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fi%2Foucs9pnqnhd6oe2tkraz.jpg" alt="Antsle Nano" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I do think there is potential for Antsle to improve. If Antsle was able to replicate more of the public cloud services in antMan, I think it could be a very compelling product overall, even with the subscription-based licensing. But as of right now, I find it hard to recommend. &lt;em&gt;That said&lt;/em&gt;, if you are just looking for a small, quiet, easy-to-setup device to run some virtual machines... look no further. Antsle's products will do just that.&lt;/p&gt;

&lt;p&gt;What do you think? Is Antsle worth it? Or would you stick with your public cloud account? Perhaps you are using another solution at home that I should look at? Let me know!&lt;/p&gt;

&lt;p&gt;p.s. Given my current antMan license is valid for another 10 months, let me know if you’d like more in-depth walkthrough.&lt;/p&gt;

</description>
      <category>antsle</category>
      <category>cloud</category>
      <category>homelab</category>
      <category>raspberrypi</category>
    </item>
    <item>
      <title>Dealing with long strings in YAML</title>
      <dc:creator>Bryan Woolsey</dc:creator>
      <pubDate>Fri, 23 Oct 2020 00:38:06 +0000</pubDate>
      <link>https://dev.to/ragnarkon/dealing-with-long-strings-in-yaml-kpc</link>
      <guid>https://dev.to/ragnarkon/dealing-with-long-strings-in-yaml-kpc</guid>
      <description>&lt;p&gt;We have a step in our Puppet CI/CD Pipeline that lints YAML data using &lt;a href="https://github.com/adrienverge/yamllint" rel="noopener noreferrer"&gt;adrienverge/yamllint&lt;/a&gt;. One of the rules limits the number of characters on a single line. Normally not a huge deal... except when dealing with long URIs or other long strings.&lt;/p&gt;

&lt;p&gt;The problem is, I have to change URI Hiera data so infrequently that I always forget exactly what the syntax is. Admittedly, this post is really for me to look back on and reference, but why not share?&lt;/p&gt;

&lt;p&gt;And while we're on the topic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://yaml.org/YAML_for_ruby.html" rel="noopener noreferrer"&gt;YAML Cookbook for Ruby&lt;/a&gt; is an awesome resource for YAML syntax in general.&lt;/li&gt;
&lt;li&gt;Puppet parses YAML using the following function in Ruby:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combine with Interactive Ruby Shell or &lt;code&gt;ruby -e&lt;/code&gt; for a quick and easy way to test any YAML file you create.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plain Style
&lt;/h2&gt;

&lt;h3&gt;
  
  
  "Flow" Styles
&lt;/h3&gt;

&lt;p&gt;No character escaping, or characters matching &lt;code&gt;/[#:]/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;This is a very&lt;/span&gt;
     &lt;span class="s"&gt;long string.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"This is a very long string."&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Single-quoted style
&lt;/h3&gt;

&lt;p&gt;No special characters, no escaping. Literal &lt;code&gt;'&lt;/code&gt; must be doubled-up (&lt;code&gt;''&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;This&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;isn'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t&lt;/span&gt;
     &lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;very&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;short&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;string.'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"This isn't a very short string."&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Double-quoted style
&lt;/h3&gt;

&lt;p&gt;Characters matching &lt;code&gt;/[\\"]/&lt;/code&gt; must be escaped by a &lt;code&gt;\&lt;/code&gt; character. Common escape sequences may be used. Line concatenation with a trailing &lt;code&gt;\&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;VERY&lt;/em&gt; useful for long URIs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://this.is.my&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;     &lt;span class="s"&gt;.very.long.string"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"http://this.is.my.very.long.string"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Block Notation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Literal Style
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;This is a very&lt;/span&gt;
  &lt;span class="s"&gt;long string.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"This is a very&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;long string.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Folded Style
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;This is a very&lt;/span&gt;
  &lt;span class="s"&gt;long string.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"This is a very long string.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Block chomping indicator
&lt;/h3&gt;

&lt;p&gt;You may notice that both strings have newlines attached to the end. Want those gone? Use a different chomping indicator:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;: "clip". Keeps the newline.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;|+&lt;/code&gt;, &lt;code&gt;&amp;gt;+&lt;/code&gt;: "keep". Keeps the newline, and also keeps tailing blank lines.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;|-&lt;/code&gt;, &lt;code&gt;&amp;gt;-&lt;/code&gt;: "strip". Removes the newline.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;key0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;Do. Or do not.&lt;/span&gt;
  &lt;span class="s"&gt;There is no try.&lt;/span&gt;

&lt;span class="na"&gt;key1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;+&lt;/span&gt;
  &lt;span class="s"&gt;I find your lack of&lt;/span&gt;
  &lt;span class="s"&gt;faith disturbing.&lt;/span&gt;

&lt;span class="na"&gt;key2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
  &lt;span class="s"&gt;The Force will be&lt;/span&gt;
  &lt;span class="s"&gt;with you. Always.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"key0"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Do. Or do not. There is no try.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"key1"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"I find your lack of faith disturbing.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"key2"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"The Force will be with you. Always."&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://yaml.org/spec/1.2/spec.html" rel="noopener noreferrer"&gt;YAML v1.2 Specification&lt;/a&gt;&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>yaml</category>
      <category>hiera</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Avoiding Resource Conflicts in Puppet</title>
      <dc:creator>Bryan Woolsey</dc:creator>
      <pubDate>Fri, 21 Aug 2020 03:51:10 +0000</pubDate>
      <link>https://dev.to/ragnarkon/avoiding-resource-conflicts-2lik</link>
      <guid>https://dev.to/ragnarkon/avoiding-resource-conflicts-2lik</guid>
      <description>&lt;p&gt;One of the most common questions I get from coworkers new to Puppet is "how can I avoid resource conflicts?".&lt;/p&gt;

&lt;p&gt;The typical resource conflict I run across is usually caused by multiple modules attempting to manage a common folder or package dependency. (&lt;code&gt;C:\temp&lt;/code&gt; in particular seems to be a big hit for some reason.) If multiple modules manage the same resource, Puppet will throw a duplicate resource declaration error.&lt;/p&gt;

&lt;p&gt;In an ideal world of &lt;a href="https://puppet.com/docs/pe/2019.0/designing_system_configs_roles_and_profiles.html" rel="noopener noreferrer"&gt;roles and profiles&lt;/a&gt; and perfectly coded modules, resource conflicts should never occur. But, given that we don't live in an ideal world, there are a few helpful functions to avoid the issue.&lt;/p&gt;

&lt;p&gt;Before we move on, It is important to note that the functions listed below are still impacted by evaluation order (explained in greater detail later). These functions are not magic "fix all" functions, and should be avoided if at all possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;defined&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://puppet.com/docs/puppet/6.1/function.html#defined" rel="noopener noreferrer"&gt;defined&lt;/a&gt; function is built into Puppet and will return &lt;code&gt;true&lt;/code&gt; if a given resource is defined within the catalog. The function may be used within a conditional to check if a particular resource exists (yet).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nf"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# do stuff ...
&lt;/span&gt;  &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'directory'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;defined_with_params&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This function (and every function hereafter) is provided by the &lt;a href="https://forge.puppet.com/puppetlabs/stdlib" rel="noopener noreferrer"&gt;puppetlabs/stdlib&lt;/a&gt; Forge module. As the name suggests, this function checks for a resource with specific attributes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;defined_with_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'johndoe'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'ensure'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'present'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# do stuff ...
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'johndoe'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;absent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;a href="https://forge.puppet.com/puppetlabs/stdlib#defined_with_params" rel="noopener noreferrer"&gt;puppetlabs/stdlib README&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ensure_resource&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This function is pure gold. Given a resource type, title, and attribute hash, this function will create a resource in the catalog only if it does not already exist. If the resource does exist, but has different attributes, a duplicate resource definition error is thrown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="nf"&gt;ensure_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'file'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'ensure'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'directory'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use pass in arrays to create multiple resources with a single function call. Check out the &lt;a href="https://forge.puppet.com/puppetlabs/stdlib#ensure_resource" rel="noopener noreferrer"&gt;README&lt;/a&gt; for more info.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ensure_resources&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Essentially a souped-up version of the &lt;code&gt;ensure_resource&lt;/code&gt; function that allows you to pass in a hash of resources. In my opinion, this function quickly makes your code unreadable, and I tend to avoid it. However, it definitely has its place, particularly when describing resources within Hiera.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="nf"&gt;ensure_resources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cron'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'backupjob'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'command'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/usr/bin/backup'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'user'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'hour'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;'copyjob'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'command'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/usr/sbin/copystuff'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'minute'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'ensure'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'present'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;ensure_packages&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Similar to the previous function, but specifically for &lt;code&gt;package&lt;/code&gt; resources. I use it for dependencies when a node's package manager cannot sort it out on its own (namely Windows).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="nv"&gt;$my_pkgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'libcurl3'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'ensure'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'present'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s1"&gt;'zabbix'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'ensure'&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'3.4.0'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;ensure_packages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$my_pkgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Impact of evaluation order
&lt;/h2&gt;

&lt;p&gt;How and when resources &amp;amp; functions are evaluated during catalog compilation may impact the result of these functions. Puppet does not always evaluate classes and resources in the order in which they are defined within a manifest. This evaluation-order independence has the potential to change the outcome of these functions and may make it seem like they are... well... broken.&lt;/p&gt;

&lt;p&gt;Exactly how catalog compilation works gets into the nitty gritty of Puppet, and I won't pretend to completely understand how it works. All I can do is attempt to explain the issue as I understand it. (Feel free to yell at me if I get anything wrong.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# Evaluated first
&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'directory'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Evaluated second
&lt;/span&gt;&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nf"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'directory'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# This catalog will compile.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, the catalog will compile successfully, as the &lt;code&gt;file&lt;/code&gt; resource is evaluated before the &lt;code&gt;ensure_resource&lt;/code&gt; function. However, if the evaluation order is flipped, the catalog will fail to compile, as the &lt;code&gt;ensure_resource&lt;/code&gt; function already added the &lt;code&gt;File['/tmp/shared']&lt;/code&gt; resource to the catalog before the &lt;code&gt;file&lt;/code&gt; resource declaration is evaluated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# Evaluated first
&lt;/span&gt;&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nf"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'directory'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Evaluated second
&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/shared'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'directory'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# This catalog will fail
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Evaluation order can quickly become an issue when the same resources are added to the catalog in two completely different, unrelated classes. This issue can largely be avoided by using these handy functions in all classes that define the resource. That said, use some restriant to ensure these functions aren't used everywhere. If you have widespread use of these functions, chances are you aren't structuring your code base correctly.&lt;/p&gt;

</description>
      <category>puppet</category>
    </item>
    <item>
      <title>The better way to access Puppet facts</title>
      <dc:creator>Bryan Woolsey</dc:creator>
      <pubDate>Fri, 21 Aug 2020 03:51:01 +0000</pubDate>
      <link>https://dev.to/ragnarkon/the-better-way-to-access-puppet-facts-391n</link>
      <guid>https://dev.to/ragnarkon/the-better-way-to-access-puppet-facts-391n</guid>
      <description>&lt;p&gt;I spend a &lt;em&gt;lot&lt;/em&gt; of time working with facts in Puppet. I've probably spent more time figuring out the best way to manage custom external facts unique to our environment (node lifecycle status, datacenter, etc.) this past year than anything else in the Puppet universe. So I was rather surprised to find out there are better ways to access node facts within manfiests than using the standard &lt;code&gt;$facts&lt;/code&gt; hash.&lt;/p&gt;

&lt;p&gt;Most of the information that follows came from a recent &lt;a href="https://puppet.com/community/office-hours" rel="noopener noreferrer"&gt;office-hours&lt;/a&gt; conversation on the Puppet Community Slack. If you haven't already, I highly suggest you check out the Puppet Community Slack channel. It is a great place to bounce ideas off of Puppet engineers and other members of the community.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fact variables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Classic top-scope variables
&lt;/h3&gt;

&lt;p&gt;Node facts have always been accessible in Puppet using top scope variables. For example, the fact &lt;code&gt;kernel&lt;/code&gt; is automatically available within a manifests in the variable &lt;code&gt;$kernel&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;$kernel&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Linux'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# do stuff...
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downside of using variables is that it isn't immediately obvious you are using a node fact in your manifest. May not be an issue for common well-known facts, but for custom facts it'll cause your manifest to be difficult to read. Many developers will explicitly call a top-scope variable (ie. &lt;code&gt;$::kernel&lt;/code&gt;) as a hint, but it still doesn't completely resolve the issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;$facts&lt;/code&gt; hash
&lt;/h3&gt;

&lt;p&gt;In Puppet 3.5 (I think), PuppetLabs added the &lt;code&gt;$facts&lt;/code&gt; hash. Node facts are merged together into a single hash, with may be accessed using the fact name as the key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nv"&gt;$facts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'kernel'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Linux'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# do non-Linux stuff...
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how I've always accessed facts within my manifests, and so far I haven't had any trouble. &lt;em&gt;However,&lt;/em&gt; while this approach works for most facts, it does not work well for structured facts.&lt;/p&gt;

&lt;p&gt;Take a node that has the following custom structured fact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cmdb_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"datacenter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"virginia"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I wanted to retrieve the node's datacenter within my manifest, I could simply use the variable &lt;code&gt;$facts['cmdb_data']['datacenter']&lt;/code&gt;. On nodes that do &lt;em&gt;not&lt;/em&gt; have the custom structured fact (a newly provisioned node, for example), I had assumed the variable would simply have the value &lt;code&gt;undef&lt;/code&gt;. Turns out this is not the case, in reality the catalog will blow up and fail to compile.&lt;/p&gt;

&lt;h2&gt;
  
  
  The functions that help
&lt;/h2&gt;

&lt;p&gt;Luckily there are a few functions available to help you access structured facts.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;dig&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Similar to Ruby's built-in &lt;code&gt;dig&lt;/code&gt; function, the Puppet variant allows you to "dig" into complex data structures. Refer to &lt;a href="https://puppet.com/docs/puppet/6.2/function.html#dig" rel="noopener noreferrer"&gt;Puppet's function documentation&lt;/a&gt; for more information.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"node_meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"johndoe"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"datacenter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"virginia"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="nv"&gt;$facts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'node_meta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'owner'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# returns: 'johndoe'
&lt;/span&gt;&lt;span class="nv"&gt;$facts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'node_meta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'enclosure'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'rack'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# returns: undef
&lt;/span&gt;
&lt;span class="nv"&gt;$facts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'node_meta'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'enclosure'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'rack'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# fails with catalog compilation error
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;fact&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A relatively new entry to the &lt;a href="https://forge.puppet.com/puppetlabs/stdlib#fact" rel="noopener noreferrer"&gt;puppetlabs/stdlib forge module&lt;/a&gt;, the &lt;code&gt;fact&lt;/code&gt; function behaves similarly to the &lt;code&gt;dig&lt;/code&gt; function, but is explicitly used for facts. The function also supports dot notation, making it relatively compact and easy to read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="nv"&gt;$server_owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'node_meta.owner.name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;get&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;get&lt;/code&gt; is a new function added in Puppet 6. It provides generally the same functionality as &lt;code&gt;dig&lt;/code&gt; and &lt;code&gt;fact&lt;/code&gt;, but has the ability to return a default value if the value isn't present in the data structure. Like the &lt;code&gt;fact&lt;/code&gt; function, this function uses dot notation for navigation values.&lt;/p&gt;

&lt;p&gt;As an aside, for those who are huge fans of stdlib's &lt;code&gt;pick&lt;/code&gt; function, the &lt;code&gt;get&lt;/code&gt; function has the potential to replace &lt;code&gt;pick&lt;/code&gt; in many situations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="nv"&gt;$facts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'node_meta.owner.name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'no_owner_information'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;$server_owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$facts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'node_meta.owner.name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'no_owner_information'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;get&lt;/code&gt; function also has an optional lambda for more advanced error handing should the value not exist. More information is available in &lt;a href="https://puppet.com/docs/puppet/6.2/function.html#get" rel="noopener noreferrer"&gt;Puppet's function&lt;br&gt;
documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You should probably be using the &lt;code&gt;dig&lt;/code&gt;, &lt;code&gt;fact&lt;/code&gt;, or &lt;code&gt;get&lt;/code&gt; functions to retrieve fact values rather than top-scope variables or the &lt;code&gt;$facts&lt;/code&gt; hash.&lt;/p&gt;

&lt;p&gt;If you are on Puppet 6, the &lt;code&gt;get&lt;/code&gt; function is a no-brainer in my opinion. If you can't make the jump to Puppet 6, my vote would be the &lt;code&gt;fact&lt;/code&gt; function available in puppetlabs/stdlib.&lt;br&gt;
&lt;code&gt;dig&lt;/code&gt; is my least favorite of the three (not a fan of the array notation), but it will still get the job done.&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>facter</category>
    </item>
  </channel>
</rss>
