<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>albatrossflavour.com</title>
		<description>of course you don't get wafers with it</description>
		<link></link>
		<atom:link href="/feed.xml" rel="self" type="application/rss+xml" />
		
			<item>
				<title>Using Let's Encrypt with the Puppet Enterprise console</title>
				<description>&lt;p&gt;Had an itch I’ve been meaning to scratch for a while.  I build my &lt;a href=&quot;https://puppet.com&quot;&gt;Puppet&lt;/a&gt; environment using &lt;a href=&quot;https://terraform.io&quot;&gt;Terraform&lt;/a&gt;, which makes it nice and easy to tear things down and rebuild them.  That is great, but it does leave me with an issue when it comes to the console SSL certificates.&lt;/p&gt;

&lt;p&gt;Puppet will generate self-signed certs for the console, which work &lt;em&gt;fine&lt;/em&gt;, but it was always a niggle that the certs couldn’t be automagically coaxed into being valid.&lt;/p&gt;

&lt;p&gt;Since moving over to &lt;a href=&quot;https://github.com/albatrossflavour/k3s-cluster&quot;&gt;kubernetes&lt;/a&gt; for my home lab, I’ve come to expect managed SSL certificates for any public facing services, without me having to do anything.&lt;/p&gt;

&lt;p&gt;Finally set some time aside to look at the options and thought I’d publish the details of the journey as well as where I ended up.&lt;/p&gt;

&lt;h1 id=&quot;step-1---lets-encrypt&quot;&gt;Step 1 - Let’s Encrypt&lt;/h1&gt;

&lt;p&gt;Obviously I wasn’t going to reinvent the wheel.  If I wanted to manage and use Let’s Encrypt on a Puppet Enterprise server, I’d be using the &lt;a href=&quot;https://forge.puppet.com/modules/puppet/letsencrypt&quot;&gt;Let’s Encrypt module&lt;/a&gt;.  The module makes it very simple to get the relevant packages installed and configured.&lt;/p&gt;

&lt;div class=&quot;language-puppet highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'letsencrypt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;config&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;email&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'certs@albatrossflavour.com'&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;py&quot;&gt;config_dir&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/etc/letsencrypt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One of the beauties of this module is that it also sets up a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cron&lt;/code&gt; job to renew the generated certs, so you don’t need to keep an eye on it.&lt;/p&gt;

&lt;p&gt;Classify your server with that and you’ll end up with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;certbot&lt;/code&gt; and it’s dependencies.  Great start and feeling confident about the future!&lt;/p&gt;

&lt;p&gt;Then we need to generate a certificates:&lt;/p&gt;

&lt;div class=&quot;language-puppet highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;letsencrypt::certonly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'puppet.gcp.albatrossflavour.com'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;domains&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'puppet.gcp.albatrossflavour.com'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;manage_cron&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;py&quot;&gt;plugin&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'webroot'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;webroot_paths&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;www&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Annnnnnnd that’s where things start to go south.&lt;/p&gt;

&lt;p&gt;When you request a cert, the most simple method of validating you are who you say you are is to have a web server on the host respond to a query sent to port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;80&lt;/code&gt;.  Sure we say, easy, couldn’t take much to do!&lt;/p&gt;

&lt;p&gt;By default, the Puppet Enterprise &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; config does a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;301&lt;/code&gt; redirect for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt; requests to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https&lt;/code&gt;.  This means any queries from Let’s Encrypt to validate the requests will end up as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https&lt;/code&gt; requests and the cert request will fail.&lt;/p&gt;

&lt;h1 id=&quot;step-2---nginx&quot;&gt;Step 2 - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt;&lt;/h1&gt;

&lt;p&gt;Let me introduce you to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet_enterprise::profile::console::proxy::http_redirect::enable_http_redirect&lt;/code&gt;.  This parameter controls if that redirect is in place.  So step …. 6(?) was to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hiera&lt;/code&gt; to disable the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt; redirect:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;s&quot;&gt;❯ cat data/role/role::pe::master.yaml&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;puppet_enterprise::profile::console::proxy::http_redirect::enable_http_redirect: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once we run this through a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet&lt;/code&gt; run, the redirect gets removed!  Score!&lt;/p&gt;

&lt;p&gt;Only problem is, without the redirect, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; server doesn’t listen on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;80&lt;/code&gt;.  OK, we can fix that easily.  We could use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pe_nginx::directive&lt;/code&gt; type, but I found it to be a bit of an overkill for what I needed.  Instead I opted for a simple template:&lt;/p&gt;

&lt;div class=&quot;language-puppet highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/etc/puppetlabs/nginx/conf.d/certs.conf'&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;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;owner&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;group&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;mode&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0644'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;${module_name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/cert_vhost.conf.erb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;notify&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'pe_nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and the template is just:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;server {
  listen       80;
  server_name  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@fqdn&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;;
  index        index.html;
  location /.well-known {
    root /var/www;
  }
  location / {
    return 301 https://$server_name$request_uri;
  }
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Sorry, still using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;erb&lt;/code&gt;, I will &lt;strong&gt;at some point&lt;/strong&gt; rewrite everthing in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;epp&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;The template keeps the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;301&lt;/code&gt; redirect in place for anything &lt;em&gt;other&lt;/em&gt; than a request to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/.well-known&lt;/code&gt;, which is where Let’s Encrypt looks for the validation info.&lt;/p&gt;

&lt;p&gt;Run this through and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; vhost gets created.  However the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;letsencrypt::certonly&lt;/code&gt; call still fails on the first run.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notify&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pe_nginx&lt;/code&gt; service, which is done when we create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cert_vhost.conf&lt;/code&gt;, doesn’t happen in the order we need.  The Let’s Encrypt module is trying to get a response &lt;em&gt;before&lt;/em&gt; we’ve setup the vhost.  Now this would work on subsequent runs, but &lt;strong&gt;Golden Rule #1&lt;/strong&gt; is to make sure, whenever possible, that you get a clean puppet run in one pass.  Plus this was a challenge.&lt;/p&gt;

&lt;p&gt;Before I worked on fixing that, I wanted to make sure I could make the rest of it work.  Let’s sum up where we are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’ve got the Let’s Encrypt client installed and configured&lt;/li&gt;
  &lt;li&gt;I’ve got a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; vhost running that allows the Let’s Encrypt web validation queries through and redirects any other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt; traffic.&lt;/li&gt;
  &lt;li&gt;After a couple of puppet runs, I’ve got valid SSL certs in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/letsencrypt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;step-3---the-console-certs&quot;&gt;Step 3 - The console certs&lt;/h1&gt;

&lt;p&gt;It’d been a while since I played with the Puppet console SSL certs, so I checked in with &lt;a href=&quot;https://www.puppet.com/docs/pe/latest/use_a_custom_ssl_cert_for_the_console.html&quot;&gt;the source of truth&lt;/a&gt;, which outlines the steps we need to go through to use custom SSL certs with the console:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Retrieve the custom certificate and private key.&lt;/li&gt;
  &lt;li&gt;Move the certificate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/puppetlabs/puppet/ssl/certs/console-cert.pem&lt;/code&gt;, replacing any existing file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console-cert.pem&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Move the private key to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/puppetlabs/puppet/ssl/private_keys/console-cert.pem&lt;/code&gt;, replacing any existing file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console-cert.pem&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So we can just create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file&lt;/code&gt; resource that takes the Let’s Encrypt cert/key and places them into the console SSL directory structure.&lt;/p&gt;

&lt;div class=&quot;language-puppet highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/etc/puppetlabs/puppet/ssl/certs/console-cert.pem'&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;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;owner&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'pe-puppet'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;group&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'pe-puppet'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;links&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'follow'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;mode&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0640'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;source&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/letsencrypt/live/&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{facts['puppet_server']}/cert.pem&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;backup&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.puppet_bak'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;notify&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;'pe-nginx'&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;Letsencrypt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Certonly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$facts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'puppet_server'&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;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/etc/puppetlabs/puppet/ssl/private_keys/console-cert.pem'&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;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;owner&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'pe-puppet'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;group&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'pe-puppet'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;links&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'follow'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;mode&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0644'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;source&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/letsencrypt/live/&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{facts['puppet_server']}/privkey.pem&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;backup&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.puppet_bak'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;notify&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;'pe-nginx'&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;Letsencrypt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Certonly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$facts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'puppet_server'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sure enough, when I try this on the Puppet server, life is good and we have a console with valid SSL certs.&lt;/p&gt;

&lt;p&gt;Time to crack open a bottle of red and tick an item off my to do list.&lt;/p&gt;

&lt;p&gt;Damn it, &lt;strong&gt;Golden Rule #2&lt;/strong&gt; raises it’s head.  It’s not finished until you know it works fine from scratch… &lt;strong&gt;&lt;em&gt;WITHOUT&lt;/em&gt;&lt;/strong&gt; breaking &lt;strong&gt;Golden Rule #1&lt;/strong&gt;.&lt;/p&gt;

&lt;h1 id=&quot;step-4---rebuild&quot;&gt;Step 4 - Rebuild&lt;/h1&gt;

&lt;p&gt;Nuked the Puppet server and rebuilt it.  Lots of failures on the first run (we kinda expected that), however they didn’t go away.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; service never gets restarted as the various dependencies can be resolved, but not in the way we need.&lt;/p&gt;

&lt;p&gt;This is the &lt;em&gt;fun&lt;/em&gt; (?) of declarative configuration management.  I can only &lt;strong&gt;&lt;em&gt;manage&lt;/em&gt;&lt;/strong&gt; things once, which includes only being able to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notify&lt;/code&gt; the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pe-nginx&lt;/code&gt; service once.  Then the compiler will figure out when the service is restarted, based on all of the dependencies in the catalog.&lt;/p&gt;

&lt;p&gt;I played around with a lot of options, some &lt;em&gt;waaaaay&lt;/em&gt; hackier than I wanted to go with.  Plus this was becoming a fun exercise.&lt;/p&gt;

&lt;p&gt;All I needed to be able to do was to inject a restart of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pe-nginx&lt;/code&gt; service twice in a single run.&lt;/p&gt;

&lt;p&gt;To get there, I bent a few of the future &lt;strong&gt;Golden Rules&lt;/strong&gt; and came up with:&lt;/p&gt;

&lt;div class=&quot;language-puppet highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'restart_nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;command&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/bin/systemctl restart pe-nginx'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;refreshonly&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;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; resource will allow me to do a restart of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pe-nginx&lt;/code&gt; outside of, and before, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notify =&amp;gt; Service['pe-nginx']&lt;/code&gt; parameters.&lt;/p&gt;

&lt;p&gt;Yes, it’s a hack, but it’s a hack on the side of the angels.  Not only will it solve the issue of the process not working at all, but it will also allow the certs to be generated, and installed, in a single run.&lt;/p&gt;

&lt;h1 id=&quot;step-5---quick-robin-to-the-pdk-mobile&quot;&gt;Step 5 - Quick Robin, to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdk&lt;/code&gt; mobile&lt;/h1&gt;

&lt;p&gt;I did a fair bit of testing and found the end result to be far more reliable and useful than I thought it would be.&lt;/p&gt;

&lt;p&gt;I first created a fully parameterised profile to manage the certs.  Once I got that working, it was a no-brainer to create a module to share the love.&lt;/p&gt;

&lt;p&gt;That’s how I ended up with the &lt;a href=&quot;https://forge.puppet.com/modules/albatrossflavour/pe_console_letsencrypt/readme&quot;&gt;pe_console_letsencrypt&lt;/a&gt; module.  You can also just &lt;a href=&quot;https://github.com/albatrossflavour/pe_console_letsencrypt/blob/main/manifests/init.pp&quot;&gt;check out the code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve done a fair bit of testing, but I’m certainly not saying it’s bulletproof.&lt;/p&gt;

&lt;h1 id=&quot;step-6---profit&quot;&gt;Step 6 - &lt;a href=&quot;https://en.wikipedia.org/wiki/Gnomes_(South_Park)&quot;&gt;Profit&lt;/a&gt;?&lt;/h1&gt;

&lt;p&gt;I’m still scratching my head to think of another way I could get the certs generated and in place in a single run &lt;strong&gt;without&lt;/strong&gt; the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; hack.  That being said, I’ve had hacks a lot worse go live in the past!&lt;/p&gt;

&lt;h1 id=&quot;note&quot;&gt;Note&lt;/h1&gt;

&lt;p&gt;While researching the links for this post, I came across a &lt;a href=&quot;http://blog.graypockets.com/2017/10/lets-encrypt-puppet-enterprise-console.html&quot;&gt;blog less frequently updated than mine&lt;/a&gt; which has a post from 2017 showing a similar way of achieving the same thing, but without the hack to make it work in a single run.&lt;/p&gt;
</description>
				<pubDate>Thu, 30 Mar 2023 00:00:00 +0000</pubDate>
				<link>/2023/03/using-lets-encrypt-with-the-puppet-enterprise-console/</link>
				<guid isPermaLink="true">/2023/03/using-lets-encrypt-with-the-puppet-enterprise-console/</guid>
			</item>
		
			<item>
				<title>os_patching in the news</title>
				<description>&lt;p&gt;I’ve been working on an article about my &lt;a href=&quot;https://forge.puppet.com/albatrossflavour/os_patching&quot;&gt;puppet os_patching module&lt;/a&gt; for a while.  I finally finished it a couple of weeks ago and it’s been picked up by &lt;a href=&quot;https://jaxenter.com/patching-linux-windows-159518.html&quot;&gt;https://jaxenter.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It feels great to be able to write and talk about the things I’m working on.  That’s one of the biggest changes since leaving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$old-job&lt;/code&gt;.&lt;/p&gt;
</description>
				<pubDate>Tue, 25 Jun 2019 00:00:00 +0000</pubDate>
				<link>/2019/06/os_patching-in-the-news/</link>
				<guid isPermaLink="true">/2019/06/os_patching-in-the-news/</guid>
			</item>
		
			<item>
				<title>Moving to github pages</title>
				<description>&lt;p&gt;I’ve moved the site over to &lt;a href=&quot;https://pages.github.com&quot;&gt;Github pages&lt;/a&gt;, it’s early days but I’m really enjoying being able to manage the whole site as a plain text repo.&lt;/p&gt;

&lt;p&gt;Everything had been hosted on Squarespace for years.  While the platform is great, it offered a lot of power where I didn’t need it and didn’t offer the features I did want.&lt;/p&gt;

&lt;p&gt;Since I’m trying to resurrect the site, I thought it’d be good to be able to have a base platform that I feel happy with and one that doesn’t lock me into a hosting provider.  Github pages offers integration with &lt;a href=&quot;https://jekyllrb.com&quot;&gt;Jeykll&lt;/a&gt; which has made my migration so much easier.  It also allows for the use of &lt;a href=&quot;https://help.github.com/en/articles/using-a-custom-domain-with-github-pages&quot;&gt;custom domains&lt;/a&gt; rather than using the $yourname.github.io.&lt;/p&gt;

&lt;p&gt;Getting the old site redirects into place took a bit of effort and I’m not 100% sure I’ve nailed all of them, but I’m pretty happy with how things are going so far.  I’ve played around with customising the &lt;a href=&quot;https://github.com/pages-themes/hacker&quot;&gt;theme&lt;/a&gt;, it’s not where I want it to be yet, but it feels a lot more like what I want already.&lt;/p&gt;
</description>
				<pubDate>Tue, 28 May 2019 00:00:00 +0000</pubDate>
				<link>/2019/05/github-pages/</link>
				<guid isPermaLink="true">/2019/05/github-pages/</guid>
			</item>
		
			<item>
				<title>Using Terraform and Puppet Bolt together ... Terrabolt?  Boltform?</title>
				<description>&lt;p&gt;I was playing around with Terraform and GCP the other day to run up a couple of different environments.  It niggled me that I needed to hack around to get my instances working with Puppet, sure it was only a provisioning script, but it still lacked elegance.&lt;/p&gt;

&lt;p&gt;A bit of google-fu led me to &lt;a href=&quot;http://blog.lucywyman.me/cloud-provisioning-with-terraform-and-bolt.html&quot;&gt;Lucy Wyman’s awesome post about combining Bolt and Terraform&lt;/a&gt;.  I spent a while diving into how Lucy had set things up and I was really impressed, it was fairly easy to adapt that workflow to what I was looking for.&lt;/p&gt;

&lt;p&gt;A few days of effort later and I had a single bolt plan which would call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform&lt;/code&gt; to provision the infrastructure and then use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bolt&lt;/code&gt; to apply all of the configuration.  It was neat, but a little loquacious when it came to adding nodes to the inventory.&lt;/p&gt;

&lt;p&gt;Lucy’s (and the one I used too) used the following code to build the inventories.&lt;/p&gt;

&lt;h3 id=&quot;plansbuild_pepp&quot;&gt;plans/build_pe.pp&lt;/h3&gt;

&lt;div class=&quot;language-puppet highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cd &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$tf_path&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;} &amp;amp;&amp;amp; terraform apply&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$localhost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ip_string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cd &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$tf_path&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;} &amp;amp;&amp;amp; terraform output public_ips&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$localhost&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;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$r&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;$r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'stdout'&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;$ips&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ip_string&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;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ip&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&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Turn IPs into Bolt targets, and add to inventory
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$targets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ips&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ip&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;nc&quot;&gt;Target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&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;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ip&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_to_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'terraform'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;inventoryyaml&quot;&gt;inventory.yaml&lt;/h3&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;groups&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;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;terraform&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;nodes&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;c1&quot;&gt;# This will be populated by the Bolt plan&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;transport&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;private-key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/.ssh/id_rsa-phraseless&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;host-key-check&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not a huge problem to iterate over that for different inventory groups, but the lack of elegance still bothered me, but it was a great solution and the best that could be done.  Sorry, that should be “and &lt;strong&gt;&lt;em&gt;was&lt;/em&gt;&lt;/strong&gt; the best that could be done”.&lt;/p&gt;

&lt;p&gt;On the 17th May &lt;a href=&quot;https://puppet.com/docs/bolt/latest/bolt_release_notes.html&quot;&gt;Bolt V0.20.0&lt;/a&gt; was released.  The feature that really caught my eye was :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A new plugin in inventory v2 loads Terraform state and map resource properties to target parameters. This plugin enables using a Terraform project to dynamically determine the targets to use when running Bolt&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The inventory file changes were straight forward and made life far easier going forward.&lt;/p&gt;

&lt;p&gt;I decided to try setting up a moderately complex set of infrastructure which could be setup with as few moving parts as possible.  My idea was to do a full Puppet Enterprise deployment, including :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Puppet Enterprise Master&lt;/li&gt;
  &lt;li&gt;PE HA replica&lt;/li&gt;
  &lt;li&gt;Compilers behind a load balancer&lt;/li&gt;
  &lt;li&gt;Continuous Delivery for Puppet Enterprise (CD4PE)&lt;/li&gt;
  &lt;li&gt;Workers for the CD4PE&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The terraform side looks like this :&lt;/p&gt;

&lt;h3 id=&quot;petf&quot;&gt;pe.tf&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;resource &quot;google_dns_managed_zone&quot; &quot;frontend&quot; {
  project    = &quot;puppet&quot;
  dns_name   = &quot;gcp.example.com.&quot;
  name       = &quot;gcp-example&quot;
  visibility = &quot;public&quot;
}

resource &quot;google_dns_record_set&quot; &quot;frontend&quot; {
  project      = &quot;puppet&quot;
  name         = &quot;puppetmaster.gcp.example.com.&quot;
  managed_zone = &quot;gcp-example&quot;
  rrdatas      = [&quot;${google_compute_instance.master.network_interface.0.access_config.0.nat_ip}&quot;]
  ttl          = &quot;300&quot;
  type         = &quot;A&quot;
}

resource &quot;google_compute_firewall&quot; &quot;puppet&quot; {
  project = &quot;puppet&quot;
  name    = &quot;pe-master&quot;
  network = &quot;${google_compute_network.pe-network.name}&quot;
  target_tags = [ &quot;pe-master&quot; ]
  allow {
    protocol = &quot;tcp&quot;
    ports = [ &quot;22&quot;, &quot;5432&quot;, &quot;443&quot;,&quot;8140&quot;,&quot;8142&quot;,&quot;8143&quot;,&quot;8170&quot;,&quot;4433&quot;,&quot;8123&quot;,&quot;8080&quot;,&quot;8081&quot; ]
  }
}

resource &quot;google_compute_network&quot; &quot;pe-network&quot; {
  project                 = &quot;puppet&quot;
  name                    = &quot;pe-network&quot;
  auto_create_subnetworks = &quot;true&quot;
}

resource &quot;google_compute_instance&quot; &quot;master&quot; {
  project      = &quot;puppet&quot;
  zone         = &quot;australia-southeast1-b&quot;
  name         = &quot;master&quot;
  machine_type = &quot;n1-standard-4&quot;
  boot_disk {
    initialize_params {
      size = 30
      image = &quot;centos-cloud/centos-7&quot;
    }
  }
  network_interface {
    network       = &quot;${google_compute_network.pe-network.self_link}&quot;
    access_config = {
    }
  }
  lifecycle {
    ignore_changes = [ &quot;attached_disk&quot; ]
  }

  tags = [ &quot;pe-master&quot;, &quot;https-server&quot; ]

  metadata = {
   &quot;sshKeys&quot;        = &quot;tony_green:${file(&quot;~/.ssh/gcp.pub&quot;)}&quot;
   &quot;enable-oslogin&quot; = true
  }
}

resource &quot;google_compute_instance&quot; &quot;compiler&quot; {
  count                     = 3
  allow_stopping_for_update = true
  project                   = &quot;puppet&quot;
  zone                      = &quot;australia-southeast1-a&quot;
  name                      = &quot;compiler-${count.index + 1}&quot;
  machine_type              = &quot;n1-standard-2&quot;
  boot_disk {
    initialize_params {
      image = &quot;centos-cloud/centos-7&quot;
    }
  }
  network_interface {
    network       = &quot;${google_compute_network.pe-network.self_link}&quot;
    access_config = {
    }
  }

  tags = [ &quot;compiler&quot;, &quot;pe-master&quot; ]

  metadata = {
   &quot;sshKeys&quot;        = &quot;tony_green:${file(&quot;~/.ssh/gcp.pub&quot;)}&quot;
   &quot;enable-oslogin&quot; = true
  }
}

resource &quot;google_compute_instance&quot; &quot;cd4pe&quot; {
  project      = &quot;puppet&quot;
  zone         = &quot;australia-southeast1-a&quot;
  name         = &quot;cd4pe&quot;
  machine_type = &quot;n1-standard-4&quot;
  boot_disk {
    initialize_params {
      size = 30
      image = &quot;centos-cloud/centos-7&quot;
    }
  }
  network_interface {
    network       = &quot;${google_compute_network.pe-network.self_link}&quot;
    access_config = {
    }
  }

  tags = [ &quot;pe-master&quot;, &quot;cd4pe&quot;, &quot;https-server&quot; ]

  metadata = {
    &quot;enable-oslogin&quot; = true
    &quot;sshKeys&quot;        = &quot;tony_green:${file(&quot;~/.ssh/gcp.pub&quot;)}&quot;
  }

  depends_on = [&quot;google_compute_instance.master&quot;]
}
resource &quot;google_compute_instance&quot; &quot;replica&quot; {
  project      = &quot;puppet&quot;
  zone         = &quot;australia-southeast1-a&quot;
  name         = &quot;replica&quot;
  machine_type = &quot;n1-standard-4&quot;
  boot_disk {
    initialize_params {
      image = &quot;centos-cloud/centos-7&quot;
    }
  }
  network_interface {
    network       = &quot;${google_compute_network.pe-network.self_link}&quot;
    access_config = {
    }
  }

  tags = [ &quot;pe-master&quot;, &quot;https-server&quot; ]

  metadata = {
    &quot;enable-oslogin&quot; = true
    &quot;sshKeys&quot;        = &quot;tony_green:${file(&quot;~/.ssh/gcp.pub&quot;)}&quot;
  }

  depends_on = [&quot;google_compute_instance.master&quot;]
}

resource &quot;google_compute_instance_group&quot; &quot;compiler-cluster&quot; {
  project     = &quot;puppet&quot;
  name        = &quot;compiler-cluster&quot;
  description = &quot;Terraform test instance group&quot;

  instances = [
    &quot;${google_compute_instance.compiler.*.self_link}&quot;
  ]

  named_port {
    name = &quot;puppet-8140&quot;
    port = &quot;8140&quot;
  }

  named_port {
    name = &quot;puppet-8142&quot;
    port = &quot;8142&quot;
  }

  zone = &quot;australia-southeast1-a&quot;
}

resource &quot;google_compute_forwarding_rule&quot; &quot;compiler-forwarding-rule&quot; {
  project               = &quot;puppet&quot;
  name                  = &quot;compiler-lb&quot;
  load_balancing_scheme = &quot;INTERNAL&quot;
  network               = &quot;${google_compute_network.pe-network.name}&quot;
  backend_service       = &quot;${google_compute_region_backend_service.compiler-lb.self_link}&quot;
  ports                 = [ &quot;8140&quot;, &quot;8142&quot; ]
  region                = &quot;australia-southeast1&quot;
}

resource &quot;google_compute_region_backend_service&quot; &quot;compiler-lb&quot; {
  project          = &quot;puppet&quot;
  name             = &quot;compiler-lb&quot;
  protocol         = &quot;TCP&quot;
  timeout_sec      = 10
  session_affinity = &quot;NONE&quot;
  region           = &quot;australia-southeast1&quot;

  backend {
    group = &quot;${google_compute_instance_group.compiler-cluster.self_link}&quot;
  }

  health_checks = [&quot;${google_compute_health_check.compiler-healthcheck.self_link}&quot;]
}

resource &quot;google_compute_health_check&quot; &quot;compiler-healthcheck&quot; {
  project            = &quot;puppet&quot;
  name               = &quot;compiler-healthcheck&quot;
  check_interval_sec = 5
  timeout_sec        = 5

  tcp_health_check {
    port = &quot;8140&quot;
  }
}

output &quot;master_ip&quot; {
  value = [&quot;${google_compute_instance.master.network_interface.0.access_config.0.nat_ip}&quot;]
}

output &quot;compiler_ip&quot; {
  value = [&quot;${google_compute_instance.compiler.*.network_interface.0.access_config.0.nat_ip}&quot;]
}

output &quot;replica_ip&quot; {
  value = [&quot;${google_compute_instance.replica.network_interface.0.access_config.0.nat_ip}&quot;]
}

output &quot;cd4pe_ip&quot; {
  value = [&quot;${google_compute_instance.cd4pe.network_interface.0.access_config.0.nat_ip}&quot;]
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The bolt plan and inventory look like this :&lt;/p&gt;

&lt;h3 id=&quot;inventoryyaml-1&quot;&gt;inventory.yaml&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;groups&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;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;target-lookups&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;na&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;terraform&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/dev/terraform_puppet&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;resource_type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;google_compute_instance.master&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;network_interface.0.access_config.0.nat_ip&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;replica&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;target-lookups&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;na&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;terraform&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/dev/terraform_puppet&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;resource_type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;google_compute_instance.replica&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;network_interface.0.access_config.0.nat_ip&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;compilers&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;target-lookups&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;na&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;terraform&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/dev/terraform_puppet&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;resource_type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;google_compute_instance.compiler&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;network_interface.0.access_config.0.nat_ip&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;cd4pe&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;target-lookups&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;na&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;terraform&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/dev/terraform_puppet&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;resource_type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;google_compute_instance.cd4pe&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;network_interface.0.access_config.0.nat_ip&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;transport&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;private-key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/.ssh/gcp&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;centos&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;host-key-check&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;run-as&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;plansbuild_pepp-1&quot;&gt;plans/build_pe.pp&lt;/h3&gt;

&lt;div class=&quot;language-puppet highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;plan&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;terraform_puppet::build_pe&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;$tf_path&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;$localhost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'localhost'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cd &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$tf_path&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;} &amp;amp;&amp;amp; terraform apply --auto-approve&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$localhost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;# Pull targets out of terraform, configured in the inventory.yaml file
&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;$master&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'master'&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;nv&quot;&gt;$master&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Must specify a single master, not &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;${master}&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;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;$replica&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'replica'&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;nv&quot;&gt;$master&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Must specify a single master, not &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;${master}&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;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;$cd4pe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'cd4pe'&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;nv&quot;&gt;$master&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Must specify a single cd4pe, not &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;${cd4pe}&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;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;$compilers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_targets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'compilers'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;# Transfer installer scripts to the master and do the install
&lt;/span&gt;  &lt;span class=&quot;nf&quot;&gt;upload_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'~/dev/terraform_puppet/provision'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/tmp/provision'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$master&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/tmp/provision/setup.sh'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$master&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;# Install the agent on the replia
&lt;/span&gt;  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'curl -k https://master.c.puppet.internal:8140/packages/current/install.bash | sudo bash -s -- --puppet-service-ensure stopped'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$replica&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/puppetlabs/bin/puppet agent -t || exit 0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$replica&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;# Configure the replica
&lt;/span&gt;  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/puppetlabs/bin/puppet-infrastructure provision replica replica.c.puppet.internal'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$master&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;# Install the agents on the compilers
&lt;/span&gt;  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'curl -k https://master.c.puppet.internal:8140/packages/current/install.bash | sudo bash -s main:dns_alt_names=puppet.example.com'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$compilers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/puppetlabs/bin/puppet agent -t || exit 0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$compilers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/puppetlabs/bin/puppet agent -t || exit 0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$master&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'curl -k https://master.c.puppet.internal:8140/packages/current/install.bash | sudo bash -s -- --puppet-service-ensure stopped'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$replica&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/puppetlabs/bin/puppet agent -t || exit 0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$replica&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;# Install the agent on cd4pe
&lt;/span&gt;  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'curl -k https://master.c.puppet.internal:8140/packages/current/install.bash | sudo bash -s -- --puppet-service-ensure stopped'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$cd4pe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/puppetlabs/bin/puppet agent -t || exit 0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$cd4pe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;run_command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/opt/puppetlabs/bin/puppet task run pe_installer_cd4pe::install cd4pe_admin_email=&quot;puppet@example.com&quot; cd4pe_admin_password=&quot;p@ssword&quot; -n cd4pe.c.puppet.internal'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$master&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_run_as'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The step to provision PE just copies up a script to download the latest version of Puppet Enterprise, untar it, and run the setup with a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pe.conf&lt;/code&gt; to get things like code manager working.&lt;/p&gt;

&lt;h2 id=&quot;what-worked&quot;&gt;What worked&lt;/h2&gt;

&lt;p&gt;In under 30 mins I can go from not having any PE infrastructure to having a full HA master, compile masters and CD4PE.  Everything other than CD4PE is fully automated and is running in it’s final state by the end of the process.&lt;/p&gt;

&lt;p&gt;This was a proof of concept for me, using PE as it’s a platform I’m very familiar with.  Adapting it other applications is trivial.&lt;/p&gt;

&lt;h2 id=&quot;what-didnt-work&quot;&gt;What didn’t work&lt;/h2&gt;

&lt;p&gt;Getting started with the dynamic inventory was tricky, however it turns out the problem was because I was  using a remote backend for my terraform state.  Mine was stored on GCP storage, so the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform.state&lt;/code&gt; file just contained a pointer to the remote storage, so the new bolt plugin didn’t work.  Once I moved back to local state storage (not something I want to do long term) everything “just worked”.&lt;/p&gt;

&lt;p&gt;I need to look into that in more detail and talk to the Bolt team to see if there’s something that I’m missing.&lt;/p&gt;

&lt;p&gt;The plugin also needs to have the terraform state in place before the plan starts, so having the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt; command inside the plan won’t work on the first run and &lt;em&gt;may&lt;/em&gt; not work correctly on subsequent runs.  Again, this isn’t a huge problem and could be something that I’m doing wrong or a feature that will be added in future.&lt;/p&gt;

&lt;p&gt;I’m looking forward to seeing the CD4PE setup get a bit more puppetized.  It’s still a fair bit of pointy clickyness once you have it installed.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I’m still pretty new to terraform and I know there’s a lot of duplication in my code, I’m going to clean that up next.&lt;/li&gt;
  &lt;li&gt;I’m not happy with the way I’m using bolt to do the install of the puppet agents on the nodes.  I know I could just use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prep&lt;/code&gt; function, but that just installs the agent, it doesn’t point it to the puppet master (at least as far as I could tell).  I’ll look into how I can make that nicer.&lt;/li&gt;
  &lt;li&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform import&lt;/code&gt; to pull state information out of existing infrastructure so that I can use bolt to mange it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Bolt continues to go from strength to strength.  The dynamic inventory (currently supporting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform&lt;/code&gt; and PuppetDB connections) really makes life a lot easier.&lt;/p&gt;

&lt;p&gt;Since you can drive bolt off your existing puppet control repository, you can get even more creative with how you stand up and manage infrastructure.&lt;/p&gt;

&lt;p&gt;This process could easily work with a mixed-vendor/hybrid cloud environment.&lt;/p&gt;
</description>
				<pubDate>Sun, 26 May 2019 11:34:29 +0000</pubDate>
				<link>/2019/05/puppet-bolt-and-terraform/</link>
				<guid isPermaLink="true">/2019/05/puppet-bolt-and-terraform/</guid>
			</item>
		
			<item>
				<title>@waiting for, @agenda, delegation and OmniFocus</title>
				<description>&lt;p&gt;There was a kink in my workflow for OmniFocus for quite a while, I could never find the right way to handle items I’ve delegated to or need feedback from others on.  There are dozens of different workflows that I’ve seen out on the interwebs but none of them fitted right.&lt;/p&gt;

&lt;p&gt;I’ve slowly changed role at $work over the last couple of years, from being a full time techie to running a (growing) team.  I was finding having a single @waiting-for context didn’t scale, I tried putting tagging into the task summary but that didn’t allow me to see things at varying levels.&lt;/p&gt;

&lt;p&gt;What finally worked for me is to nest some contexts like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/context-nest.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;By nesting in this way, I do a couple of things which are important to me.  Firstly, any items I need to talk to John Doe about will sit in the base “John Doe” context.  Secondly, any items which I need John to come back to me on will sit in the “WF: John Doe” context.&lt;/p&gt;

&lt;p&gt;This simple change to my workflow means I can simply select one context (“John Doe”) and see both the items I need to talk to John about and the items I’m waiting for him on.&lt;/p&gt;

&lt;p&gt;When taken to the next level, it becomes even more powerful.  By continuing the nesting idea, you can give more structure to your agenda contexts:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/context-full.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now I have layers to the agendas.  I can select the “Engineering team” context if I want to see every task I have that involves that team, from individuals to the meetings.  I can also drill down to individuals to see the micro view.&lt;/p&gt;

&lt;p&gt;When used with the new OmniFocus “quick open” function, you get REALLY fast access to the data you need at whatever level you need to see it at.  It makes it easy when I catch up with anyone to pull up a list of what I could ask them and what they’re supposed to be coming back to me on.&lt;/p&gt;

&lt;p&gt;The down side is you end up with a lot of contexts.  I have almost 100 at the moment, 80 of them nested under my base “Agenda” context.  I’ve not noticed a performance issue from doing it though and the payoff is more than worth the potential clutter.&lt;/p&gt;
</description>
				<pubDate>Sat, 04 Jun 2016 11:34:29 +0000</pubDate>
				<link>/2016/06/delegation-and-omnifocus/</link>
				<guid isPermaLink="true">/2016/06/delegation-and-omnifocus/</guid>
			</item>
		
			<item>
				<title>Inside OmniFocus by OmniGroup</title>
				<description>&lt;p&gt;I was stunned to find that the OmniGroup mentioned &lt;a href=&quot;http://albatrossflavour.com/omnifocus-project-layout&quot;&gt;my project layout post&lt;/a&gt; on the new &lt;a href=&quot;http://inside.omnifocus.com/collected&quot;&gt;Inside OmniFocus Collected&lt;/a&gt; page.  OmniGroup have always been great at building a community around their products, it’s great to see them pulling together some tricks and tips into a portal like this.&lt;/p&gt;
</description>
				<pubDate>Wed, 02 Jul 2014 11:34:29 +0000</pubDate>
				<link>/2014/07/Inside-OmniFocus-by-OmniGroup/</link>
				<guid isPermaLink="true">/2014/07/Inside-OmniFocus-by-OmniGroup/</guid>
			</item>
		
			<item>
				<title>OmniFocus Quick Wins</title>
				<description>&lt;p&gt;Could it be?  Yes, it is, another OmniFocus workflow post.&lt;/p&gt;

&lt;p&gt;This is an evolution of the classic 2 minute rule that is a core part of the GTD workflow.&lt;/p&gt;

&lt;p&gt;I’ve been struggling finding free time at $work to be able to work off my lists.  We’re down a few people and are busier than usual at the moment, so things are starting to back up.&lt;/p&gt;

&lt;p&gt;While I’m not dropping things, I’m certainly seeing a build up in my action lists at the moment.&lt;/p&gt;

&lt;p&gt;I started paying attention to where and when I was able to find slots of time to action things off my lists.  They tended to fall into the following categories:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;5 minute window because people hadn’t turned up for a meeting&lt;/li&gt;
  &lt;li&gt;15-30 minute window because either a meeting had been cancelled or I didn’t have back to back meetings for some reason (yes, it was that bad)&lt;/li&gt;
  &lt;li&gt;60 minute windows, usually when I get in early &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a few limiting criteria when choosing the tasks you can work in when you follow the .  You have:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Context (phone/office/agenda/errands etc.)&lt;/li&gt;
  &lt;li&gt;Energy available&lt;/li&gt;
  &lt;li&gt;Time available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is an option in OmniFocus to include details of &lt;em&gt;estimated time&lt;/em&gt; in actions.  You might have a phone call to make that you know will only be 5 minutes, but another that will take at least 30.  Having that information available to you helps when you know you’re short of long stretches of time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/task-with-time.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;By putting the estimated effort into these tasks, you can create a perspective which shows all tasks that can be completed within defined times.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/of-quick-wins-perspective.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you have this in place, you can use this “Quick Wins” perspectives to see your tasks, ordered in the time it’ll take you to do them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/example-quick-wins.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One thing to be careful of is that tasks WITHOUT an estimated date will not show up in the “Quick Wins” perspective.&lt;/p&gt;

&lt;p&gt;I’ve been using this for a while and it’s really helped me make use of the small bits of time that crop up.  It’s amazing how many projects you can push forward with just a few minutes of action.&lt;/p&gt;
</description>
				<pubDate>Fri, 13 Jun 2014 11:34:29 +0000</pubDate>
				<link>/2014/06/OmniFocus-Quick-Wins/</link>
				<guid isPermaLink="true">/2014/06/OmniFocus-Quick-Wins/</guid>
			</item>
		
			<item>
				<title>OmniFocus Project Layout</title>
				<description>&lt;p&gt;We’ve been having a lot of discussions about Omnifocus at the Sydney GTD Meet-Up over the last few weeks. One of the common questions is around how to lay out the projects view.&lt;/p&gt;

&lt;p&gt;OmniFocus gives you a lot of flexibility in how you arrange your projects, giving you folders and nesting as ways of customising things.&lt;/p&gt;

&lt;p&gt;Over the years, I’ve played around with many different ways of laying things out. It was about 12 months ago that I hit on my current layout that works really well for me.&lt;/p&gt;

&lt;p&gt;Being an avid GTDer I have a mindmap which lists my areas of focus. These are the key areas of your life that will be the focus of your projects and next actions.&lt;/p&gt;

&lt;p&gt;You might want to think about these are the different hats we wear in different parts of our lives. This is an excerpt of my areas of focus:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Work
    &lt;ul&gt;
      &lt;li&gt;Management
        &lt;ul&gt;
          &lt;li&gt;Budgets&lt;/li&gt;
          &lt;li&gt;Training&lt;/li&gt;
          &lt;li&gt;Strategy and roadmaps&lt;/li&gt;
          &lt;li&gt;Communication and PR&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Service Improvement&lt;/li&gt;
      &lt;li&gt;Projects&lt;/li&gt;
      &lt;li&gt;Monitoring and reporting&lt;/li&gt;
      &lt;li&gt;Personal Development&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Personal
    &lt;ul&gt;
      &lt;li&gt;Family&lt;/li&gt;
      &lt;li&gt;Health&lt;/li&gt;
      &lt;li&gt;Personal Development&lt;/li&gt;
      &lt;li&gt;Jobs around the house&lt;/li&gt;
      &lt;li&gt;Administration&lt;/li&gt;
      &lt;li&gt;Finance&lt;/li&gt;
      &lt;li&gt;Holidays and recreation&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I found that if I laid out my &lt;strong&gt;folder&lt;/strong&gt; structure in OmniFocus in the same way, I could accomplish two things. Firstly, I could use the structure as part of my weekly review. Because all of the projects would appear under the relevant area of focus, I can tell at a glance if there is an area that I’m not focusing on enough.&lt;/p&gt;

&lt;p&gt;This happened to me in the last few days. I did my review and noticed that I didn’t have any projects in my budget folder - yet we’re fast approaching budget season.&lt;/p&gt;

&lt;p&gt;The second reason is a bit more subtle but, in my opinion, more powerful. Unless you have put a due date on a project or action, the context view will list tasks in a set order. That order is the order they appear in the projects list.&lt;/p&gt;

&lt;p&gt;That means, all things being equal, tasks for projects in my Management folder will appear higher up the next action list than tasks for projects in my Monitoring and reporting folder.&lt;/p&gt;

&lt;p&gt;For example. I have two phone calls to make, one to book in a budget meeting (area of focus == budget) and one to talk to a project manager about resourcing (area of focus == project). If neither of the tasks or projects have due dates, the call about the budget meeting would appear above the call to the project manager.&lt;/p&gt;

&lt;p&gt;This means you can arrange your folders with a pseudo level of importance. This helps a lot if you’re working off larger next action lists. It doesn’t remove any other filtering or sorting you may do, just adds another metric to assess your next action decisions against.&lt;/p&gt;

&lt;p&gt;This workflow is valid in all versions of OmniFocus (Mac and iOS)&lt;/p&gt;
</description>
				<pubDate>Tue, 10 Jun 2014 11:34:29 +0000</pubDate>
				<link>/2014/06/omnifocus-project-layout/</link>
				<guid isPermaLink="true">/2014/06/omnifocus-project-layout/</guid>
			</item>
		
			<item>
				<title>Digital Tickler File on macOS Using Hazel</title>
				<description>&lt;p&gt;I’ve never really found a place in my physical life for a &lt;a href=&quot;http://en.wikipedia.org/wiki/Tickler_file&quot;&gt;tickler file&lt;/a&gt; in my Getting Things Done workflow, they seem like a great idea but I have no real world paper to manage.&lt;/p&gt;

&lt;p&gt;When carrying out GTD training, I’m often asked if there is digital equivalent of a tickler file. I’m usually training fairly technical people who’re trying to embrace the paperless lifestyle, the idea of having a draw dedicated to 43 folders and having to remember to check it isn’t that appealing. Tickler files only work if you have the discipline to check them daily.&lt;/p&gt;

&lt;p&gt;There are various ways to fake a tickler file, some of which I use already. My current method is to put a link to the file I want “tickled” into [Omnifocus][http://omnifocus.com] with a start date of the day I want to see it. That works pretty well and certainly gives the desired (fully automatic) result.&lt;/p&gt;

&lt;p&gt;Well, this would be a pretty short post if that was the end of it….. But it’s not.&lt;/p&gt;

&lt;p&gt;I was over in Houston doing training a couple of weeks ago and the topic of tickler files came up again. We discussed the idea of having a digital version of the tickler and some people though it would be really useful. I put a loop into Omnifocus’s inbox and didn’t think too much more about it.&lt;/p&gt;

&lt;p&gt;A few days later I was at the airport processing my inbox while waiting to board. I got to the inbox item “Digital tickler files?” and when I got to the standard GTD question of “is it actionable?” I decided, “yes, it is”.&lt;/p&gt;

&lt;p&gt;I spent the next couple of hours figuring out how to setup a fully automated tickler file system on a Mac using a wonderful tool called &lt;a href=&quot;http://www.noodlesoft.com/hazel.php&quot;&gt;Hazel&lt;/a&gt;. For those of you who (for some unknown reason) don’t know what Hazel is, I’ll quote their website : “Hazel watches whatever folders you tell it to, automatically organizing your files according to the rules you create.”&lt;/p&gt;

&lt;p&gt;I use Hazel extensively to manage my files, handle my filing, organise my backups and perform housekeeping on my directories, amongst many other things. I figured if there was a tool that could take care of the hard work involved in a digital tickler file, Hazel would be it.&lt;/p&gt;

&lt;p&gt;What I’ve come up with is a couple of rules that take your files and put them under a tickler directory structure, which Hazel manages. The rules also then “untickle” the files on the day that you’ve requested them back, moving the files back into a specified directory.&lt;/p&gt;

&lt;p&gt;Enough waffling, let’s get into how you can set this up. Firstly, obviously, you need a Mac running the Hazel. At the time of writing this I’m running version 3.1.3 of Hazel, I’d recommend a recent version as one of the conditions (Current Time) was only recently added to the product.&lt;/p&gt;

&lt;p&gt;You need to decide two things, which directory will be your “inbox” and which directory will contain your “tickle” directory. I use my Downloads folder as an inbox and put the tickle directory on Dropbox under $user/Dropbox/Documents/tickle. You can obviously change these to suit your setup.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/hazel.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You then need to &lt;a href=&quot;https://github.com/bandcamp/tickler/archive/master.zip&quot;&gt;download the rules&lt;/a&gt; and import them into Hazel. The rule “Downloads” needs to be associated with your chosen inbox folder, the rule “tickler” needs to be associated with your chosen tickle folder.&lt;/p&gt;

&lt;p&gt;Once those files are in place, you can start testing. Easiest initial test is to add a filename suffix to a file in your inbox. Hazel will look for anything that has a filename that ends in - tickle MMM DD (e.g. example - tickle Jan 01.txt). Hazel will strip out the - tickle MMM DD and put the file into your tickle folder in a structure like this tickle/MMM/DD. You can use &lt;a href=&quot;http://smilesoftware.com/TextExpander/index.html&quot;&gt;textexpander&lt;/a&gt; to automate the addition of the suffix, there is a &lt;a href=&quot;https://github.com/bandcamp/tickler/archive/master.zip&quot;&gt;snippet&lt;/a&gt; in the download which will help you out.&lt;/p&gt;

&lt;p&gt;Once the file is in the tickle structure, you can safely forget about it. On the day you’ve chosen, the file will automagically be placed back into your chosen inbox folder, ready for you to work on it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/tickler.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m actively developing the code, but it seems pretty solid at the moment. You can download the code from my &lt;a href=&quot;http://github.com/albatrossflavour&quot;&gt;GitHub site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any comments, suggestions or problems, feel free to e-mail me.&lt;/p&gt;
</description>
				<pubDate>Wed, 14 Aug 2013 11:34:29 +0000</pubDate>
				<link>/2013/08/digital-gtd-tickler-file-with-hazel/</link>
				<guid isPermaLink="true">/2013/08/digital-gtd-tickler-file-with-hazel/</guid>
			</item>
		
			<item>
				<title>Monospace font joy</title>
				<description>&lt;p&gt;I really love writing in a decent mono-spaced, non-serif, font.  From hacking in the vim to writing Markdown in nvAlt, having a nice font seems to make things just a little more motivating.&lt;/p&gt;

&lt;p&gt;There’s a &lt;a href=&quot;http://slant.co/topics/67/viewpoints/5/~what-are-the-best-programming-fonts~source-code-pro&quot;&gt;great article&lt;/a&gt; which I’ve just read which gives you the chance to view the best programming fonts, including a sample of code.&lt;/p&gt;

&lt;p&gt;Source Code Pro has edged ahead (yes, there’s a vote!) but I’m glad to see a few new mono-spaced friends I can add to my list.&lt;/p&gt;
</description>
				<pubDate>Tue, 30 Apr 2013 11:34:29 +0000</pubDate>
				<link>/2013/04/monospace-font-joy/</link>
				<guid isPermaLink="true">/2013/04/monospace-font-joy/</guid>
			</item>
		
	</channel>
</rss>
