<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/devops/</id>
  <link href="https://www.endpointdev.com/blog/tags/devops/"/>
  <link href="https://www.endpointdev.com/blog/tags/devops/" rel="self"/>
  <updated>2025-11-25T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Migrating from Legacy Networking to systemd-networkd on Ubuntu 24.04</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/11/legacy-networking-to-systemd-networkd/"/>
      <id>https://www.endpointdev.com/blog/2025/11/legacy-networking-to-systemd-networkd/</id>
      <published>2025-11-25T00:00:00+00:00</published>
      <author>
        <name>Bharathi Ponnusamy</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/11/legacy-networking-to-systemd-networkd/banner.webp&#34; alt=&#34;Apartment buildings and a domed white Indian building in Leh city, India sit among trees in front of a layered mountain range, capped with clouds.&#34;&gt;&lt;/p&gt;
&lt;!-- photo by Bharathi Ponnusamy --&gt;
&lt;p&gt;During a recent Ubuntu server upgrade, I migrated a production system from the legacy ifupdown networking stack to the modern systemd-networkd service. This migration was part of preparing several remote production servers for a smooth upgrade to Ubuntu 24.04, which relies heavily on systemd-managed components.&lt;/p&gt;
&lt;p&gt;In this post, I’ll walk through the full migration process, how we automated it safely across multiple remote servers, and the lessons learned from implementing rollback and watchdog mechanisms for zero-downtime transitions.&lt;/p&gt;
&lt;h3 id=&#34;why-migrate-to-systemd-networkd&#34;&gt;Why migrate to systemd-networkd?&lt;/h3&gt;
&lt;p&gt;Ubuntu 24.04 uses systemd-networkd as the default backend for network management. The traditional ifupdown scripts (/etc/network/interfaces) are no longer the recommended way to configure networking. Some of the modern features enabled by systemd-networkd include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Predictable network interface naming (e.g., ens3, eth0, etc.)&lt;/li&gt;
&lt;li&gt;Built-in support for bridges, VLANs, and bonds&lt;/li&gt;
&lt;li&gt;Faster boot times with asynchronous network handling&lt;/li&gt;
&lt;li&gt;Unified configuration under /etc/systemd/network&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Migrating now also avoids future compatibility issues and takes advantage of systemd’s integrated logging and management.&lt;/p&gt;
&lt;h3 id=&#34;step-1-check-the-current-setup&#34;&gt;Step 1: Check the current setup&lt;/h3&gt;
&lt;p&gt;Before the migration, I confirmed the system was still using ifupdown:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls /etc/network/interfaces
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat /etc/network/interfaces&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Output showed legacy configuration similar to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;auto eth0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;iface eth0 inet static
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    address 10.42.41.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    netmask 255.255.0.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-2-install-and-enable-systemd-networkd&#34;&gt;Step 2: Install and enable systemd-networkd&lt;/h3&gt;
&lt;p&gt;Since systemd-networkd wasn’t already active, I installed and enabled it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install systemd-networkd systemd-resolved -y
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable systemd-resolved&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, I stopped ifupdown to prevent conflicts.&lt;/p&gt;
&lt;h3 id=&#34;step-3-create-a-network-configuration-file&#34;&gt;Step 3: Create a network configuration file&lt;/h3&gt;
&lt;p&gt;Each interface now has its own .network file under &lt;code&gt;/etc/systemd/network/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;DHCP-based interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# /etc/systemd/network/10-eth0.network
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Match]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name=eth0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Network]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DHCP=yes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Static interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# /etc/systemd/network/20-eth1.network
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Match]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name=eth1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Network]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Address=10.42.41.1/16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Gateway=10.42.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DNS=8.8.8.8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DNS=1.1.1.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-4-configure-systemd-resolved&#34;&gt;Step 4: Configure systemd-resolved&lt;/h3&gt;
&lt;p&gt;This step ensures your system’s DNS resolver works correctly with systemd-networkd.&lt;/p&gt;
&lt;p&gt;systemd-resolved listens locally on 127.0.0.53 and handles DNS resolution, caching, and per-interface settings.&lt;/p&gt;
&lt;p&gt;Link your system’s &lt;code&gt;/etc/resolv.conf&lt;/code&gt; to use it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then to verify:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;resolvectl status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we skip this, the system might lose DNS resolution after reboot. Applications like apt, curl, and ping may fail to resolve hostnames even though interfaces are up.&lt;/p&gt;
&lt;h3 id=&#34;restart-and-verify&#34;&gt;Restart and verify&lt;/h3&gt;
&lt;p&gt;Restart both services:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl restart systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl restart systemd-resolved&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Check active links:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;networkctl list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Detailed view for one interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;networkctl status eth0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sample output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;● 2: eth0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       Type: ether
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      State: routable (configured)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     Address: 10.42.41.1/16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     Gateway: 10.42.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     DNS: 8.8.8.8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Test:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ping -c 3 8.8.8.8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ping -c 3 google.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-6-reboot-and-confirm&#34;&gt;Step 6: Reboot and confirm&lt;/h3&gt;
&lt;p&gt;Reboot once to ensure everything persists. After the reboot,&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl status systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl status systemd-resolved&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and confirm interface and DNS are still functional.&lt;/p&gt;
&lt;h3 id=&#34;handling-remote-migration-and-downtime-risks&#34;&gt;Handling remote migration and downtime risks&lt;/h3&gt;
&lt;p&gt;Performing this migration remotely on production systems without physical console access required special care. I prepared several layers of protection to prevent accidental lockouts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Console or IPMI verification&lt;/strong&gt;: Ensured each node had out-of-band access in case SSH connectivity was lost.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Timed rollback safety script&lt;/strong&gt;: A background process automatically reverted to legacy networking if the new configuration didn’t come up within a few minutes.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;sleep &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;300&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; ! ping -c1 8.8.8.8 &amp;amp;&amp;gt;/dev/null; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    systemctl disable systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    systemctl &lt;span style=&#34;color:#038&#34;&gt;enable&lt;/span&gt; networking
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    reboot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;automating-the-migration-and-rollback-process&#34;&gt;Automating the migration and rollback process&lt;/h3&gt;
&lt;p&gt;To streamline upgrades across multiple servers, I created automation scripts that handle migration, rollback, and ongoing monitoring.&lt;/p&gt;
&lt;h4 id=&#34;migrate_to_systemd_networkdsh&#34;&gt;migrate_to_systemd_networkd.sh&lt;/h4&gt;
&lt;p&gt;This script performs a complete, logged migration with built-in validation and rollback.
It checks for valid .network files, disables legacy services, enables systemd-networkd, tests connectivity, and if all retries fail, restores the old configuration automatically.&lt;/p&gt;
&lt;p&gt;Key features&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Validates configuration syntax using systemd-analyze verify&lt;/li&gt;
&lt;li&gt;Masks conflicting services (&lt;code&gt;ifup@eth*&lt;/code&gt;, NetworkManager)&lt;/li&gt;
&lt;li&gt;Performs up to three connectivity tests&lt;/li&gt;
&lt;li&gt;Restarts SSH and dependent services&lt;/li&gt;
&lt;li&gt;Schedules a controlled reboot or user-confirmed one&lt;/li&gt;
&lt;li&gt;Rolls back cleanly if connectivity fails&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;upgrade_watchdogsh&#34;&gt;upgrade_watchdog.sh&lt;/h4&gt;
&lt;p&gt;During do-release-upgrade, networking may restart multiple times.
This watchdog script runs in the background to ensure systemd-networkd remains active and services like SSH come back automatically.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Executes every 10 minutes&lt;/li&gt;
&lt;li&gt;Verifies /etc/systemd/network/*.network exists and services are running as expected if it fails, this script restarts the service&lt;/li&gt;
&lt;li&gt;Restarts systemd-networkd, ssh, and dependent services&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, both scripts allowed us to perform fully remote OS upgrades with zero manual downtime and consistent recovery paths.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Migrating to systemd-networkd modernizes Ubuntu servers and simplifies long-term maintenance.
With the automation scripts and safety mechanisms in place, we successfully migrated multiple remote servers with no downtime and automatic rollback protection.&lt;/p&gt;
&lt;p&gt;This upgrade not only prepares infrastructure for Ubuntu 24.04 but also integrates cleanly with our automation and monitoring systems, ensuring reliable networking for years ahead.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Testing Ansible Automation with Molecule</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/03/testing-ansible-with-molecule/"/>
      <id>https://www.endpointdev.com/blog/2025/03/testing-ansible-with-molecule/</id>
      <published>2025-03-28T00:00:00+00:00</published>
      <author>
        <name>Kannan Ponnusamy</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/03/testing-ansible-with-molecule/optical-disc-drive.webp&#34; alt=&#34;Photo of an open hard drive, with the data reading arm extended over the disc.&#34;&gt;&lt;br&gt;
&lt;a href=&#34;https://unsplash.com/photos/photo-of-optical-disc-drive-1iVKwElWrPA&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@heapdump&#34;&gt;Patrick Lindenberg&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;1-why-test-with-molecule&#34;&gt;1. Why Test with Molecule?&lt;/h3&gt;
&lt;p&gt;Molecule is a test framework for Ansible roles. It supports testing with multiple instances, operating systems and distributions, virtualization providers, test frameworks, and testing scenarios.&lt;/p&gt;
&lt;p&gt;Molecule is useful because it increases confidence in your automation and ensures roles are reliable. It catches issues early in the development cycle, reducing production problems. It also ensures consistency across different environments and platforms.&lt;/p&gt;
&lt;h3 id=&#34;2-install-prerequisites&#34;&gt;2. Install Prerequisites&lt;/h3&gt;
&lt;p&gt;Make sure you have Python &amp;gt;= 3.6 and pip installed. Then install Ansible and Molecule using pip:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install ansible
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install molecule&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Molecule uses the &lt;code&gt;delegated&lt;/code&gt; driver by default. Other drivers can be installed separately from PyPI, most of them being included in the &lt;code&gt;molecule-plugins&lt;/code&gt; package. We are going to use the Docker driver, so let&amp;rsquo;s install that by running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install &amp;#34;molecule[docker]&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;3-ansible-monorepo-structure&#34;&gt;3. Ansible Monorepo Structure&lt;/h3&gt;
&lt;p&gt;We use the Ansible monorepo structure. This means our playbooks, variables, scripts, roles, plugins, inventory scripts, and configuration all reside and are version controlled together in the same repository.&lt;/p&gt;
&lt;p&gt;Here is the high level layout:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;~/ansible-endpoint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── ansible.cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── ansible_hosts
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── molecule/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── playbooks/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── roles/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── apache_server/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── nginx_server/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We&amp;rsquo;ll focus on &lt;code&gt;roles/nginx_server&lt;/code&gt;, showing how to add and configure the Molecule scenarios for testing and also how to verify it.&lt;/p&gt;
&lt;h3 id=&#34;4-creating-your-first-molecule-scenario&#34;&gt;4. Creating Your First Molecule Scenario&lt;/h3&gt;
&lt;p&gt;Inside the &lt;code&gt;roles/nginx_server&lt;/code&gt; directory, let&amp;rsquo;s initialize the Molecule scenario:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd roles/nginx_server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;molecule init scenario --driver-name docker --scenario-name default&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command will create the following files:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;roles/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── nginx_server/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── tasks/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └── main.yml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── molecule/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └── default/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │       ├── molecule.yml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │       ├── converge.yml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │       ├── verify.yml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │       └── destroy.yml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here are some details about the files created by Molecule:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;molecule.yml&lt;/code&gt; — The Molecule configuration file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;---&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;driver&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;docker&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;platforms&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;molecule-rocky9-${CI_JOB_ID}&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;geerlingguy/docker-rockylinux9-ansible:latest&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;privileged&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pre_build_image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;volumes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;- /sys/fs/cgroup:/sys/fs/cgroup:rw&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;cgroupns_mode&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;host&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;command&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;/usr/sbin/init&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;provisioner&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ansible&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Key blocks from &lt;code&gt;molecule.yml&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;provisioner&lt;/code&gt; is the tool that will be used to provision the scenario. We are using Ansible to run the scenario itself.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;driver&lt;/code&gt; — As mentioned above, we are using Docker as the driver.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;platforms&lt;/code&gt; — Here we define the target platform for the scenario. We are using the Rocky 9 Docker image as the target platform. Thanks to &lt;a href=&#34;https://github.com/geerlingguy/docker-rockylinux9-ansible&#34;&gt;Jeff Geerling&lt;/a&gt; who created the Rocky 9 Docker image for Ansible testing with systemd in it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;converge.yml&lt;/code&gt; — The playbook to converge the scenario. This tells Molecule to apply the &lt;code&gt;nginx_server&lt;/code&gt; role to our test container (the &amp;ldquo;instance&amp;rdquo; defined in &lt;code&gt;molecule.yml&lt;/code&gt;).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;---&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Converge&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;hosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;all&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;gather_facts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;tasks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Run the nginx_server role&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;hosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;all&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;roles&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- nginx_server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;verify.yml&lt;/code&gt; — The playbook to verify whether the converge was successful. This tells Molecule to verify that our role has been correctly installed on the Docker instance. Here we are going to check if NGINX is installed, running and also responding to requests.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;---&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Verify&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;hosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;all&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;tasks&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Check if NGINX is installed&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ansible.builtin.package&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;nginx&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;state&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;present&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;check_mode&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;register&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;install&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;failed_when&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(install is changed) or (install is failed)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Check if NGINX service is running&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ansible.builtin.service&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;nginx&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;state&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;started&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;enabled&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;check_mode&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;register&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;service&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;failed_when&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(service is changed) or (service is failed)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Verify NGINX is up and running&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ansible.builtin.uri&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;url&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;http://localhost&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;status_code&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;200&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;destroy.yml&lt;/code&gt; — Destroys the instance defined in the &lt;code&gt;molecule.yml&lt;/code&gt; file. There won&amp;rsquo;t be any file, but the default Docker driver will automatically destroy the instance after the test is finished. If you want to override this behavior, you can add a &lt;code&gt;destroy.yml&lt;/code&gt; file to the scenario directory.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;5-running-molecule-tests&#34;&gt;5. Running Molecule tests&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A Molecule run consists of the various lifecycle events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;converge&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;verify&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;destroy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are some of the important events in the Molecule lifecycle run. To run the full lifecycle Molecule run, we can use the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd roles/nginx_server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;molecule test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command executes the following steps in sequence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create: Spins up a test Rocky 9 container as mentioned in the &lt;code&gt;molecule.yml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Converge: Applies the &lt;code&gt;nginx_server&lt;/code&gt; role inside the container.&lt;/li&gt;
&lt;li&gt;Verify: Verifies whether the NGINX server is installed, running, and responding to requests.&lt;/li&gt;
&lt;li&gt;Destroy: Cleans up the container.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can also run each step individually:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;molecule create&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;molecule converge&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;molecule verify&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;molecule destroy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To avoid rebuilding the containers, we can run commands from converge step.&lt;/p&gt;
&lt;h3 id=&#34;6-integrating-molecule-with-gitlab-cicd&#34;&gt;6. Integrating Molecule with GitLab CI/CD&lt;/h3&gt;
&lt;p&gt;We want to run the Molecule tests whenever a merge request which changes the particular role is created.&lt;/p&gt;
&lt;p&gt;To automatically run the Molecule tests, we can use the GitLab CI/CD.&lt;/p&gt;
&lt;p&gt;Here is an example of a &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file that runs our Molecule tests in the Docker based environment for our &lt;code&gt;nginx_server&lt;/code&gt; role.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;stages&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;- test&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;before_script&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;- pip install molecule ansible docker molecule-docker&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;molecule_test&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;stage&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;test&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;variables&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ROLE_PATH&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;roles/nginx_server&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- cd $ROLE_PATH&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- molecule test&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;rules&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;changes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- $ROLE_PATH/**&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With this setup, we&amp;rsquo;ve set up a Molecule scenario for a specific Ansible role, then run tests both manually and automatically via GitLab CI. This allows tests to trigger whenever a merge request modifies that role, ensuring a quick feedback loop and a higher degree of reliability in the automation.&lt;/p&gt;
&lt;p&gt;To learn more about Molecule, here are some useful links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://ansible.readthedocs.io/projects/molecule/&#34;&gt;Molecule documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/ansible/molecule&#34;&gt;Molecule GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/ansible-community/molecule-plugins&#34;&gt;Molecule driver plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Automating DNS Management with GitLab CI/CD</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2024/09/automating-dns-management-with-gitlab-ci-cd/"/>
      <id>https://www.endpointdev.com/blog/2024/09/automating-dns-management-with-gitlab-ci-cd/</id>
      <published>2024-09-06T00:00:00+00:00</published>
      <author>
        <name>Kannan Ponnusamy</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2024/09/automating-dns-management-with-gitlab-ci-cd/robot-manufacturing-floor.webp&#34; alt=&#34;Several robotic arms sit perched over mechanical tracks in a factory, ready to do some type of assembly.&#34;&gt;&lt;br&gt;
&lt;a href=&#34;https://unsplash.com/photos/a-factory-filled-with-lots-of-orange-machines-8gr6bObQLOI&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@simonkadula&#34;&gt;Simon Kadula&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At End Point, managing DNS records across multiple domains has historically been a manual task. This blog post details our journey from manual processes to an automated workflow using GitLab CI/CD.&lt;/p&gt;
&lt;h3 id=&#34;our-initial-approach&#34;&gt;Our Initial Approach&lt;/h3&gt;
&lt;p&gt;With multiple domains and frequent updates necessary to manage the servers, manual handling of DNS changes became a bottleneck. Initially, our process looked like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make changes to the OpenTofu configuration files&lt;/li&gt;
&lt;li&gt;Create a merge request (MR) in GitLab&lt;/li&gt;
&lt;li&gt;They would run &lt;code&gt;tofu plan&lt;/code&gt; manually and paste the plan output into the MR for review&lt;/li&gt;
&lt;li&gt;A coworker would review the MR and approve the changes&lt;/li&gt;
&lt;li&gt;Once merged, the engineer would manually run &lt;code&gt;tofu apply&lt;/code&gt; to implement the changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While this process worked, automating it could enhance our productivity and minimize errors, integrating our DNS management directly into our CI/CD pipeline.&lt;/p&gt;
&lt;h3 id=&#34;the-solution-automating-with-gitlab-cicd&#34;&gt;The Solution: Automating with GitLab CI/CD&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Change Submission: Engineers make changes to the OpenTofu files and submit a merge request&lt;/li&gt;
&lt;li&gt;Plan Creation: A GitLab CI/CD job automatically generates an OpenTofu plan when changes are proposed&lt;/li&gt;
&lt;li&gt;Review Process: A coworker reviews the automatically generated plan in the MR&lt;/li&gt;
&lt;li&gt;Applying Changes: Once approved and merged, another CI/CD job automatically runs &lt;code&gt;tofu apply&lt;/code&gt; to implement the changes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;implementation-details&#34;&gt;Implementation Details:&lt;/h3&gt;
&lt;h4 id=&#34;1-gitlab-ciyaml&#34;&gt;1. &lt;code&gt;.gitlab-ci.yaml&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;If you are doing this on a self-hosted GitLab instance you would need to import the repo to your hosted GitLab workspace and use it there.&lt;/p&gt;
&lt;p&gt;We divided our GitLab CI/CD pipelines into three main stages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Validation: Where we check if everything looks right&lt;/li&gt;
&lt;li&gt;Planning: Where we create a plan to show the changes&lt;/li&gt;
&lt;li&gt;Deployment: Where the changes get applied once everything is approved&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&amp;rsquo;s important to note that our CI/CD pipeline runs on a dedicated worker. This means only this worker has access to the secret keys needed for DNS management. By isolating these credentials to a single, controlled environment, we significantly reduce the risk of unauthorized access or accidental exposure of sensitive data.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;include&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;component&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;gitlab.com/components/opentofu/job-templates@main&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;inputs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;version&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0.24.0&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;opentofu_version&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1.6.2&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;auto_apply&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;before_script&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;- source /home/gitlab-runner/.tofu.env&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;stages&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;validate, build, deploy]&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;fmt-example.com&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;extends&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.opentofu:fmt ]&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;variables&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TF_ROOT&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TF_STATE_NAME&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example_com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;rules&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;if&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$CI_PIPELINE_SOURCE == &amp;#34;merge_request_event&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;changes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- example.com/*&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;if&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$CI_COMMIT_BRANCH == &amp;#34;main&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;changes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- example.com/*&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;validate-example.com&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;extends&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.opentofu:validate ]&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;variables&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TF_ROOT&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TF_STATE_NAME&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example_com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;rules&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;if&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$CI_PIPELINE_SOURCE == &amp;#34;merge_request_event&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;changes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- example.com/*&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;if&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$CI_COMMIT_BRANCH == &amp;#34;main&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;changes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- example.com/*&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;plan-example.com&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;extends&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.opentofu:plan ]&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;variables&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TF_ROOT&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TF_STATE_NAME&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example_com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;rules&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;if&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$CI_PIPELINE_SOURCE == &amp;#34;merge_request_event&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;changes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- example.com/*&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;if&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$CI_COMMIT_BRANCH == &amp;#34;main&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;changes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- example.com/*&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;after_script&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- source /home/gitlab-runner/.tofu.env&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- ./gitlab-comment-tofu-log.sh $TF_ROOT/plan_output.txt&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;apply-example.com&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;extends&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.opentofu:apply ]&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;variables&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TF_ROOT&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TF_STATE_NAME&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example_com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;rules&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;if&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$CI_COMMIT_BRANCH == &amp;#34;main&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;changes&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;- example.com/*&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;after_script&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- source /home/gitlab-runner/.tofu.env&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- ./gitlab-comment-tofu-log.sh $TF_ROOT/apply_output.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;2-posting-opentofu-planapply-logs-as-merge-request-comments&#34;&gt;2. Posting OpenTofu plan/apply logs as merge request comments&lt;/h4&gt;
&lt;p&gt;To facilitate code reviews, we created a script (&lt;code&gt;gitlab-comment-tofu-log.sh&lt;/code&gt;) that automatically formats and posts the OpenTofu plan and apply logs as comments on merge requests. This ensures that reviewers can easily understand the changes before approving them.&lt;/p&gt;
&lt;p&gt;Here is the script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;set&lt;/span&gt; -e
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;GitLab MR Comment Script Starts&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [ &lt;span style=&#34;color:#369&#34;&gt;$#&lt;/span&gt; -eq &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; ]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Error: No output file path provided&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Usage: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$0&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; &amp;lt;path_to_output_file&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;output_file&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$1&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Using output file: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$output_file&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Checking environment variables&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [ -z &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$GITLAB_API_TOKEN&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; ]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Error: GITLAB_API_TOKEN is not set&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [ -z &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CI_PROJECT_ID&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; ]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Error: CI_PROJECT_ID is not set&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [ -n &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CI_MERGE_REQUEST_IID&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; ]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Using CI_MERGE_REQUEST_IID: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CI_MERGE_REQUEST_IID&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;CI_MERGE_REQUEST_IID not set, fetching from API&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;CURRENT_COMMIT_SHA&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;git rev-parse HEAD&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Current commit SHA: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CURRENT_COMMIT_SHA&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;MR_INFO&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;curl --silent --header &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;PRIVATE-TOKEN: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$GITLAB_API_TOKEN&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://&amp;lt;link_to_self_hosted_gitlab&amp;gt;/api/v4/projects/&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CI_PROJECT_ID&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/repository/commits/&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CURRENT_COMMIT_SHA&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/merge_requests&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$MR_INFO&lt;/span&gt; | jq &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;. | length&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; -eq &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; ]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Error: No merge request found for this commit&amp;#34;&lt;/span&gt; &amp;gt;&amp;amp;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#038&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;CI_MERGE_REQUEST_IID&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$MR_INFO&lt;/span&gt; | jq &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;.[0].iid&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Found Merge Request IID: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CI_MERGE_REQUEST_IID&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Reading output file: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$output_file&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [ ! -f &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$output_file&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; ]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Error: File &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$output_file&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; not found&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;file_name&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;basename &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$output_file&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [[ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$file_name&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; == plan_* ]]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;heading&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;br&amp;gt;Plan log for &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$TF_ROOT&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;output&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;awk &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/OpenTofu used the selected providers/,0&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$output_file&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;elif&lt;/span&gt; [[ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$file_name&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; == apply_* ]]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;heading&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;br&amp;gt;Apply log for &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$TF_ROOT&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;output&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;awk &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/OpenTofu has been successfully initialized/,0&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$output_file&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;heading&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;br&amp;gt;Tofu Output for &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$TF_ROOT&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Filtered output read. Length: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${#&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;output&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; characters&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Cleaning and formatting output&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;cleaned_output&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; -e &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$output&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; | sed -e &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/\x1b\[[0-9;]*m//g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;    -e &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/\\u001b\[[0-9;]*m//g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;    -e &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/\\n/\n/g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;    -e &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/\\&amp;#34;//g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;    -e &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/^&amp;#34;//g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;    -e &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/&amp;#34;$//g&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Cleaned output. Length: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${#&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;cleaned_output&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; characters&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Format the output with heading and code block&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;formatted_output&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;printf&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;## %s\n\n```\n%s\n```&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$heading&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$cleaned_output&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Formatted output. Length: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${#&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;formatted_output&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; characters&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Posting comment to GitLab MR&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;response&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;curl --silent --show-error --fail &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;     --request POST &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;     --header &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;PRIVATE-TOKEN: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$GITLAB_API_TOKEN&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;     --header &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;     --data &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;jq -n --arg body &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$formatted_output&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;{&amp;#34;body&amp;#34;: $body}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;     &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://code.self_hosted_gitlab.com/api/v4/projects/&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CI_PROJECT_ID&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/merge_requests/&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$CI_MERGE_REQUEST_IID&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/notes&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; [ &lt;span style=&#34;color:#369&#34;&gt;$?&lt;/span&gt; -eq &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; ]; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Comment successfully posted to GitLab MR&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Error: Failed to post comment to GitLab MR. Response: &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$response&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;exit&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Script completed successfully&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here is the &lt;code&gt;tofu plan&lt;/code&gt; output comment:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2024/09/automating-dns-management-with-gitlab-ci-cd/plan.webp&#34; alt=&#34;The screenshot shows a plan log entry in merge request. The log entry reports actions by OpenTofu, indicating it used selected providers to generate an execution plan.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Here is the &lt;code&gt;tofu apply&lt;/code&gt; output comment:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2024/09/automating-dns-management-with-gitlab-ci-cd/apply.webp&#34; alt=&#34;The screenshot shows a tofu apply log entry in merge request. The log entry reports apply action by OpenTofu, indicating it used selected providers to apply the above generated plan.&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;3-migrating-to-gitlabs-remote-http-backend&#34;&gt;3. Migrating to GitLab’s Remote HTTP Backend&lt;/h4&gt;
&lt;p&gt;Transitioning your local Tofu state to a remote backend hosted by GitLab can streamline state management and enhance security. Follow these straightforward steps to achieve this.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, set up the necessary environment variables with your specific details:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;TF_STATE_NAME&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;example_net&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;TF_HTTP_ADDRESS&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://&amp;lt;link_to_GitLab_instance&amp;gt;&amp;gt;/api/v4/projects/460/terraform/state/&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$TF_STATE_NAME&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;TF_HTTP_USERNAME&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;create_a_user&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;TF_HTTP_PASSWORD&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;create_a_password&amp;gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Initialize the remote backend with Tofu, specifying your new backend configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tofu init -migrate-state &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;  -backend-config=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;address=&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$TF_HTTP_ADDRESS&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;  -backend-config=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username=&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$TF_HTTP_USERNAME&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;&lt;/span&gt;  -backend-config=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;password=&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$TF_HTTP_PASSWORD&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify the migration by running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tofu plan&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After these steps, your Tofu environment will be fully integrated with GitLab’s remote HTTP backend, with state management and enhanced collaboration and security.&lt;/p&gt;
&lt;p&gt;Automation of our DNS management with GitLab CI/CD and OpenTofu has transformed our operations, making them more efficient, error-resistant, and secure. We encourage other teams to explore similar automation strategies to enhance their infrastructure management processes.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Kubernetes From The Ground Up With AWS EC2</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2022/10/kubernetes-from-the-ground-up-with-aws-ec2/"/>
      <id>https://www.endpointdev.com/blog/2022/10/kubernetes-from-the-ground-up-with-aws-ec2/</id>
      <published>2022-10-06T00:00:00+00:00</published>
      <author>
        <name>Jeffry Johar</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2022/10/kubernetes-from-the-ground-up-with-aws-ec2/ship.webp&#34; alt=&#34;A docked fishing ship faces the camera. A man stands on a dinghy next to it.&#34;&gt;&lt;br&gt;
Photo by Darry Lin &lt;!-- https://www.pexels.com/@darrylin/ --&gt;&lt;/p&gt;
&lt;p&gt;One way to learn Kubernetes infrastructure is to build it from scratch. This way of learning was introduced by the founding father of Kubernetes himself: &lt;a href=&#34;https://twitter.com/kelseyhightower&#34;&gt;Mr. Kelsey Hightower&lt;/a&gt;. The lesson is known as &lt;a href=&#34;https://github.com/kelseyhightower/kubernetes-the-hard-way&#34;&gt;“Kubernetes The Hard Way”&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For this blog entry I would like to take a less demanding approach than Kubernetes The Hard Way, while still being educational. I would like to highlight only the major steps in creating a Kubernetes cluster and what is covered in &lt;a href=&#34;https://training.linuxfoundation.org/certification/certified-kubernetes-administrator-cka/&#34;&gt;CKA (Certified Kubernetes Administrator) exams&lt;/a&gt;. Thus we are going to use the &lt;code&gt;kubeadm&lt;/code&gt; tools to build the Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;The steps of creating a Kubernetes cluster are hidden to you if you are using a Kubernetes as a service such as AWS EKS, GCP GKE or the enterprise suites of Kubernetes such as Red Hat Openshift or VMware Tanzu. All of these products let you use Kubernetes without the need to worry about creating it.&lt;/p&gt;
&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;For this tutorial we will need the following from AWS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An active AWS account&lt;/li&gt;
&lt;li&gt;EC2 instances with Amazon Linux 2 as the OS&lt;/li&gt;
&lt;li&gt;AWS Keys for SSH to access control node and managed nodes&lt;/li&gt;
&lt;li&gt;Security group which allows SSH and HTTP&lt;/li&gt;
&lt;li&gt;A decent editor such as Vim or Notepad++ to create the inventory and the playbook&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;ec2-instances-provisioning&#34;&gt;EC2 Instances provisioning&lt;/h3&gt;
&lt;p&gt;Provisioning of the the control plane, a.k.a. the master node:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to AWS Console → EC2 → Launch Instances.&lt;/li&gt;
&lt;li&gt;Set the Name tag to &lt;code&gt;Master&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the Amazon Linux 2 AMI.&lt;/li&gt;
&lt;li&gt;Select a key pair. If there are no available key pairs, please create one according to Amazon’s instructions.&lt;/li&gt;
&lt;li&gt;Allow SSH and 6443 TCP ports.&lt;/li&gt;
&lt;li&gt;Set Number of Instances to 1.&lt;/li&gt;
&lt;li&gt;Click Launch Instance.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Provisioning of the worker nodes, a.k.a. the minions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to AWS Console → EC2 → Launch Instances.&lt;/li&gt;
&lt;li&gt;Set the Name tag to &lt;code&gt;Node&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the Amazon Linux 2 AMI.&lt;/li&gt;
&lt;li&gt;Select a key pair. If there are no available key pairs, please create one according to Amazon’s instructions.&lt;/li&gt;
&lt;li&gt;Allow SSH TCP port.&lt;/li&gt;
&lt;li&gt;Set Number of Instances to 2.&lt;/li&gt;
&lt;li&gt;Click Launch Instance.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;installing-the-container-runtime&#34;&gt;Installing the container runtime&lt;/h3&gt;
&lt;p&gt;All Kubernetes nodes require some sort of container runtime engine. For these nodes we are going to use Docker. Log in to all EC2 instances and execute the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install Docker.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo yum update -y
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo amazon-linux-extras install docker -y
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo usermod -a -G docker ec2-user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo service docker start
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable docker.service
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo su - ec2-user&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify the Docker installation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We should get an empty Docker status:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONTAINER ID   IMAGE          COMMAND                  CREATED       STATUS      PORTS                    NAMES&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install TC (Traffic Controller). This is required by the kubeadm tool.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo yum install tc -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;kubernetes-control-plane-setup&#34;&gt;Kubernetes control plane setup&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the Kubernetes repository. Log in to the node and paste the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &amp;lt;&amp;lt;&amp;#39;EOF&amp;#39; | sudo tee /etc/yum.repos.d/kubernetes.repo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[kubernetes]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;name=Kubernetes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-$basearch
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpgcheck=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exclude=kubelet kubeadm kubectl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EOF&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the Kubernetes binaries for Control Plane (&lt;code&gt;kubelet&lt;/code&gt;, &lt;code&gt;kubeadm&lt;/code&gt;, &lt;code&gt;kubectl&lt;/code&gt;) and enable it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable --now kubelet&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Initiate the Control Plane. The &lt;code&gt;--ignore-preflight-errors&lt;/code&gt; switch is required because we are using a system which has fewer than 2 CPUs and less than 2 GB of RAM. The &lt;code&gt;--pod-network-cidr&lt;/code&gt; value is the default value for flannel (a networking add-on).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo kubeadm init --ignore-preflight-errors=NumCPU,Mem --pod-network-cidr=10.244.0.0/16&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are 3 important points from the output of this command. They are the successful note on the cluster initalization, the kubeconfig setup and the worker node joining string. The following is a sample output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/10/kubernetes-from-the-ground-up-with-aws-ec2/kubeadm01.webp&#34; alt=&#34;The output of kubeadm, with the three important points highlighted. They read: 1: &amp;ldquo;Your Kubernetes control-lane as been initialized successfully!&amp;rdquo;, 2: &amp;ldquo;mkdir -p $HOME/.kube;
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config;
sudo chown $(id -u):$(id -g) $HOME/.kube/config&amp;rdquo;, 3: &amp;ldquo;kubeadm join 172.XX.XX.XX:6643 &amp;ndash;token XXXX &amp;ndash;discover-token-ca-cert-hash XX&amp;rdquo;&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the configuration file for kubectl a.k.a. kubeconfig to connect to the Kubernetes cluster. The scripts are from previous output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p $HOME/.kube
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo chown $(id -u):$(id -g) $HOME/.kube/config&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the pod network add-on. We are going to use &lt;a href=&#34;https://github.com/flannel-io/flannel&#34;&gt;flannel&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;kubernetes-worker-nodes-setup&#34;&gt;Kubernetes worker nodes setup&lt;/h3&gt;
&lt;p&gt;Execute the following in all worker nodes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the Kubernetes repository. Log in to the node and paste the following&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &amp;lt;&amp;lt;&amp;#39;EOF&amp;#39; | sudo tee /etc/yum.repos.d/kubernetes.repo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[kubernetes]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;name=Kubernetes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-$basearch
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpgcheck=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exclude=kubelet kubeadm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EOF&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the Kubernetes binaries for worker nodes (kubelet, kubeadm) and enable kubelet.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo yum install -y kubelet kubeadm --disableexcludes=kubernetes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable --now kubelet&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Execute the join command with &lt;code&gt;sudo&lt;/code&gt;. This command is from step #3 in the Kubernetes Control Plane Setup section.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/10/kubernetes-from-the-ground-up-with-aws-ec2/kubeadm-join.webp&#34; alt=&#34;A command and its results: sudo kubeadm join 172.XX.XX.XX:6443 &amp;ndash;token XXXX &amp;ndash;discovery-token-ca-cert-hash sha256:4XXX&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;hello-kubernetes-&#34;&gt;Hello, Kubernetes :)&lt;/h3&gt;
&lt;p&gt;We have successfully created a Kubernetes cluster. Let’s check on the cluster and try to deploy some sample applications.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the latest status of the nodes. You might need to wait a minute or more for all nodes to become &lt;code&gt;Ready&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl get nodes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sample output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/10/kubernetes-from-the-ground-up-with-aws-ec2/kubeadm02.webp&#34; alt=&#34;Results of the kubectl get nodes. 3 nodes appear, each with the Ready status.&#34;&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Deploy a sample Nginx web server&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl create deployment mynginx --image=nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Scale the Deployment to have 6 replicas and check on where the pods run. The pods should be assigned randomly to the available worker nodes.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl scale --replicas=6 deployment/mynginx
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl get pods -o wide&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sample output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/10/kubernetes-from-the-ground-up-with-aws-ec2/kubeadm03.webp&#34; alt=&#34;Results of the kubectl get nodes and kubectl get pods. The two worker nodes are highlighted, pointing to their coinciding output from the command &amp;ldquo;kubectl get pods -o wide&amp;rdquo;.&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;That&amp;rsquo;s all, folks. I hope this blog entry has shed some insights on what it takes to create a Kubernetes cluster. Have a nice day :)&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>CI/CD with Azure DevOps</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2022/08/cicd-with-azure-devops/"/>
      <id>https://www.endpointdev.com/blog/2022/08/cicd-with-azure-devops/</id>
      <published>2022-08-28T00:00:00+00:00</published>
      <author>
        <name>Dylan Wooters</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/moonrise-in-sibley.webp&#34; alt=&#34;Moonrise in Sibley Volcanic Park. The sun casts a shadow over everything but the tops of the trees and a brown, grassy hill. The moon has risen just above the hill, against the backdrop of a light blue cloudless evening sky.&#34;&gt;&lt;br&gt;
Photo by Dylan Wooters, 2022.&lt;/p&gt;
&lt;p&gt;A development process that includes manual builds, tests, and deployments can work for a small-scale project, but as your codebase and team grow, it can quickly become time-consuming and unwieldy. This can be particularly true if you’re a .NET developer. We all know the struggle of merging in the latest feature and clicking build in Visual Studio, only to have it fail, citing cryptic errors. “Maybe a clean will fix it?”&lt;/p&gt;
&lt;p&gt;If this sounds like your current situation, it’s likely time to consider building a Continuous Integration and Continuous Deployment pipeline, commonly known as “CI/CD”. A good CI/CD pipeline will help you automate the painful areas of building, testing, and deploying your code, as well as help to enforce best practices like pull requests and build verification.&lt;/p&gt;
&lt;p&gt;There are many great options to choose from when selecting a CI/CD tool. There are self-hosted options like &lt;a href=&#34;https://www.jenkins.io/&#34;&gt;Jenkins&lt;/a&gt; and &lt;a href=&#34;https://www.jetbrains.com/teamcity/&#34;&gt;TeamCity&lt;/a&gt;. There are also providers like &lt;a href=&#34;https://github.com/features/actions&#34;&gt;GitHub&lt;/a&gt; and Azure DevOps, which offer CI/CD alongside cloud-hosted source control. All of these options have pros and cons, but if you’re looking for a large feature set, flexibility, and in particular good support for .NET solutions, you should consider Azure DevOps.&lt;/p&gt;
&lt;p&gt;In this post, I’ll show you how to set up an Azure CI/CD pipeline for a self-hosted .NET MVC web solution targeting two different environments.&lt;/p&gt;
&lt;h3 id=&#34;creating-environments&#34;&gt;Creating Environments&lt;/h3&gt;
&lt;p&gt;The first step is to sign up for a free account at &lt;a href=&#34;https://azure.microsoft.com/en-us/services/devops/&#34;&gt;Azure DevOps&lt;/a&gt;. Once you have your account created, you can then create your Environments. The Environments are the places where you want your app to be deployed.&lt;/p&gt;
&lt;p&gt;In the case of a recent project here at End Point Dev, we had three different environments—UAT, Stage, and Production—all running as IIS websites on self-hosted Windows VMs. The UAT environment was on the UAT server, and the Stage and Production environments were on the Production server. So you’ll want to plan out your environments accordingly.&lt;/p&gt;
&lt;p&gt;Once you do so, head to Azure DevOps and then click on Environments under the Pipelines section, then click Create Environment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/create-env.webp&#34; alt=&#34;Azure DevOps. A menu in the drawer on the left has Pipelines expanded, with Environments selected in its sub-menu. On the right, outside the menu, is a button that reads Create environment.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Then enter a name from the environment and select Virtual Machines and click Next.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/new-env.webp&#34; alt=&#34;A dialog box called &amp;ldquo;New environment.&amp;rdquo; The Name field is filled out as &amp;ldquo;UAT,&amp;rdquo; and the Description field is filled out with &amp;ldquo;Testing environment.&amp;rdquo; The Resource field has three radio buttons, with Virtual machines selected. There is a &amp;ldquo;Next&amp;rdquo; button at the bottom of the dialog, which is highlighted.&#34;&gt;&lt;/p&gt;
&lt;p&gt;In the following window, select Generic Provider, and then select your OS, which in our case is Windows. You will see a registration script with a copy icon next to it. Click the icon to copy the (rather lengthy) PowerShell registration script to the clipboard, and then paste it into a text file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/registration-script.webp&#34; alt=&#34;The same &amp;ldquo;New environment&amp;rdquo; dialog. Under the &amp;ldquo;Virtual machine resource&amp;rdquo; section, &amp;ldquo;Provider&amp;rdquo; has &amp;ldquo;Generic provider&amp;rdquo; selected, &amp;ldquo;Operating system&amp;rdquo; has &amp;ldquo;Windows&amp;rdquo; selected, and under &amp;ldquo;Registration script&amp;rdquo; is a copy icon with instructions to run it in PowerShell.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Next, connect to the target environment. Open a PowerShell window as Administrator, copy and paste the registration script, and then press Enter. The PowerShell script will then do its magic and register the environment with Azure. It may take a minute to run, but afterwards you should see a success message.&lt;/p&gt;
&lt;p&gt;Now, if you head back to Azure DevOps and click on the Environment, you should see the server name of the machine that you ran the PowerShell script on. The server name is referred to as a Resource in Azure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/uat-env.webp&#34; alt=&#34;The UAT Environment, Resources tab. It shows a server with a redacted name and a latest job ID with a green check mark.&#34;&gt;&lt;/p&gt;
&lt;p&gt;You will then want to complete the above steps for each of your additional target environments, for example, Staging and Production. In our case, Staging and Production are hosted on the same web server, so we only need to create one additional environment (Production) in Azure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/prod-env.webp&#34; alt=&#34;The Production Enviroment, Resources tab.&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;creating-the-pipelines&#34;&gt;Creating the Pipelines&lt;/h3&gt;
&lt;p&gt;Now it’s time to create the actual Pipeline. The Pipeline will be responsible for building and deploying your app to the Environments that you created in the previous step. To start, Click on Pipelines in Azure DevOps, and then click the Create Pipeline button.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/create-pipeline.webp&#34; alt=&#34;Azure DevOps. A menu in the drawer on the left has Pipelines expanded, with Pipelines selected in its sub-menu. Outside the menu is a button that reads Create Pipeline.&#34;&gt;&lt;/p&gt;
&lt;p&gt;You’ll then be asked where your source code lives. In our case, the source code exists in a repo in Azure DevOps. The nice thing about Azure is that you can also target source code that exists on another provider, for example, GitHub, Bitbucket, or even a self-hosted Git repo (Other Git). Click on your hosting provider and follow the instructions.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;re connected to your source provider, then you can configure the Pipeline. The “Pipeline” is actually just a YAML file within your target repo. You can read all about the Azure YAML syntax &lt;a href=&#34;https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/?view=azure-pipelines&#34;&gt;here&lt;/a&gt;. We’ll choose a Starter Pipeline, which opens up a text editor and enables you to create your YAML file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/configure-your-pipeline.webp&#34; alt=&#34;Azure DevOps. A menu in the drawer on the left has Pipelines expanded. On the right a configure tab is selcted. A heading reads &amp;ldquo;Configure your pipeline,&amp;rdquo; under which are a couple options. highlighted is &amp;ldquo;Starter pipeline.&amp;rdquo;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Delete the sample text that appears in the editor, and then enter the following YAML. This is a pre-baked YAML pipeline that restores and builds a .NET web project, and then publishes the build assets to your three different target environments. In the next section, we will take a deep dive into how the YAML works so that you can edit it to fit your needs.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;trigger&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- uat&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- staging&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- main&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pool&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;vmImage&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;windows-latest&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;variables&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;projectPath&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ci_tutorial/ci_tutorial_web.csproj&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;packageName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ci_tutorial_web.zip&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;solution&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;**/*.sln&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;buildPlatform&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;AnyCPU&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;artifactName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;AzureDrop&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;${{ if eq(variables[&amp;#39;Build.SourceBranchName&amp;#39;], &amp;#39;uat&amp;#39;) }}:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;websiteName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ci_tutorial_uat&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;buildConfiguration&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UAT&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environmentName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UAT&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;targetVM&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UATWEBAPP1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;${{ if eq(variables[&amp;#39;Build.SourceBranchName&amp;#39;], &amp;#39;staging&amp;#39;) }}:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;websiteName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ci_tutorial_staging&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;buildConfiguration&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Staging&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environmentName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Production&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;targetVM&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;WEBAPP1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;${{ if eq(variables[&amp;#39;Build.SourceBranchName&amp;#39;], &amp;#39;main&amp;#39;) }}:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;websiteName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ci_tutorial_prod&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;buildConfiguration&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Release&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environmentName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Production&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;targetVM&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;WEBAPP1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;stages&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;stage&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;build&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;jobs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;job&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;RestoreAndBuild&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;steps&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;task&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;NuGetToolInstaller@1&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;task&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;NuGetCommand@2&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;inputs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;restoreSolution&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(solution)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;task&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;VSBuild@1&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;inputs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;solution&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(projectPath)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;msbuildArgs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation=&amp;#34;$(build.artifactStagingDirectory)&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;platform&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(buildPlatform)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;configuration&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(buildConfiguration)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;#- task: VSTest@2&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;#  inputs:&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;#    platform: &amp;#39;$(buildPlatform)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;#    configuration: &amp;#39;$(buildConfiguration)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;task&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;PublishBuildArtifacts@1&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;inputs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;PathtoPublish&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(Build.ArtifactStagingDirectory)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ArtifactName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(artifactName)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;publishLocation&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Container&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;stage&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Deploy&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;displayName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Deploy to IIS&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;dependsOn&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Build&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;jobs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;deployment&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;DeploytoIIS&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;displayName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Deploy the web application to dev environment&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environment&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;${{ variables.environmentName }}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;resourceName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;${{ variables.targetVM }}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;resourceType&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;VirtualMachine&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strategy&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;runOnce&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;deploy&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;steps&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;          &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;task&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;DownloadBuildArtifacts@0&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;inputs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;buildType&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;current&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;downloadType&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;specific&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;downloadPath&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(System.ArtifactsDirectory)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;          &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;task&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;IISWebAppManagementOnMachineGroup@0&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;displayName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Create App Pool and Website&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;inputs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;WebsiteName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(websiteName)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;WebsitePhysicalPath&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;%SystemDrive%\inetpub\wwwroot\$(websiteName)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;CreateOrUpdateAppPoolForWebsite&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;AppPoolNameForWebsite&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(websiteName)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# For testing, to confirm target website&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;          &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;task&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;PowerShell@2&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;displayName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Display target websitename&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;inputs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;targetType&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;inline&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Write-Host &amp;#34;Target Website Name: $(websiteName)&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;          &lt;/span&gt;- &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;task&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;IISWebAppDeploymentOnMachineGroup@0&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;displayName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Deploy IIS Website&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;inputs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;WebSiteName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(websiteName)&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;              &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Package&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$(System.ArtifactsDirectory)\$(artifactName)\$(packageName)&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;understanding-the-yaml&#34;&gt;Understanding the YAML&lt;/h3&gt;
&lt;p&gt;The first section of the YAML, &lt;code&gt;trigger&lt;/code&gt;, determines which branches will trigger the pipeline when they receive a push. In our case, we have three branches: &lt;code&gt;uat&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, and &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;trigger&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- uat&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- staging&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;- main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;variables&lt;/code&gt; section defines variables that are used later in the pipeline YAML. An important part of this section is the use of &lt;code&gt;if&lt;/code&gt; statements to assign values to certain variables (i.e., &lt;code&gt;environmentName&lt;/code&gt;) based on the source branch for the build. &lt;strong&gt;This mechanism allows the pipeline to deploy to several different environments using a single YAML file.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Note that the &lt;code&gt;if&lt;/code&gt; statements also switch the build configuration. If you have config transforms, this will be crucial for ensuring that the correct config files are deployed to each environment.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;${{ if eq(variables[&amp;#39;Build.SourceBranchName&amp;#39;], &amp;#39;uat&amp;#39;) }}:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;websiteName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ci_tutorial_uat&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;buildConfiguration&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UAT&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environmentName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UAT&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;targetVM&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UATWEBAPP1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;${{ if eq(variables[&amp;#39;Build.SourceBranchName&amp;#39;], &amp;#39;staging&amp;#39;) }}:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;websiteName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ci_tutorial_staging&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;buildConfiguration&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Staging&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environmentName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Production&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;targetVM&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;WEBAPP1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;${{ if eq(variables[&amp;#39;Build.SourceBranchName&amp;#39;], &amp;#39;main&amp;#39;) }}:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;websiteName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ci_tutorial_prod&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;buildConfiguration&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Release&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environmentName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Production&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;targetVM&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;WEBAPP1&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, we get into the actual actions, or stages, of the pipeline. This pipeline has two stages: build and deploy. The build stage runs a NuGet restore and then a VS build, and then publishes the build output/​artifacts to an Azure cloud storage location. The last bit is something that took me a while to wrap my head around: &lt;strong&gt;The build does not occur on the target environment via the runner—it actually occurs up in “Azure land”, on a Windows cloud VM.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The VM on which the build occurs can be defined using the &lt;code&gt;pool: vmImage&lt;/code&gt; section. In our case, we are running the build on &lt;code&gt;windows-latest&lt;/code&gt;, which currently translates to the brand new Windows Server 2022.&lt;/p&gt;
&lt;p&gt;The deploy stage then downloads the build artifacts from Azure and deploys them to IIS on the target machine, using the runner that you installed as part of the environment setup above. Note that the variables defined in the &lt;code&gt;if&lt;/code&gt; statements come into play here&amp;mdash;this is how the pipeline knows which environment/VM to target.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;environment&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;${{ variables.environmentName }}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;resourceName&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;${{ variables.targetVM }}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;resourceType&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;VirtualMachine&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;IISWebAppManagementOnMachineGroup&lt;/code&gt; task will automatically create an app pool and website in IIS for the application. This feature can be toggled using the &lt;code&gt;CreateOrUpdateAppPoolForWebsite&lt;/code&gt; setting. If a website already exists based on the WebsiteName, Azure will simply deploy to that existing app pool and website.&lt;/p&gt;
&lt;h3 id=&#34;adding-branch-protection-and-build-validation&#34;&gt;Adding Branch Protection and Build Validation&lt;/h3&gt;
&lt;p&gt;In order to ensure successful deployments to Production, you will likely want to add in branch protection for your main or master branch, and also consider build validation.&lt;/p&gt;
&lt;p&gt;Branch protection allows you to enforce reviews for pull requests into certain branches. Build validation is a feature that pre-compiles your .NET code to ensure that it builds prior to merging a pull request. Both features help to improve code quality and prevent broken deployments.&lt;/p&gt;
&lt;p&gt;To add branch protection, go to Repo → Branches → &lt;em&gt;your target branch&lt;/em&gt; → &lt;em&gt;right side menu&lt;/em&gt;, then select Branch Policies.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/branch-policies.webp&#34; alt=&#34;Under the Branches header, the &amp;ldquo;main&amp;rdquo; branch has a menu expanded on the far right of its row. Highlighted is &amp;ldquo;Branch policies.&amp;rdquo;&#34;&gt;&lt;/p&gt;
&lt;p&gt;To enforce reviews, turn on “Require a minimum number of reviewers”, and adjust the number of reviewers and settings as required.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/minimum-reviewers.webp&#34; alt=&#34;A dialog box titled &amp;ldquo;Branch Policies.&amp;rdquo; A switch next to &amp;ldquo;Require a minimum number of reviewers&amp;rdquo; is turned on.&#34;&gt;&lt;/p&gt;
&lt;p&gt;For build validation, scroll down to the Build Validation section, and click the plus button to add a new policy. Select the target build pipeline, adjust the policy settings as necessary (see below), and then give it a display name. Click save, and the build validation policy will be applied to the branch.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An important note here is that the build validation essentially runs the CI pipeline. In our case, that means it will run the build and the deployment. This is not ideal; we only want it to run the build (NuGet restore and VS build). So, it’s advisable to create a separate pipeline with only the build stage. This can be done easily by copying our example YAML and removing the “Deploy” stage, then saving it as a new pipeline. Then, choose that new pipeline as the target build pipeline in the validation policy.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/add-build-policy.webp&#34; alt=&#34;A header reads &amp;ldquo;Add build policy.&amp;rdquo; under that is a form, with &amp;ldquo;Build pipeline (required)&amp;rdquo; highlighted. Highlight text reads &amp;ldquo;Add your new &amp;lsquo;build-only&amp;rsquo; pipeline here.&amp;rdquo; &amp;ldquo;ci-tutorial&amp;rdquo; is selected.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now when you open up a PR, it can only be merged once a review has been applied, and the build validation has run successfully.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/pr-view.webp&#34; alt=&#34;Pull request view, &amp;ldquo;Overview&amp;rdquo; tab. Next to green check marks are messages saying &amp;ldquo;Required check succeeded&amp;rdquo; with the successful build validation under it, and &amp;ldquo;1 reviewer approved.&amp;rdquo;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;monitoring-and-troubleshooting-the-pipeline&#34;&gt;Monitoring and Troubleshooting the Pipeline&lt;/h3&gt;
&lt;p&gt;You can see the overall status of your pipeline by clicking on the Pipelines section in the left-side navigation. Green means good!&lt;/p&gt;
&lt;p&gt;To dive into more details, click on the pipeline name. This will show all the recent pipeline runs. Then click on a particular run. This will bring up the run details, including a flowchart for each stage.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/pipeline-flowchart.webp&#34; alt=&#34;Pipeline flowchart, with a completed &amp;ldquo;build&amp;rdquo; leading into a completed &amp;ldquo;Deploy to IIS.&amp;rdquo;&#34;&gt;&lt;/p&gt;
&lt;p&gt;To dive further, click on a specific stage. You’ll then see all of the tasks in the stage, with actual output details on the right, similar to what you would see in the output window in Visual Studio.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/08/cicd-with-azure-devops/stage-details.webp&#34; alt=&#34;Stage details. On the left are the successful completed jobs from the &amp;ldquo;build&amp;rdquo; and &amp;ldquo;Deploy to IIS&amp;rdquo; sections. On the right is more verbose output for a selected job.&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you encounter an error in your pipeline, the stage details page is a great place to troubleshoot.&lt;/strong&gt; Azure will show you the task that generated the error by displaying a red error icon next to the task. You can then click on the task and trace the output on the right to determine the exact error message.&lt;/p&gt;
&lt;h3 id=&#34;wrapping-up&#34;&gt;Wrapping Up&lt;/h3&gt;
&lt;p&gt;Hopefully, this post gives you a good overview of CI/CD options for a self-hosted .NET application. Azure is almost infinitely configurable, so there are many other options to explore that are not covered in this post, whether it be within the pipeline YAML, or through other settings like branch policies/​protection. If you have any questions, please feel free to leave a comment. Happy automating!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>DevOps &amp; Kubernetes engineer job opening</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/11/devops-kubernetes-engineer-job/"/>
      <id>https://www.endpointdev.com/blog/2021/11/devops-kubernetes-engineer-job/</id>
      <published>2021-11-04T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2021/11/devops-kubernetes-engineer-job/20210811-195659.jpg&#34; alt=&#34;dumpster with worn sticker warning against sleeping or falling&#34; /&gt;
&lt;!-- Photo by Jon Jensen --&gt;
&lt;p&gt;We are looking for a full-time, salaried DevOps / Kubernetes engineer to work on cloud hosting projects with our clients and our internal hosting team.&lt;/p&gt;
&lt;p&gt;End Point Dev is an Internet technology consulting company based in New York City, with 50 employees serving many clients ranging from small family businesses to large corporations. We are going strong after 26 years in business!&lt;/p&gt;
&lt;p&gt;Even before the pandemic most of us worked remotely from home offices. We collaborate using SSH, Git, project tracking tools, Zulip chat, video conferencing, and of course email and phones.&lt;/p&gt;
&lt;h3 id=&#34;what-you-will-be-doing&#34;&gt;What you will be doing:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Automate, set up, support, and maintain complex containerized applications in the cloud.&lt;/li&gt;
&lt;li&gt;Audit and improve security, backups, reliability, monitoring.&lt;/li&gt;
&lt;li&gt;Work together with End Point Dev co-workers and our clients’ in-house staff.&lt;/li&gt;
&lt;li&gt;Use your desktop operating system of choice: Linux, macOS, or Windows.&lt;/li&gt;
&lt;li&gt;Work with open source software and contribute back as opportunity arises.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;youll-need-professional-experience-with&#34;&gt;You&amp;rsquo;ll need professional experience with:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Production Kubernetes administration on Amazon EKS (2+ years)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Linux and common distributions including Ubuntu, RHEL/​CentOS&lt;/li&gt;
&lt;li&gt;Public clouds such as AWS, GCP, Azure, Linode, DigitalOcean&lt;/li&gt;
&lt;li&gt;Containerization with Docker and/or Podman&lt;/li&gt;
&lt;li&gt;IaC tools such as Terraform, CloudFormation, Ansible, Chef, Puppet, Salt&lt;/li&gt;
&lt;li&gt;CI/CD pipelines, release management&lt;/li&gt;
&lt;li&gt;Git version control, GitHub or GitLab&lt;/li&gt;
&lt;li&gt;Scripting and programming with bash, Python, Ruby, Go, etc.&lt;/li&gt;
&lt;li&gt;OS fundamentals, networking, and firewalls&lt;/li&gt;
&lt;li&gt;HTTP, REST APIs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;you-have-these-important-work-traits&#34;&gt;You have these important work traits:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Strong verbal and written communication skills&lt;/li&gt;
&lt;li&gt;An eye for detail&lt;/li&gt;
&lt;li&gt;Tenacity in solving problems and focusing on customer needs&lt;/li&gt;
&lt;li&gt;A feeling of ownership of your projects&lt;/li&gt;
&lt;li&gt;Work both independently and as part of a team&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-work-here-offers&#34;&gt;What work here offers:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Collaboration with knowledgeable, friendly, helpful, and diligent co-workers around the world&lt;/li&gt;
&lt;li&gt;Freedom from being tied to an office location&lt;/li&gt;
&lt;li&gt;Flexible, sane work hours&lt;/li&gt;
&lt;li&gt;Paid holidays and vacation&lt;/li&gt;
&lt;li&gt;Annual bonus opportunity&lt;/li&gt;
&lt;li&gt;(For U.S. employees:) Health insurance subsidy and 401(k) retirement savings plan&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;get-in-touch-with-us&#34;&gt;Get in touch with us:&lt;/h3&gt;
&lt;p&gt;&lt;del&gt;Please email us an introduction to &lt;a href=&#34;mailto:jobs@endpointdev.com&#34;&gt;jobs@endpointdev.com&lt;/a&gt; to apply.&lt;/del&gt;
&lt;strong&gt;(This job has been filled.)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Include your location, a resume/​CV, your Git repository or LinkedIn URLs, and whatever else may help us get to know you.&lt;/p&gt;
&lt;p&gt;We look forward to hearing from you! Direct work seekers only, please—​this role is not for agencies or subcontractors.&lt;/p&gt;
&lt;p&gt;We are an equal opportunity employer and value diversity at our company. We do not discriminate on the basis of sex/​gender, race, religion, color, national origin, sexual orientation, age, marital status, veteran status, or disability status.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Installing Ubuntu 18.04 to a different partition from an existing Ubuntu installation</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/04/install-ubuntu-to-different-partition/"/>
      <id>https://www.endpointdev.com/blog/2020/04/install-ubuntu-to-different-partition/</id>
      <published>2020-04-06T00:00:00+00:00</published>
      <author>
        <name>Bharathi Ponnusamy</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/04/install-ubuntu-to-different-partition/banner.jpg&#34; alt=&#34;Clean setup&#34;&gt;&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;http://web.archive.org/web/20200208071440/https://unsplash.com/@patrykgradyscom&#34;&gt;Patryk Grądys&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Our Liquid Galaxy systems are running on Ubuntu 14.04 LTS (Trusty). We decided to upgrade them to Ubuntu 18.04 LTS (Bionic) since Ubuntu 14.04 LTS reached its end of life on April 30, 2019.&lt;/p&gt;
&lt;h3 id=&#34;upgrading-from-ubuntu-1404-lts&#34;&gt;Upgrading from Ubuntu 14.04 LTS&lt;/h3&gt;
&lt;p&gt;The recommended way to upgrade from Ubuntu 14.04 LTS is to first upgrade to 16.04 LTS, then to 18.04 LTS, which will continue to receive support until April 2023:&lt;/p&gt;
&lt;p&gt;14.04 LTS → 16.04 LTS → 18.04 LTS&lt;/p&gt;
&lt;p&gt;Ubuntu has LTS → LTS upgrades, allowing you to skip intermediate non-LTS releases, but we can’t skip intermediate LTS releases; we have to go via 16.04, unless we want to do a fresh install of 18.04 LTS.&lt;/p&gt;
&lt;p&gt;For a little more longevity, we decided to do a fresh install of Ubuntu 18.04 LTS. Not only is this release supported into 2023 but it will offer a direct upgrade route to Ubuntu 20.04 LTS when it’s released in April 2020.&lt;/p&gt;
&lt;h3 id=&#34;installing-clean-ubuntu-1804-lts-from-ubuntu-1404-lts&#34;&gt;Installing Clean Ubuntu 18.04 LTS from Ubuntu 14.04 LTS&lt;/h3&gt;
&lt;h4 id=&#34;install-debootstrap&#34;&gt;Install debootstrap&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&#34;https://linux.die.net/man/8/debootstrap&#34;&gt;debootstrap&lt;/a&gt; utility installs a very minimal Debian system. Debootstrap will install a Debian-based OS into a sub-directory. You don’t need an installation CD for this. However, you need to have access to the corresponding Linux distribution repository (e.g. Debian or Ubuntu).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apt-get -y install debootstrap&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-a-new-root-partition&#34;&gt;Creating a new root partition&lt;/h4&gt;
&lt;p&gt;Create a logical volume with size 12G and format the filesystem to ext4:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lvcreate -L12G -n ROOT_VG/ROOT_VOLUME
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkfs.ext4 /dev/ROOT_VG/ROOT_VOLUME&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;mounting-the-new-root-partition&#34;&gt;Mounting the new root partition&lt;/h4&gt;
&lt;p&gt;Mount the partition at &lt;code&gt;/mnt/root18&lt;/code&gt;. This will be the root (/) of your new system.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p /mnt/root18
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mount /dev/ROOT_VG/ROOT_VOLUME /mnt/root18&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;bootstrapping-the-new-root-partition&#34;&gt;Bootstrapping the new root partition&lt;/h4&gt;
&lt;p&gt;Debootstrap can download the necessary files directly from the repository. You can substitute any Ubuntu archive mirror for &lt;code&gt;ports.ubuntu.com/ubuntu-ports&lt;/code&gt; in the command example below. Mirrors are listed &lt;a href=&#34;https://wiki.ubuntu.com/Mirrors&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Replace $ARCH below with your architecture: amd64, arm64, armhf, i386, powerpc, ppc64el, or s390x.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;debootstrap --arch &lt;span style=&#34;color:#369&#34;&gt;$ARCH&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$DISTRO&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$ROOT_MOUNTPOINT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;debootstrap --arch amd64 bionic /mnt/root18&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;installing-fstab&#34;&gt;Installing fstab&lt;/h4&gt;
&lt;p&gt;This just changes the root (/) partition path in the new installation while keeping the &lt;code&gt;/boot&lt;/code&gt; partition intact. For example, &lt;code&gt;/dev/mapper/headVG-root /&lt;/code&gt; → &lt;code&gt;/dev/mapper/headVG-root18 /&lt;/code&gt;. Since device names are not guaranteed to be the same after rebooting or when a new device is connected, we use UUIDs (Universally Unique Identifiers) to refer to partitions in fstab. We don’t need to use UUIDs for logical volumes since their device names won’t change.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;OLD_ROOT_PATH&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;awk &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;$2 == &amp;#34;/&amp;#34; { print $1 }&amp;#39;&lt;/span&gt; /etc/fstab&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sed &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;s:^&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;OLD_ROOT_PATH&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;\s:/dev/mapper/headVG-root18 :&amp;#34;&lt;/span&gt; /etc/fstab &amp;gt; /mnt/root18/etc/fstab&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;mounting-things-in-the-new-root-partition&#34;&gt;Mounting things in the new root partition&lt;/h4&gt;
&lt;p&gt;Bind &lt;code&gt;/dev&lt;/code&gt; to the new location, then mount &lt;code&gt;/sys&lt;/code&gt;, &lt;code&gt;/proc&lt;/code&gt;, and &lt;code&gt;/dev/pts&lt;/code&gt; from your host system to the target system.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mount --bind /dev /mnt/root18/dev
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mount -t sysfs none /mnt/root18/sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mount -t proc none /mnt/root18/proc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mount -t devpts none /mnt/root18/dev/pts&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;configuring-apt&#34;&gt;Configuring apt&lt;/h4&gt;
&lt;p&gt;Debootstrap will have created a very basic &lt;code&gt;/mnt/root18/etc/apt/sources.list&lt;/code&gt; that will allow installing additional packages. However, I suggest that you add some additional sources, such as the following, for source packages and security updates:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;deb http://us.archive.ubuntu.com/ubuntu bionic main universe
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;deb-src http://us.archive.ubuntu.com/ubuntu bionic main universe
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;deb http://security.ubuntu.com/ubuntu bionic-security main universe
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;deb-src http://security.ubuntu.com/ubuntu bionic-security main universe&amp;#34;&lt;/span&gt; &amp;gt; /mnt/root18/etc/apt/sources.list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make sure to run &lt;code&gt;apt update&lt;/code&gt; with &lt;code&gt;chroot&lt;/code&gt; after you have made changes to the target system sources list.&lt;/p&gt;
&lt;p&gt;Now we’ve got a real Ubuntu system, if a rather small one, on disk. &lt;code&gt;chroot&lt;/code&gt; into it to set up the base configurations.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;LANG&lt;/span&gt;=C.UTF-8 chroot /mnt/root18 /bin/bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;installing-required-packages-and-running-chef-client&#34;&gt;Installing required packages and running chef-client&lt;/h3&gt;
&lt;p&gt;As we are maintaining most of the Liquid Galaxy configuration and packages with &lt;a href=&#34;https://www.chef.io/&#34;&gt;Chef&lt;/a&gt;, we need to install chef-client, configure it on the new target system, and run chef-client to complete the setup.&lt;/p&gt;
&lt;p&gt;Copy the chef configuration and persistent net udev rules into place:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp -a /etc/chef /mnt/root18/etc/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp /etc/udev/rules.d/70-persistent-net.rules /mnt/root18/etc/udev/rules.d/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Install and run chef-client and let it set up our user login:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;&amp;lt;EOF | chroot /mnt/root18
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;apt-get update &amp;amp;&amp;amp; apt-get install -y curl wget
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;curl -L https://omnitruck.chef.io/install.sh | bash -s -- -v 12.5.1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;chef-client -E production_trusty -o &amp;#39;recipe[users]&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;EOF&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, chroot and install the required packages:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;&amp;lt;EOF | chroot /mnt/root18
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;mount /boot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;apt-get update &amp;amp;&amp;amp; apt-get install -y --no-install-recommends linux-image-generic lvm2 openssh-server ifupdown net-tools
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;locale-gen en_US.UTF-8
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;EOF&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;set-ubuntu-1404-to-boot-default&#34;&gt;Set Ubuntu 14.04 to boot default&lt;/h3&gt;
&lt;p&gt;Create file &lt;code&gt;42_custom_template_trusty&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Entry for trusty system&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;menuentry &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;TRUSTY&amp;#39;&lt;/span&gt; --class liquid --class gnu-linux --class gnu --class os {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;# Skipped lines&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        linux   /trusty/vmlinuz-&lt;span style=&#34;color:#369&#34;&gt;$trusty_kernel_version&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;root&lt;/span&gt;=/dev/mapper/headVG-root ro nomodeset &lt;span style=&#34;color:#369&#34;&gt;biosdevname&lt;/span&gt;=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; modprobe.blacklist=gma500_gfx quiet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        initrd  /trusty/initrd.img-&lt;span style=&#34;color:#369&#34;&gt;$trusty_kernel_version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create file &lt;code&gt;42_custom_template_bionic&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Entry for bionic system&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;menuentry &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;BIONIC&amp;#39;&lt;/span&gt; --class liquid --class gnu-linux --class gnu --class os {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;# Skipped lines&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        linux   /vmlinuz-&lt;span style=&#34;color:#369&#34;&gt;$bionic_kernel_version&lt;/span&gt;-generic &lt;span style=&#34;color:#369&#34;&gt;root&lt;/span&gt;=/dev/mapper/headVG-root18 ro nomodeset net.ifnames=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;biosdevname&lt;/span&gt;=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; modprobe.blacklist=gma500_gfx quiet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        initrd  /initrd.img-&lt;span style=&#34;color:#369&#34;&gt;$bionic_kernel_version&lt;/span&gt;-generic
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;on-the-current-system-trusty&#34;&gt;On the Current system (Trusty):&lt;/h4&gt;
&lt;p&gt;Back up the current Trusty kernel files into &lt;code&gt;/boot/trusty&lt;/code&gt; and create a custom menu entry configuration for Ubuntu 14.04 on &lt;code&gt;42_custom_trusty&lt;/code&gt;. Update &lt;code&gt;/etc/default/grub&lt;/code&gt; to set Ubuntu 14.04 as the default menu entry and run &lt;code&gt;update-grub&lt;/code&gt; to apply it to the current system. This will be used as a fail-safe method to run Trusty again if there is a problem with the new installation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -vp /boot/trusty
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp -v /boot/*-generic /boot/trusty/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;envsubst &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;${trusty_kernel_version}&amp;#39;&lt;/span&gt; &amp;lt; 42_custom_template_trusty &amp;gt; /etc/grub.d/42_custom_template_trusty
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chmod +x /etc/grub.d/42_custom_template_trusty
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sed -i &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/GRUB_DEFAULT=.*/GRUB_DEFAULT=&amp;#34;TRUSTY&amp;#34;/&amp;#39;&lt;/span&gt; /etc/default/grub
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;update-grub&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;on-the-target-system-bionic&#34;&gt;On the target system (Bionic):&lt;/h4&gt;
&lt;p&gt;Create the custom menu entry for Ubuntu 14.04 and Ubuntu 18.04 on the target system.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p /mnt/root18/etc/grub.d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;envsubst &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;${trusty_kernel_version}&amp;#39;&lt;/span&gt; &amp;lt; 42_custom_template_trusty &amp;gt; /mnt/root18/etc/grub.d/42_custom_template_trusty
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;envsubst &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;${bionic_kernel_version}&amp;#39;&lt;/span&gt; &amp;lt; 42_custom_template_bionic &amp;gt; /mnt/root18/etc/grub.d/42_custom_template_bionic
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chmod +x /mnt/root18/etc/grub.d/42_custom_template_{trusty,bionic}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;chroot&lt;/code&gt; into the target system and update &lt;code&gt;/etc/default/grub&lt;/code&gt; to set Ubuntu 14.04 as the default menu entry and run &lt;code&gt;update-grub&lt;/code&gt;. This will also update the GRUB configuration to boot Ubuntu 14.04 as default and update the 0th menu entry to Ubuntu 18.04 (Bionic).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;&amp;lt;EOF | chroot /mnt/root18
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;sed -i &amp;#39;s/GRUB_DEFAULT=.*/GRUB_DEFAULT=&amp;#34;TRUSTY&amp;#34;/&amp;#39; /etc/default/grub
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;update-grub
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;EOF&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;boot-into-bionic&#34;&gt;Boot into Bionic&lt;/h3&gt;
&lt;p&gt;To boot into Ubuntu 18.04 (Bionic), reboot the system after &lt;code&gt;grub-reboot bionic&lt;/code&gt; and test if the bionic system is working as expected.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ grub-reboot bionic
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ reboot&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Reboot and test our new 0th GRUB entry:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ grub-reboot &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ reboot&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A normal reboot returns to Ubuntu 14.04 (Trusty) since the default menu entry is still set to Ubuntu 14.04 (Trusty).&lt;/p&gt;
&lt;h3 id=&#34;set-ubuntu-1804-to-boot-default&#34;&gt;Set Ubuntu 18.04 to boot default&lt;/h3&gt;
&lt;p&gt;To set our new Ubuntu 18.04 installation as the default menu entry, change GRUB_DEFAULT to 0 in &lt;code&gt;/etc/default/grub&lt;/code&gt; and run &lt;code&gt;update-grub&lt;/code&gt; to apply it. The next reboot will boot into Ubuntu 18.04.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sed -i &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s/GRUB_DEFAULT=.*/GRUB_DEFAULT=0/&amp;#39;&lt;/span&gt; /etc/default/grub
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;update-grub&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Congratulations! You now have a freshly installed Ubuntu 18.04 system.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Job opening: Linux system administration and DevOps remote engineer</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/04/linux-sysadmin-devops-remote-job/"/>
      <id>https://www.endpointdev.com/blog/2019/04/linux-sysadmin-devops-remote-job/</id>
      <published>2019-04-18T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/04/linux-sysadmin-devops-remote-job/kevin-horvat-1354011-unsplash-small.jpg&#34; alt=&#34;computer monitors behind silhouetted head&#34; /&gt;&lt;br&gt;Photo by &lt;a href=&#34;https://unsplash.com/photos/Pyjp2zmxuLk&#34;&gt;Kevin Horvat&lt;/a&gt; on Unsplash&lt;/p&gt;
&lt;p&gt;We are looking for a full-​time, salaried engineer to work during business hours in UTC-10 to UTC-6 (somewhere between Hawaii Time and Mountain Time) in the fun space where operations and development overlap!&lt;/p&gt;
&lt;p&gt;End Point is a 23-​year-old Internet technology consulting company based in New York City, with about 50 employees, most working remotely from home offices. We collaborate using SSH, GitLab, GitHub, chat, video conferencing, and good old email and phones.&lt;/p&gt;
&lt;p&gt;We serve many development and hosting clients ranging from small family businesses to large corporations.&lt;/p&gt;
&lt;h3 id=&#34;what-you-will-be-doing&#34;&gt;What you will be doing:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Remotely set up and maintain Linux servers (mostly RHEL/​CentOS, Debian, and Ubuntu), with custom web applications&lt;/li&gt;
&lt;li&gt;Audit and improve security, backups, reliability, monitoring&lt;/li&gt;
&lt;li&gt;Support developer use of major language ecosystems&lt;/li&gt;
&lt;li&gt;Automate provisioning with Terraform, Ansible, Chef, Puppet, etc.&lt;/li&gt;
&lt;li&gt;Troubleshoot problems with performance, automation, security&lt;/li&gt;
&lt;li&gt;Use open source tools and contribute back as opportunity arises&lt;/li&gt;
&lt;li&gt;Use your desktop OS of choice: Linux, macOS, Windows&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-you-bring&#34;&gt;What you bring:&lt;/h3&gt;
&lt;p&gt;Professional experience with Linux system administration and web application support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cloud providers such as DigitalOcean, Linode, AWS, Azure, Google Cloud, Heroku, etc.&lt;/li&gt;
&lt;li&gt;Networking&lt;/li&gt;
&lt;li&gt;TLS and PKI&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;Web servers and HTTP&lt;/li&gt;
&lt;li&gt;Databases such as PostgreSQL, MySQL, Solr, Elasticsearch, CouchDB, MongoDB, etc.&lt;/li&gt;
&lt;li&gt;Libraries in Ruby gems, PHP PEAR/​PECL, Python PyPI, Node.js npm, Perl CPAN, Java/​JVM JARs, etc.&lt;/li&gt;
&lt;li&gt;Security consciousness, and ideally familiarity with PCI DSS, HIPAA, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And just as important:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Strong verbal and written communication skills&lt;/li&gt;
&lt;li&gt;A good remote work environment&lt;/li&gt;
&lt;li&gt;An eye for detail&lt;/li&gt;
&lt;li&gt;Tenacity in solving problems&lt;/li&gt;
&lt;li&gt;Ownership of projects to get things done well&lt;/li&gt;
&lt;li&gt;Work both independently and as part of a team&lt;/li&gt;
&lt;li&gt;Focus on customer needs&lt;/li&gt;
&lt;li&gt;Be part of emergency on-call rotation including weekends&lt;/li&gt;
&lt;li&gt;Willingness to shift work time after hours for occasional scheduled work&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-work-here-offers&#34;&gt;What work here offers:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Flexible full-time work hours&lt;/li&gt;
&lt;li&gt;Paid holidays and vacation&lt;/li&gt;
&lt;li&gt;For U.S. employees: health insurance subsidy and 401(k) retirement savings plan&lt;/li&gt;
&lt;li&gt;Annual bonus opportunity&lt;/li&gt;
&lt;li&gt;Freedom from being tied to an office location&lt;/li&gt;
&lt;li&gt;Collaborate with knowledgeable, friendly, and diligent co-workers around the world&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;getting-in-touch-with-us&#34;&gt;Getting in touch with us:&lt;/h3&gt;
&lt;p&gt;&lt;del&gt;Please email us an introduction to jobs@endpointdev.com to apply.&lt;/del&gt;
&lt;strong&gt;(This job has been filled.)&lt;/strong&gt;
Include your location, a resume/​CV, your GitHub or LinkedIn URLs, or whatever else helps us get to know you.&lt;/p&gt;
&lt;p&gt;We look forward to hearing from you! Full-time employment seekers only, please—​this role is not for agencies or subcontractors.&lt;/p&gt;
&lt;p&gt;We are an equal opportunity employer and value diversity at our company. We do not discriminate on the basis of sex/​gender, race, religion, color, national origin, sexual orientation, age, marital status, veteran status, or disability status.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>SRV DNS records in Terraform and Cloudflare</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/06/srv-dns-terraform-cloudflare/"/>
      <id>https://www.endpointdev.com/blog/2018/06/srv-dns-terraform-cloudflare/</id>
      <published>2018-06-26T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2018/06/srv-dns-terraform-cloudflare/2109300822_07103c8f1a_o-crop.jpg&#34; alt=&#34;woman walking across train tracks&#34; /&gt;&lt;br&gt;&lt;a href=&#34;https://www.flickr.com/photos/carbonnyc/2109300822/&#34;&gt;(Photo by David Goehring, CC BY 2.0, cropped)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At End Point we are using &lt;a href=&#34;https://www.terraform.io/&#34;&gt;Terraform&lt;/a&gt; for a few clients to manage their web hosting &lt;a href=&#34;https://en.wikipedia.org/wiki/Infrastructure_as_Code&#34;&gt;infrastructure as code (IaC)&lt;/a&gt;. Terraform is particularly helpful when working with multiple cloud or infrastructure providers and stitching together their services.&lt;/p&gt;
&lt;p&gt;For example, for one web application that involves failover from the primary production infrastructure to a secondary location at a different provider, we are using &lt;a href=&#34;https://www.cloudflare.com/&#34;&gt;Cloudflare&lt;/a&gt; as a CDN to provide caching, DDoS mitigation, and traffic routing in front of virtual servers at DigitalOcean and Amazon Web Services (AWS).&lt;/p&gt;
&lt;p&gt;We decided we wanted to store all of their infrastructure configuration in Terraform, not just what is required for the web application, so we can recreate their entire infrastructure from their Git repository.&lt;/p&gt;
&lt;p&gt;This all went fine until we got to their email DNS records. Our client is using Microsoft Office 365 for their email, which requires some SRV records. Terraform’s Cloudflare provider works fine with the universal MX records, but when we first wanted to do this, the Terraform provider for Cloudflare did not support SRV records at all.&lt;/p&gt;
&lt;p&gt;Luckily for us, Terraform recently (6 April 2018) gained support for DNS SRV records as mentioned in the &lt;a href=&#34;https://github.com/terraform-providers/terraform-provider-cloudflare/blob/master/CHANGELOG.md#100-april-06-2018&#34;&gt;release notes&lt;/a&gt; and described in more detail in the &lt;a href=&#34;https://github.com/terraform-providers/terraform-provider-cloudflare/pull/29&#34;&gt;pull request&lt;/a&gt; that added the feature.&lt;/p&gt;
&lt;p&gt;Great! So now we can get on with this.&lt;/p&gt;
&lt;p&gt;I began by naively assuming that the SRV record data should be given in space-separated form like many DNS interfaces use, including BIND and Cloudflare’s web interface itself. I tried setting it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;resource &amp;#34;cloudflare_record&amp;#34; &amp;#34;_sipfederationtls_tcp&amp;#34; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  domain = &amp;#34;${var.domain}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name   = &amp;#34;_sip._tcp.${var.subdomain}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  type   = &amp;#34;SRV&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  value  = &amp;#34;100 1 443 sipdir.online.lync.com.&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But that resulted in an error. So when in doubt, consult the documentation, right? I did that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.terraform.io/docs/providers/cloudflare/r/record.html#data&#34;&gt;Terraform Cloudflare provider docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record&#34;&gt;Cloudflare API docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those make it clear that a &lt;code&gt;data&lt;/code&gt; element is required, but give no indication what format is needed.&lt;/p&gt;
&lt;p&gt;This is not currently documented for Cloudflare’s API, nor for Terraform’s Cloudflare provider. User &lt;a href=&#34;https://github.com/terraform-providers/terraform-provider-cloudflare/pull/29#issuecomment-374600386&#34;&gt;EpiqSty helpfully recorded&lt;/a&gt; asking Cloudflare’s support, who recommended reverse-engineering the format:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the meantime, what I would suggest is you can create a SRV record via our UI and then you can use the API to do a GET request on the DNS records, that should show you all the fields that are required.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ok, then!&lt;/p&gt;
&lt;p&gt;It turns out that we must provide the component parts of the SRV record disassembled in key/​value pairs as the Cloudflare API expects, which looks like this in Terraform config:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;resource &amp;#34;cloudflare_record&amp;#34; &amp;#34;_sip_tls&amp;#34; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  domain = &amp;#34;${var.domain}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name   = &amp;#34;_sip._tls.${var.subdomain}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  type   = &amp;#34;SRV&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  data   = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    service  = &amp;#34;_sip&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    proto    = &amp;#34;_tls&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name     = &amp;#34;${var.subdomain}.&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    priority = 100
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    weight   = 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    port     = 443
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    target   = &amp;#34;sipdir.online.lync.com.&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One unexpected aspect of this is that it requires duplicating the parts that make up the &lt;code&gt;name&lt;/code&gt; although the name doesn’t seem to be used by Cloudflare, which assembles the name from the &lt;code&gt;data&lt;/code&gt; components &lt;code&gt;service&lt;/code&gt;, &lt;code&gt;proto&lt;/code&gt;, and &lt;code&gt;name&lt;/code&gt;. The &lt;code&gt;name&lt;/code&gt; is apparently just there for Terraform, unlike other DNS records where the name is used as the DNS record value.&lt;/p&gt;
&lt;p&gt;The Terraform Cloudflare provider also gained support for the much more obscure &lt;a href=&#34;https://en.wikipedia.org/wiki/LOC_record&#34;&gt;DNS LOC record type&lt;/a&gt; at the same time, and it works similarly.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&#34;https://github.com/benjvi&#34;&gt;Ben Vickers&lt;/a&gt; who contributed the new feature to Terraform!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Disaster Recovery — Miami to Dallas in one hit</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/10/disaster-recovery-miami-to-dallas-in/"/>
      <id>https://www.endpointdev.com/blog/2017/10/disaster-recovery-miami-to-dallas-in/</id>
      <published>2017-10-03T00:00:00+00:00</published>
      <author>
        <name>Ian Neilsen</name>
      </author>
      <content type="html">
        &lt;p&gt;Hurricane Irma came a knocking but didn’t blow Miami away.&lt;/p&gt;
&lt;p&gt;Hurricanes are never fun, and neither is knowing that your business servers may be in the direct path of a major outage due to a natural disaster.&lt;/p&gt;
&lt;p&gt;Recently we helped a client prepare a disaster recovery environment, 4 days out pending Hurricane Irma approaching Miami—​where it happens their entire server ecosystem sat. In recent months we had been discussing a long-term disaster recovery plan for this client’s infrastructure, but the final details hadn’t been worked out yet, and no work begun on it, when the news of the impending storm started to arrive.&lt;/p&gt;
&lt;p&gt;Although the Miami datacenter they are using is highly regarded and well-rated, the client had the foresight to think about an emergency project to replicate their entire server ecosystem out of Miami to somewhere a little safer.&lt;/p&gt;
&lt;p&gt;Fire suits on, battle helmets ready, GO! Two team members jumped in and did an initial review of the ecosystem: six Linux servers, one Microsoft SQL Server database with about 50 GB of data, and several little minions. All of this hosted on a KVM virtualization platform. OK, easy, right? Export the VM disks and stand up in a new datacentre on a similar KVM-based host.&lt;/p&gt;
&lt;p&gt;But no downtime could be scheduled at that time, and cloning the database would take too much time to make a replica. If we can’t clone the VM disks without downtime we need another plan. Enter our save-the-day idea: use the database clone from the development machine.&lt;/p&gt;
&lt;p&gt;So first things first: Build the servers in the new environment using the cloned copy, rsync the entire disk across, hoping that a restart would return the machine into a working state, take the development database and use the Microsoft tools at our disposal to replicate one current snapshot, then replicate row level details, in preparation of reducing the amount of lost data, should the datacenter lose connection.&lt;/p&gt;
&lt;p&gt;Over the course of the next 16 hours, with 2 DevOps engineers, we replicated 6 servers, migrated 100+ GB of data twice and had a complete environment ready for a manual cutover.&lt;/p&gt;
&lt;p&gt;Luckily the hurricane didn’t cause any damage to the datacenter, which never lost power or connectivity, so the sites never went down.&lt;/p&gt;
&lt;p&gt;Even though it wasn’t used in this case, this emergency work did have a silver lining: Until this client’s longer-term disaster recovery environment is built, these system replicas can serve as cold standby, which is much better than having to rebuild from backups if their primary infrastructure goes out.&lt;/p&gt;
&lt;p&gt;That basic phase is out of the way, so the client can breathe easier. Along the way, we produced some updated documentation, and gained deeper knowledge of the client’s software ecosystem. That’s a win!&lt;/p&gt;
&lt;p&gt;The key takeaways here: Don’t panic, and do a little planning prior. Leaving things to the last minute rarely works well.
And preferable to all of the above: Plan and build your long-term disaster recovery environment now, well before any possible disaster is on the horizon!&lt;/p&gt;
&lt;p&gt;To all the people affected by Hurricane Irma we wish a speedy rebuild.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Separate and Not Equal: Development Environments to Support People, Process, and Automation</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/01/separate-not-equal-dev-environments/"/>
      <id>https://www.endpointdev.com/blog/2017/01/separate-not-equal-dev-environments/</id>
      <published>2017-01-18T00:00:00+00:00</published>
      <author>
        <name>Dylan Wooters</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2017/01/separate-not-equal-dev-environments/bauhausstage.jpg&#34;/&gt;&lt;/div&gt;
&lt;p&gt;I believe in the separation of software environments for the long term peace and prosperity of developers, product owners, and users. Ideally, there’s a dedicated environment purpose-built to support each community: the developers, the testers, the acceptors, and the users.&lt;/p&gt;
&lt;p&gt;I learned this process from taking part in software development projects over the course of many years and working with a variety of different teams, including my own dev team, our clients’ in-house IT teams, and often other consulting firms. While each group had its own approach, one consistent factor for success was the separation of environments to support key roles.&lt;/p&gt;
&lt;h3 id=&#34;dev&#34;&gt;Dev&lt;/h3&gt;
&lt;p&gt;Development environments will vary by project and technology stack. A common decision is developing locally or creating a remote VM, and both approaches have trade-offs. Developing remotely allows you to setup an environment that mirrors QA and Production, and it also allows you to use remote desktop to connect to your dev environment from any machine: PC, Mac, or Linux. However, developing remotely comes with latency, and often it doesn’t feel as smooth as working locally. More importantly, if I find myself traveling or otherwise without a stable internet connection, developing remotely can be challenging if not impossible.&lt;/p&gt;
&lt;p&gt;The setup of the dev environment should be quick and painless, and the project should have clear documentation on how to get started developing. We often keep this in a Wiki in the project’s Git repository. Pairing this documentation with a pre-built dev environment is the ultimate way to get new developers up-and-running. This can be done by packaging up a VM, which can then be run locally or remotely using virtualization software like &lt;a href=&#34;https://www.virtualbox.org&#34;&gt;VirtualBox&lt;/a&gt; or a cloud platform like &lt;a href=&#34;https://aws.amazon.com&#34;&gt;AWS&lt;/a&gt;. &lt;a href=&#34;https://www.vagrantup.com&#34;&gt;Vagrant&lt;/a&gt; is another option worth checking out, as it touts a one-command setup of a complete dev environment.&lt;/p&gt;
&lt;h3 id=&#34;qa&#34;&gt;QA&lt;/h3&gt;
&lt;p&gt;The QA environment serves two purposes: as a place where various team members’ code can be built and run together, and a place where the specific task of QA can be performed. When you move to QA testing, it’s a good idea to create a separate branch in source control.This allows developers to push to a common QA branch, which can then be deployed to the QA environment, ideally using a CI tool like TeamCity.&lt;/p&gt;
&lt;p&gt;Once the QA testers have signed off, you can move into the UAT/Staging environment. It’s crucial that this environment match production as closely as possible, to rule out any “environmental bugs” that may creep in after deployment to Production. This means ensuring that all the framework versions match (.NET, Node, etc.), system resources are similar, and that the same security is in place (firewalls, VPNs). The data should also replicate what will be found in production, if not outright synced from production using a tool like &lt;a href=&#34;https://www.sqldelta.com/&#34;&gt;SQL Delta&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;stage&#34;&gt;Stage&lt;/h3&gt;
&lt;p&gt;In staging, the software is put before a specially designated group of users for a dress rehearsal.&lt;/p&gt;
&lt;p&gt;If I’m developing a desktop or mobile application (not a web app), I ensure that testing on a variety of different OS versions is incorporated into the UAT process. For example, I use Apple’s &lt;a href=&#34;https://developer.apple.com/testflight/&#34;&gt;TestFlight&lt;/a&gt; to test across a variety of devices using different iOS versions.&lt;/p&gt;
&lt;h3 id=&#34;prod&#34;&gt;Prod&lt;/h3&gt;
&lt;p&gt;Production is the main stage, and code should obviously only be pushed there after proper testing. Even if I have customers, clients, and/or bosses yelling at me, I try and resist the urge to push a fix directly to Production! Although the fix may have worked perfectly in my dev environment, it might not work in production, and then I can easily find myself in even deeper trouble than before. If there is not time to coordinate UAT, then at the very least I push to QA, where the build can be verified in a separate environment, and another set of eyes can perform some testing. It goes without saying that all of this can be made less frightful with unit testing, a topic that we will explore later.&lt;/p&gt;
&lt;p&gt;It’s also important to ensure that production has adequate resources before going live. If I’m building a new version or replacement of existing software, I review the existing reporting/​analytics to know things like concurrent user count and storage requirements per user. This may seem obvious, but in the rush to get software out to market, basic things can be overlooked. Also, I ensure that I have some baseline security in place on production. This means encrypting sensitive data in config files, not storing passwords (just salted hashes!), and if I’m using a cloud provider like AWS or Azure, I add two-factor authentication to the admin account.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Perl Dancer Conference 2016 Day 1</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/11/perl-dancer-conference-2016-day-1/"/>
      <id>https://www.endpointdev.com/blog/2016/11/perl-dancer-conference-2016-day-1/</id>
      <published>2016-11-30T00:00:00+00:00</published>
      <author>
        <name>Sam Batschelet</name>
      </author>
      <content type="html">
        &lt;img border=&#34;0&#34; height=&#34;306&#34; src=&#34;/blog/2016/11/perl-dancer-conference-2016-day-1/image-0.jpeg&#34; width=&#34;320&#34;/&gt;
&lt;h3 id=&#34;perl-dancer-conference-day-1&#34;&gt;Perl Dancer Conference Day 1&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;https://perl.dance/&#34;&gt;Perl Dancer Conference&lt;/a&gt; is a great event, now in its third year. &lt;img align=&#34;left&#34; border=&#34;0&#34; height=&#34;90&#34; src=&#34;/blog/2016/11/perl-dancer-conference-2016-day-1/image-1.png&#34; width=&#34;130&#34;/&gt; The event took place in the same location as last year in Vienna, Austria at the Hotel Schani Wien. For those of you who have never visited Vienna, it is a perfect place to bring the family. From visiting the beautiful parks to taking a scenic ride on the Danube River, the beautiful and historic city is known for its rich art and musical culture, and has much to offer.&lt;/p&gt;
&lt;p&gt;I was very excited to not only attend but also give a talk this year. My talk titled &lt;a href=&#34;https://www.perl.dance/talks/45-dancing-in-the-clouds&#34;&gt;“Dancing in the Clouds”&lt;/a&gt; also coincided with the release of 2 new Perl modules &lt;a href=&#34;https://metacpan.org/release/HEXFUSION/Etcd3-0.007/view/lib/Etcd3.pm&#34;&gt;Etcd3&lt;/a&gt; and &lt;a href=&#34;https://metacpan.org/pod/Dancer2::Plugin::Etcd&#34;&gt;Dancer2::Plugin::Etcd&lt;/a&gt;. This article will be the first of a 3 part series, with the final article a focus on my talk and usage examples with the new modules.&lt;/p&gt;
&lt;h3 id=&#34;sawyer-x-sawyer-x--a-bus-tour-through-dancer-core&#34;&gt;Sawyer X (Sawyer X) — A bus tour through Dancer core&lt;/h3&gt;
&lt;p&gt;The Captain of Dancer core, SawyerX, took us on a bus tour through the core functionality of Dancer2. Using practical examples of code blocks from core, he explained how different areas of the code base worked. I personally enjoyed his explanation of how hooks are implemented and created. Learning from the 1st iteration of Dancer, the second version shows maturity and stability.&lt;/p&gt;
&lt;h3 id=&#34;stefan-hornburg-racke--no-act-on-act&#34;&gt;Stefan Hornburg (Racke) — No Act on ACT&lt;/h3&gt;
&lt;p&gt;If you have ever taken the time to review a Perl conference’s website or even purchase tickets to attend you have no doubt been in contact with ACT. “Act (A Conference Toolkit) is a multilingual, template-driven, multi-conference website that can manage the users, talks, schedule, and payment for your conference.” While this package has been around for many years, it is somewhat dreaded because of its lack of features.&lt;/p&gt;
&lt;p&gt;Stefan outlines his work with Interchange6::Schema and the perl.dance website painting a picture of the replacement for ACT. Utilizing Dancer2, DBIx::Class, Moo and other modern Perl tools the infrastructure outlined is very compelling. The package has a user admin, e-commerce, and even a module to print out the passes. Although he notes that this is not a plug and play replacement for ACT yet, with a bit of work and support, it could be the future of Perl conference management.&lt;/p&gt;
&lt;h3 id=&#34;andrew-beverly--implementing-i18n-in-a-dancer-application-using-pluginlogreport&#34;&gt;Andrew Beverly — Implementing i18n in a Dancer application using Plugin::LogReport&lt;/h3&gt;
&lt;p&gt;Andrew extended his talk last year about internationalization with the Dancer2::Plugin::LogReport module. Using great examples, he not only outlined the usage of the module, but also got into details of how the process works on a technical level. Explaining the different ways that internationalization can be done, he begins to outline how he achieved his task of deploying i18n in a Dancer app.&lt;/p&gt;
&lt;h3 id=&#34;theo-van-hoesel--quickstep&#34;&gt;Theo van Hoesel — Quickstep&lt;/h3&gt;
&lt;p&gt;Theo was a great addition to the conference this year. He was able to make the event on very short notice after Dancer core Jason Crome was not able to attend due to injury. Theo outlined the Act::Voyager project briefly and the general requirements of adding user friendly features to the ACT toolkit. He also spent a good amount of time explaining the concept of web caching and how many of the existing modules failed in the task of complying with RFC7234. He then explained how all of this brought him to create HTTP::Caching and how it has “The RFC 7234 compliant brains to do caching right”. Along with this contribution part of the HTTP::Bundle, his Dancer focused Dancer2::Plugin::HTTP::Bundle was explained.&lt;/p&gt;
&lt;h3 id=&#34;job-van-achterberg-jkva--dancing-with-disabilities&#34;&gt;Job van Achterberg (jkva) — Dancing with Disabilities&lt;/h3&gt;
&lt;p&gt;Job’s talk was a very interesting look into how taking a considerate approach to development and small changes to your code can improve a disabled web user’s experience. Using the tools in macOS Job showed how simple things such as naming a list are reflected in a disabled users ability to get information. What I found very interesting in this presentation was how awkward the tools were to use even for an experienced pro like Job. It really made me think a lot about the challenges the disabled face in something many of us take for granted.&lt;/p&gt;
&lt;h3 id=&#34;jason-lewis--the-lazy-programmers-guide-to-building-html-tables-in-dancer2&#34;&gt;Jason Lewis — The Lazy Programmer’s Guide to building HTML tables in Dancer2&lt;/h3&gt;
&lt;p&gt;Jason has become a regular on the #dancer freenode IRC channel. This year he decided to travel all the way from Australia to give his presentation on his experiences replacing Crystal Reports with Dancer2 Template::Toolkit and DataTables. Although a great deal of the presentation was focused on the features of the jQuery plugin DataTables, he gave nice examples of code he used to populate reports and the hurdles he faced replacing Crystal Reports functionality. The reports looked beautiful and were very easy to convert to other data types such as PDF and CSV.&lt;/p&gt;
&lt;h3 id=&#34;stefan-seifert-nine--perl-5-and-perl-6--a-great-team&#34;&gt;Stefan Seifert (nine) — Perl 5 and Perl 6 — a great team&lt;/h3&gt;
&lt;p&gt;Stefan is a great presence at the conference, and his fun and witty personality carried over to his presentation. After opening with a really funny skit as a reporter reading the the news, he continued to outline the current state of Perl6 and how important it is for all of us as a community to embrace the fact that we are all in this together. He reminded us of the perils of Python 3’s launch and the lack of support even today. He then began to focus on the capabilities of using Perl 5 with Perl 6 together with Inline::Perl5 and Inline::Perl6 modules. To be honest before his talk I had given Perl 6 very little time. Stefan’s talk opened my eyes to the possibilities of utilizing the two versions together and the advantages that ecosystem has.&lt;/p&gt;
&lt;p&gt;Please stop back for links to day 2 of the conference and a breakdown of my talk outlining &lt;a href=&#34;https://coreos.com/etcd/&#34;&gt;etcd&lt;/a&gt; integration with Perl and &lt;a href=&#34;https://perldancer.org/&#34;&gt;Dancer2&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>8 Simple Steps to Saner Software Development</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/11/8-simple-steps-to-saner-software-development/"/>
      <id>https://www.endpointdev.com/blog/2016/11/8-simple-steps-to-saner-software-development/</id>
      <published>2016-11-10T00:00:00+00:00</published>
      <author>
        <name>Dylan Wooters</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/11/8-simple-steps-to-saner-software-development/layercake.jpg&#34;/&gt;&lt;/div&gt;
&lt;p&gt;While these might seem obvious for seasoned developers, many projects, especially legacy software, still fail to follow most of the steps below.&lt;/p&gt;
&lt;p&gt;In future posts, I’ll expand on these steps and give more details. For now, here’s an overview of some basic guidelines for making the process of software development smoother (&amp;amp; saner) for both the client and the dev team.&lt;/p&gt;
&lt;h3 id=&#34;separate-environments&#34;&gt;Separate environments:&lt;/h3&gt;
&lt;p&gt;Ideally there should be Dev, QA, UAT/Staging, and Production environments. And, UAT/Staging should be as close to Production as possible. It’s amazing how many software projects are still done on someone’s local machine and pushed directly to Production.&lt;/p&gt;
&lt;h3 id=&#34;use-a-bug-tracking-tool&#34;&gt;Use a bug tracking tool:&lt;/h3&gt;
&lt;p&gt;This is pretty obvious. Bugs need to be logged and labeled so that they can be tracked through stages of development (i.e., To-Do, In Progress, Completed). Using a bug tracking tool like Jira or GitHub also helps the non-technical stakeholders comment on and clarify requirements/issues.&lt;/p&gt;
&lt;h3 id=&#34;have-a-source-control-strategy&#34;&gt;Have a source control strategy:&lt;/h3&gt;
&lt;p&gt;It doesn’t have to be fancy. For example, if you’re using Git, it should be more than having multiple developers working out of the master branch. Ideally link branches to features or bugs defined in a bug tracking tool, and keep in mind the separation of environments, especially if the application is already in Production.&lt;/p&gt;
&lt;h3 id=&#34;do-pullmerge-requests&#34;&gt;Do pull/merge requests:&lt;/h3&gt;
&lt;p&gt;Even a short five minute review will improve the overall quality of code, reduce bugs, and communicate changes between developers responsible for different parts of the application. Web-based source control systems like GitLab, GitHub, and Bitbucket make this simple.&lt;/p&gt;
&lt;h3 id=&#34;write-unit-tests&#34;&gt;Write unit tests:&lt;/h3&gt;
&lt;p&gt;A lot of developers either feel pressure to move quickly or get lazy and don’t write tests. Regression testing becomes a nightmare without unit tests. Plus it makes you feel better to see all those tests passing.&lt;/p&gt;
&lt;h3 id=&#34;use-a-continuous-integration-tool&#34;&gt;Use a continuous integration tool:&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.jetbrains.com/teamcity/&#34;&gt;TeamCity&lt;/a&gt;, &lt;a href=&#34;https://jenkins-ci.org&#34;&gt;Jenkins&lt;/a&gt;, or even basic &lt;a href=&#34;http://githooks.com&#34;&gt;GitHooks&lt;/a&gt; are some options. Automated deployments save time and, if integrated with unit tests, reduce the bugs that make it to Production.&lt;/p&gt;
&lt;h3 id=&#34;setup-notifications&#34;&gt;Setup notifications:&lt;/h3&gt;
&lt;p&gt;If the CI tool alerts team members of deployment status, hundreds of “is the deployment done yet?” emails and chat messages can be saved. Slack integration is a good option here.&lt;/p&gt;
&lt;h3 id=&#34;consolidate-project-credentials&#34;&gt;Consolidate project credentials:&lt;/h3&gt;
&lt;p&gt;Have a secure and centralized location for managing passwords. We like keeping a KeePass checked into the relevant project’s repo.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Building Containers with Habitat</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/10/building-containers-with-habitat/"/>
      <id>https://www.endpointdev.com/blog/2016/10/building-containers-with-habitat/</id>
      <published>2016-10-17T00:00:00+00:00</published>
      <author>
        <name>Kirk Harr</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;many-containers-many-build-systems&#34;&gt;Many Containers, Many Build Systems&lt;/h3&gt;
&lt;p&gt;When working with modern container systems like Docker, Kubernetes, and Mesosphere, each provide methods for building your applications into their containers. However each build process is specific to that container system, and using similar applications across tiers of container environments would require maintaining each container’s build environment. When approaching this problem for multiple container environments, Chef Software created a tool to unify these build systems and create container-agnostic builds which could be exported into any of the containers. This tool is called &lt;a href=&#34;https://www.habitat.sh/&#34;&gt;Habitat&lt;/a&gt; which also provide some pre-built images to get applications started quickly.&lt;/p&gt;
&lt;p&gt;I recently attended a Habitat Hack event locally in Portland (Oregon) which helped me get more familiar with the system and its capabilities. We worked together in teams to take a deeper dive into various aspects of how Habitat works, you can read about our adventures over on the &lt;a href=&#34;https://blog.chef.io/2016/09/09/habitat-hack-pdx-wrap/&#34;&gt;Chef blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To examine how the various parts of the build environment work, I picked an example Node.js application from the Habitat Documentation to build and customize.&lt;/p&gt;
&lt;h3 id=&#34;nodejs-application-into-a-docker-container&#34;&gt;Node.js Application into a Docker Container&lt;/h3&gt;
&lt;p&gt;For the most basic Habitat build, you must define a plan.sh file which will contain all the build process logic as well as all configuration values to define the application. Within my Node.js example, this file contains this content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_origin=daehlie
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_name=mytutorialapp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_version=0.3.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_maintainer=&amp;#34;Kirk Harr &amp;lt;kharr@endpoint.com&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_license=()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_source=https://s3-us-west-2.amazonaws.com/${pkg_name}/${pkg_name}-${pkg_version}.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_shasum=e4e988d9216775a4efa4f4304595d7ff31bdc0276d5b7198ad6166e13630aaa9
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_filename=${pkg_name}-${pkg_version}.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_deps=(core/node)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pkg_expose=(8080)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;do_build() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  npm install
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;do_install() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cp package.json ${pkg_prefix}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cp server.js ${pkg_prefix}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mkdir -p ${pkg_prefix}/node_modules/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cp -vr node_modules/* ${pkg_prefix}/node_modules/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Within this is defined all the application details like the name of the author, the version of the application being packaged, as well as the package name. Each package can be defined with a license for the code in use as well as any code dependencies, like the Node.js application server (core/node), as well as the repository URL for locating these files. There are also two executable statements which build the package dependencies, and perform final installation setup during the eventual package installation.&lt;/p&gt;
&lt;p&gt;Additionally to define this application we must provide the logic for how to start the Node application server, and provide configuration on what ports to listen on as well as the message to be displayed once it has started. To do so we must create a stub Node.js config.json which provides the port and message values:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;message&amp;#34;: &amp;#34;Hello, World&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;port&amp;#34;: &amp;#34;8080&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We also need two hooks which will be executed at package install time and at runtime for the application respectively. These are named, init and run in our case, with init setting up the symbolic links to the various Node.js components from the core/node package which will be included in the build, and run provides the entry point for the applications flow effectively starting the npm application server. Just like with a Dockerfile, any additional logic needed during the process would be included in these two hooks, depending on if the logic was specific to install time or run time.&lt;/p&gt;
&lt;h3 id=&#34;injected-configuration-values&#34;&gt;Injected Configuration Values&lt;/h3&gt;
&lt;p&gt;In this example, both the message to be displayed to the user, as well as the port that the Node.js application server will listen on are hard-coded into our build, and all the images that resulted from it would be identical. In order to allow for some customizing of the resulting image, you can replace the hard-coded values in the Node.js config.json into variables which can be replaced during the build process:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;message&amp;#34;: &amp;#34;{{cfg.message}}&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;port&amp;#34;: &amp;#34;{{cfg.port}}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To complete the replacement, we would provide a “Tom’s Obvious, Minimal Language” (.toml) file with has a key-value pair for each of these configuration variables we want to set. This .toml file will be interpreted during each build to populate these values, creating an opportunity to customize our builds by injecting specific values into the variables defined in the application configuration. Here is an example of the syntax from this example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Message of the Day
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;message = &amp;#34;Hello, World of Habitat&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# The port number that is listening for requests.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;port = 8080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;Habitat seeks to fill in the gaps between the various container formats for Docker, Kubernetes and others, by allowing common build infrastructure and dependency libraries to be unified in distribution. By utilizing the same build infrastructure, it becomes more feasible to have a hybrid environment with various container formats in use, without creating duplicate build infrastructure which basically performs the same task slightly differently right at the end to package the application into the proper container format. Habitat helps to decouple the actual build process and all that plumbing, from the process of exporting the build image into the proper format for whatever container is in use. In that way as new container formats are developed, all that is required to accommodate them is expanding the export function for that new format, without any changes to the overall build process or customization of your code.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>DevOpsDays India — 2015</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/09/devopsdays-india-2015/"/>
      <id>https://www.endpointdev.com/blog/2015/09/devopsdays-india-2015/</id>
      <published>2015-09-30T00:00:00+00:00</published>
      <author>
        <name>Selvakumar Arumugam</name>
      </author>
      <content type="html">
        &lt;p&gt;DevOpsIndia 2015 was held at The Royal Orchid in Bengaluru on Sep 12-13, 2015. After saying hello to a few familiar faces who I often see at the conferences, I collected some goodies and entered into the hall. Everything was set up for the talks. Niranjan Paranjape, one of the organizers, was giving the introduction and overview of the conference.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/09/devopsdays-india-2015/image-0-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/09/devopsdays-india-2015/image-0.jpeg&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Justin Arbuckle from Chef gave a wonderful keynote talk about the “Hedgehog Concept” and spoke more about the importance of consistency, scale and velocity in software development.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/09/devopsdays-india-2015/image-1-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/09/devopsdays-india-2015/image-1.jpeg&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;In addition, he quoted “A small team with generalists who have a specialization, deliver far more than a large team of single skilled people.”&lt;/p&gt;
&lt;p&gt;A talk on “DevOps of Big Data infrastructure at scale” was given by Rajat Venkatesh from Qubole. He explained the architecture of Qubole Data Service (QDS), which helps to autoscale the Hadoop cluster. In short, scale up happens based on the data from Hadoop Job Tracker about the number of jobs running and time to complete the jobs. Scale down will be done by decommissioning the node, and the server will be chosen by which is reaching the boundary of an hour. This is because most of the cloud service providers charge for an hour regardless of whether the usage is 1 minute or 59 minutes.&lt;/p&gt;
&lt;p&gt;Vishal Uderani, a DevOps guy from WebEngage, presented “Automating AWS infrastructure and code deployment using Ansible.” He shared the issues facing the environments like task failure due to ssh timeout on executing a giant task using Ansible and solved by monitoring the task after triggering the task to get out of the system immediately. Integrating Rundeck with Ansible is an alternative for enterprise Ansible Tower. He also stated the following reasons for using Ansible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Good learning curve&lt;/li&gt;
&lt;li&gt;No agents will be running on the client side, which avoids having to monitor the agents at client nodes&lt;/li&gt;
&lt;li&gt;Great deployment tool&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vipul Sharma from CodeIgnition stated the importance of Resilience Testing. The application should be tested periodically to be tough and strong enough to handle any kind of load. He said Simian Army can be used to create the problems in environments and then resolving them to make them flawless. Simian Army can used to improve application using security monkey, chaos monkey, janitor monkey, etc… Also “Friday Failure” is a good method to identify the problem and improve the application.&lt;/p&gt;
&lt;p&gt;Avi Cavale from Shippable gave an awesome talk on “Modern DevOps with Docker”. He talk started with “What is the first question that arises during an outage ? &amp;hellip; What changed ?”After fixing these issues, the next question will be “Who made the change?” Both questions are bad for the business. Change is the root cause of all outage but business requires changes. In his own words, DevOps is a culture of embracing change. Along with that he explained the zero downtime ways to deploy the changes using a container.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/09/devopsdays-india-2015/image-2-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/09/devopsdays-india-2015/image-2.jpeg&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;He said DevOps is a culture, make it FACT(F-Frictionless, A-Agile, C-Continuous and T-Transparency).&lt;/p&gt;
&lt;p&gt;Rahul Mahale from SecureDB gave a demo on &lt;a href=&#34;https://terraform.io/&#34;&gt;Terraform&lt;/a&gt;, a tool for build and orchestration in Cloud. It features “Infrastructure as Code” and also provides an option to generate the diagrams and graphs of the present infrastructure.&lt;/p&gt;
&lt;p&gt;Shobhit and Sankalp from CodeIgnition shared their experience on solving network based issues. Instead of whitelisting the user’s location every time manually to provide the access to systems, they created a VPN to enable access only to users, not locations. They have resolved two more additional kind of issues by adding Router to bind two networks using FIP.  Another issue is that they need to whitelist to access third party services from containers, but it was hard to whitelist all the containers. Therefore, they created and whitelisted VMs and containers accessed the third party services through VMs.&lt;/p&gt;
&lt;p&gt;Ankur Trivedi from Xebia Labs spoke about the “&lt;a href=&#34;https://www.opencontainers.org/&#34;&gt;Open Containers Initiative&lt;/a&gt;” project. He explained the evolution of the containers (Docker—​2013 &amp;amp; rkt—​2014). The various distributions of containers are compared based on the Packing, Identity, Distribution and Runtime capabilities. Open Containers is supported by the community and various companies who are doing extensive work on containers in order to standardize them.&lt;/p&gt;
&lt;p&gt;Vamsee Kanala, a DevOps consultant, presented a talk on “Docker Networking—​A Primer”. He spoke about Bridge networking, Host Networking, Mapped container networking and None (Self Managed) with dockers. The communications between the containers can happen through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Port mapping&lt;/li&gt;
&lt;li&gt;Link&lt;/li&gt;
&lt;li&gt;Docker composing (programmatically)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, he explained the tools which feature the clustering of containers, and listed the tools that have their own way of clustering and advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Mesos&lt;/li&gt;
&lt;li&gt;Docker Swarm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Aditya Patawari from BrowserStack gave a demo on “Using Kubernetes to Build Fault Tolerant Container Clusters”. Kubernetes has a feature called “Replication Controllers,” which helps to maintain number of pods running at any time. “Kubernetes Services” defines a policy to enable access among the pods which provisions the pods as micro services.&lt;/p&gt;
&lt;p&gt;Arjun Shenoy from LinkedIn introduced a tool called “&lt;a href=&#34;https://github.com/linkedin/simoorg&#34;&gt;SIMOORG&lt;/a&gt;.” The tool was developed at LinkedIn and does the failure induction in a cluster for testing the stability of the code. It is a components-based Open Source framework and few components are replaceable with external ones.&lt;/p&gt;
&lt;p&gt;Dharmesh Kakadia, a researcher from Microsoft, gave a wonderful talk on “Mesos is the Linux”. He started with a wonderful explanation on Micro services (relating with linux commands, each command is a micro service) which is simplest, independently updatable, runnable and deployable. Mesos is a “Data Center Kernel” which takes care of scalability, fault tolerance, load balance, etc… in Data Center.&lt;/p&gt;
&lt;p&gt;At the end, I got a chance to do some hands-on things on Docker and played with some of its features. It was a wonderful conference to learn more about configuration management and the containers world.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>The ‘name’ attribute is required in cookbook metadata: Solving a Vagrant/Chef Provisioning Issue</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/04/the-name-attribute-is-required-in/"/>
      <id>https://www.endpointdev.com/blog/2015/04/the-name-attribute-is-required-in/</id>
      <published>2015-04-21T00:00:00+00:00</published>
      <author>
        <name>Greg Davidson</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;when-vagrantchef-provisioning-goes-south&#34;&gt;When Vagrant/Chef Provisioning Goes South&lt;/h3&gt;
&lt;p&gt;I recently ran into the following error when provisioning a new vagrant machine via the &lt;code&gt;vagrant up&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2015-04-21T17:10:35+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2015-04-21T17:10:35+00:00] ERROR: Cookbook loaded at path(s) [/tmp/vagrant-chef/path/to/my-cookbook] has invalid metadata: The &amp;#39;name&amp;#39; attribute is required in cookbook metadata
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2015-04-21T17:10:35+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After some googling and digging I learned version 12 of chef-client introduced a breaking change. From version 12 on, every cookbook &lt;a href=&#34;https://docs.chef.io/release/12-0/release_notes.html#metadata-rb-settings&#34;&gt;requires a name attribute in their metadata.rb file&lt;/a&gt;. A quick grep through the metadata.rb files in the project revealed several did not include name attributes. You would be correct at this point to suggest I could have added name attributes to the cookbook metadata files and been done with this. However, in this case I was a consumer of the cookbooks and was not involved in maintaining them so an alternate solution was preferable.&lt;/p&gt;
&lt;h3 id=&#34;selecting-a-specific-version-of-chef-in-vagrant&#34;&gt;Selecting a Specific Version of Chef in Vagrant&lt;/h3&gt;
&lt;p&gt;My idea for a solution was to install the most recent chef-client release prior to version 12. I was not sure how to do this initially but along the way I learned that by default, Vagrant will install the most recent release of chef-client. The &lt;a href=&#34;http://docs.vagrantup.com/v2/provisioning/chef_common.html&#34;&gt;Vagrant documentation for Chef provisioners&lt;/a&gt; described what I needed to do. The Chef version could be specified in config.vm.provision block in the Vagrantfile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config.vm.provision &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:chef_solo&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |chef|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.version = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;11.18&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.cookbooks_path = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;cookbooks&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.data_bags_path = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;data_bags&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#888&#34;&gt;# List of recipes to run&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.add_recipe &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;vagrant_main::my_project&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With this configuration change, chef-client 11.18 completed the provisioning step successfully.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Testing your chef repo pull requests with chef-zero, Vagrant and Jenkins</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/02/testing-your-chef-repo-pull-requests/"/>
      <id>https://www.endpointdev.com/blog/2015/02/testing-your-chef-repo-pull-requests/</id>
      <published>2015-02-18T00:00:00+00:00</published>
      <author>
        <name>Wojtek Ziniewicz</name>
      </author>
      <content type="html">
        &lt;p&gt;All &lt;a href=&#34;https://www.visionport.com/&#34;&gt;Liquid Galaxy&lt;/a&gt; setups deployed by End Point are managed by &lt;a href=&#34;https://www.chef.io/chef/&#34;&gt;Chef&lt;/a&gt;. Typical deployment consists of approx 3 to 9 Linux boxes from which only 1 is managed and the rest is an ISO booted from this machine via network with copy-on-write root filesystem. Because of this, typical deployment involves more steps than just updating your code and restarting application. Deployment + rollback may be even 10 times longer compared with typical web application. Due to this fact, we need to test our infrastructure extensively.&lt;/p&gt;
&lt;p&gt;What are we to do in order to make sure that our infrastructure is &lt;strong&gt;tested&lt;/strong&gt; &lt;strong&gt;well&lt;/strong&gt; &lt;strong&gt;before it hits production?&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;
&lt;br/&gt;&lt;/div&gt;
&lt;p&gt;Scary? It’s not.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;
&lt;a href=&#34;/blog/2015/02/testing-your-chef-repo-pull-requests/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em; text-align: left;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;550&#34; src=&#34;/blog/2015/02/testing-your-chef-repo-pull-requests/image-0.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h4 id=&#34;workflow-broken-down-by-pieces&#34;&gt;Workflow broken down by pieces&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;lg_chef.git repo&lt;/strong&gt;—​where we keep cookbooks, environments and node definitions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub pull request&lt;/strong&gt;—​artifact of infrastructure source code tested by Jenkins&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vagrant&lt;/strong&gt;—​virtual environment in which chef is run in order to test the artifact. There’s always 1 master node and few Vagrant boxes that boot an ISO from master via tftp protocol&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;chef-zero&lt;/strong&gt;—​Chef flavor used to converge and test the infrastructure on the basis of GitHub pull request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;chef-server/chef-client&lt;/strong&gt;—​Chef flavor used to converge and test production and pre-production environment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jenkins&lt;/strong&gt; — Continuous Integration environment that runs the converge process and part of the tests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests&lt;/strong&gt;—​two frameworks used—​&lt;a href=&#34;https://github.com/sstephenson/bats&#34;&gt;BATS&lt;/a&gt; (for the integration tests on the top) and &lt;a href=&#34;https://github.com/seattlerb/minitest&#34;&gt;minitest&lt;/a&gt; (for after-converge tests)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;lg-live-build&lt;/strong&gt;—​our fork of &lt;a href=&#34;https://www.debian.org/devel/debian-live/&#34;&gt;Debian live build&lt;/a&gt; used to build the ISO that is booted by Vagrant slaves&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;workflow-broken-down-by-the-order-of-actions&#34;&gt;Workflow broken down by the order of actions&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;User submits &lt;strong&gt;GitHub pull request&lt;/strong&gt; to &lt;strong&gt;lg_chef.git&lt;/strong&gt; repo&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub pull request&lt;/strong&gt; gets picked up by &lt;strong&gt;Jenkins&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jenkins&lt;/strong&gt; creates 1 &lt;strong&gt;master&lt;/strong&gt; &lt;strong&gt;Vagrant&lt;/strong&gt; node and several slave nodes to act as &lt;strong&gt;slaves&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;chef-zero&lt;/strong&gt; converges master &lt;strong&gt;Vagrant&lt;/strong&gt; box and runs &lt;strong&gt;minitest&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BATS&lt;/strong&gt; tests run on the freshly converged &lt;strong&gt;Vagrant master&lt;/strong&gt;box. Few steps are performed here: ISO is built, it’s distributed to the slaves, slaves boot the ISO and final integration tests are run to see whether slaves have all the goodness.&lt;/li&gt;
&lt;li&gt;If points 1 to 5 are **green,**developer merges the changes, uploads the updated cookbooks, node definitions, roles and environments and runs the final tests.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;What didn’t work for us and why&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/test-kitchen/kitchen-vagrant&#34;&gt;kitchen-vagrant &lt;/a&gt;—​because it didn’t play well with Jenkins (or JVM itself) and didn’t know how to use advanced Vagrant features for specifying multiple networking options, interfaces and drivers. However it supports using your own &lt;a href=&#34;https://github.com/test-kitchen/kitchen-vagrant/blob/master/templates/Vagrantfile.erb&#34;&gt;Vagrantfile.erb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;We’ve had some doubts about keeping all the cookbooks, environments and node definitions in one repo because chef-server/chef-client tests can only test your stuff if it’s uploaded to the Chef server, but &lt;strong&gt;chef-zero&lt;/strong&gt; came in handy&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;the-code&#34;&gt;The code&lt;/h4&gt;
&lt;p&gt;As previously mentioned, we needed our own vagrant template file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Vagrant&lt;/span&gt;.configure(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |config|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% if &lt;/span&gt; &lt;span style=&#34;color:#33b&#34;&gt;@data&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:chef_type&lt;/span&gt;] == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_zero&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.chef_zero.enabled = true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.chef_zero.chef_server_url = &amp;#34;&amp;lt;%= @data[:chef_server_url] %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.chef_zero.roles = &amp;#34;&lt;/span&gt;../../roles/&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.chef_zero.cookbooks = &amp;#34;&lt;/span&gt;../../cookbooks/&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.chef_zero.environments = &amp;#34;&lt;/span&gt;../../environments/&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.chef_zero.data_bags = &amp;#34;&lt;/span&gt;../integration/default/data_bags/&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  &amp;lt;% else %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.chef_zero.enabled = false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  &amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.omnibus.chef_version = &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:chef_version] %&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.vm.define &amp;#34;&amp;lt;%=&lt;/span&gt; &lt;span style=&#34;color:#33b&#34;&gt;@data&lt;/span&gt;[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:headnode&lt;/span&gt;][&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:slug&lt;/span&gt;] &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;&amp;#34; do |h|
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  h.vm.box = &amp;#34;&amp;lt;%= @data[:headnode][:box] %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    h.vm.box_url = &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:headnode][:box_url] %&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    h.vm.hostname =&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:hostname] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.network(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:private_network&lt;/span&gt;, {&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:ip&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;10.42.41.1&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.synced_folder &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/vagrant&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;disabled&lt;/span&gt;: &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.provider &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:virtualbox&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% @data[:headnode][:customizations].each &lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |key, value| &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        p.customize [&amp;#34;modifyvm&amp;#34;, :id, &amp;#34;&amp;lt;%= key %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= value %&amp;gt;&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    end
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    h.vm.provision :chef_client do |chef|
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;% if @data[:chef_type] =&lt;/span&gt;= &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_zero&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.environment = &amp;#34;&amp;lt;%= @data[:headnode][:provision][:environment] %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.run_list = &amp;lt;%= @data[:run_list] %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.json = &amp;lt;%= @data[:node_definition] %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.chef_server_url = &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:chef_server_url] %&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;% else %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.chef_server_url =&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:provision][:chef_server_url] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% end &lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.validation_key_path = &amp;#34;&amp;lt;%= @data[:headnode][:provision][:validation_key_path] %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.encrypted_data_bag_secret_key_path = &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:headnode][:provision][:encrypted_data_bag_secret_key_path] %&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.verbose_logging =&lt;/span&gt; &amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:headnode][:provision][:verbose_logging] %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.log_level =&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:provision][:log_level] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% end &lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.omnibus.chef_version = &amp;#34;&amp;lt;%= @data[:chef_version] %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.vm.define &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:headnode][:slug] %&amp;gt;&amp;#34; do |h|
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  h.vm.box =&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:box] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.box_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:box_url] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.hostname = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:hostname] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.network(&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:private_network&lt;/span&gt;, {&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:ip&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;10.42.41.1&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.synced_folder &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/vagrant&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;disabled&lt;/span&gt;: &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.provider &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:virtualbox&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% @data[:headnode][:customizations].each &lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |key, value| &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;        p.customize [&amp;#34;modifyvm&amp;#34;, :id, &amp;#34;&amp;lt;%= key %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= value %&amp;gt;&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    end
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    h.vm.provision :chef_client do |chef|
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;% if @data[:chef_type] =&lt;/span&gt;= &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_zero&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.environment = &amp;#34;&amp;lt;%= @data[:headnode][:provision][:environment] %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.run_list = &amp;lt;%= @data[:run_list] %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.json = &amp;lt;%= @data[:node_definition] %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.chef_server_url = &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:chef_server_url] %&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      &amp;lt;% else %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.chef_server_url =&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:provision][:chef_server_url] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% end &lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.validation_key_path = &amp;#34;&amp;lt;%= @data[:headnode][:provision][:validation_key_path] %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.encrypted_data_bag_secret_key_path = &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:headnode][:provision][:encrypted_data_bag_secret_key_path] %&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.verbose_logging =&lt;/span&gt; &amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:headnode][:provision][:verbose_logging] %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      chef.log_level =&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:provision][:log_level] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.node_name = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:headnode][:provision][:node_name] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;#display nodes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% @data[:display_nodes][:nodes].each &lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |dn| &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  config.vm.define &amp;#34;&amp;lt;%= dn[:slug] %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34; do |dn_config|
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    dn_config.vm.box_url = &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= @data[:display_nodes][:global][:box_url] %&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    dn_config.vm.hostname =&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= dn[:hostname] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.box = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;%= @data[:display_nodes][:global][:box] %&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.synced_folder &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/vagrant&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;disabled&lt;/span&gt;: &lt;span style=&#34;color:#080&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.boot_timeout = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.provider &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:virtualbox&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |&lt;span style=&#34;color:#038&#34;&gt;p&lt;/span&gt;|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;% @data[:display_nodes][:global][:customizations].each &lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |key, value| &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      p.customize [&amp;#34;modifyvm&amp;#34;, :id, &amp;#34;&amp;lt;%= key %&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= value %&amp;gt;&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    &amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      p.customize [&amp;#34;modifyvm&amp;#34;, :id, &amp;#34;--macaddress1&amp;#34;, &amp;#34;&amp;lt;%=&lt;/span&gt; dn[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:mac&lt;/span&gt;] &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      p.customize [&amp;#34;createhd&amp;#34;, &amp;#34;--filename&amp;#34;, &amp;#34;../files/&amp;lt;%= dn[:slug] %&amp;gt;&lt;/span&gt;.vmdk&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;--size&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, 80*1024]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      p.customize [&amp;#34;&lt;/span&gt;storageattach&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, :id, &amp;#34;&lt;/span&gt;--storagectl&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;IDE&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Controller&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;--port&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, 0, &amp;#34;&lt;/span&gt;--device&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, 0, &amp;#34;&lt;/span&gt;--type&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;hdd&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;--medium&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;none&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      p.customize [&amp;#34;&lt;/span&gt;storageattach&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, :id, &amp;#34;&lt;/span&gt;--storagectl&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;IDE&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Controller&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;--port&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, 0, &amp;#34;&lt;/span&gt;--device&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, 0, &amp;#34;&lt;/span&gt;--type&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;hdd&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;--medium&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;../files/&amp;lt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%= dn[:slug] %&amp;gt;.vmdk&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      p.customize [&amp;#34;storagectl&amp;#34;, :id, &amp;#34;--name&amp;#34;, &amp;#34;SATA Controller&amp;#34;, &amp;#34;--add&amp;#34;, &amp;#34;sata&amp;#34;,  &amp;#34;--controller&amp;#34;, &amp;#34;IntelAHCI&amp;#34;, &amp;#34;--hostiocache&amp;#34;, &amp;#34;on&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;      p.customize [&amp;#34;storageattach&amp;#34;, :id, &amp;#34;--storagectl&amp;#34;, &amp;#34;SATA Controller&amp;#34;, &amp;#34;--port&amp;#34;, 1, &amp;#34;--device&amp;#34;, 0, &amp;#34;--type&amp;#34;, &amp;#34;hdd&amp;#34;, &amp;#34;--medium&amp;#34;, &amp;#34;../files/ipxe_&amp;lt;%=&lt;/span&gt; dn[&lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;:slug&lt;/span&gt;] &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;%&amp;gt;.vmdk&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    end
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  end
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  &amp;lt;% end %&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It renders a Vagrant File out of following data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;This file is used to generate Vagrantfile and run_test.sh and also run tests. It should contain _all_ data needed to render the templates and run teh tests.&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_version&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;11.12.4&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_type&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_zero&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;vagrant_template_file&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;vagrantfile.erb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;run_tests_template_file&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;run_tests.sh.erb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_server_url&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://192.168.1.2:4000&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;headnode&amp;#34;&lt;/span&gt; :
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;box&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;opscode-ubuntu-14.04&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;box_url&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hostname&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lg-head&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;bats_tests_dir&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pr&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;customizations&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--memory&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2048&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--cpus&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic1&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;nat&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic2&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;intnet&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic3&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic4&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nictype1&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Am79C970A&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nictype2&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Am79C970A&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--intnet2&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;provision&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_server_url&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://chefserver.ourdomain.com:40443&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;validation_key_path&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;~/.chef/validation.pem&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;encrypted_data_bag_secret_key_path&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;~/.chef/encrypted_data_bag_secret&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;node_name&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lg-head-projectXtest.liquid.glx&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;environment&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;pull_requests&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;verbose_logging&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;display_nodes&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;global&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;box&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;opscode-ubuntu-14.04&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;box_url&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;customizations&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--memory&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2048&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--cpus&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot1&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;floppy&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot2&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;net&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot3&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot4&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;provision&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;chef_server_url&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://chefserver.ourdomain.com:40443&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;validation_key_path&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;~/.chef/validation.pem&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;encrypted_data_bag_secret_key_path&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;~/.chef/encrypted_data_bag_secret&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;node_name&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lg-head-projectXtest.liquid.glx&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;environment&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;pull_requests&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;verbose_logging&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;display_nodes&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;global&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;box&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;opscode-ubuntu-14.04&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;box_url&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;customizations&amp;#34;&lt;/span&gt; : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--memory&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2048&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--cpus&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot1&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;floppy&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot2&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;net&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot3&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot4&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--intnet1&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nicpromisc1&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;allow-all&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic1&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;intnet&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic2&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic3&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic4&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nictype1&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Am79C970A&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--ioapic&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;nodes&amp;#34;&lt;/span&gt; : [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests-kiosk&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hostname&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;kiosk&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mac&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;5ca1ab1e0001&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;slug&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests-display&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hostname&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;display&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mac&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;5ca1ab1e0002&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a result we get an on-the-fly Vagrantfile that’s used during the testing:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Vagrant.configure(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |config|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.chef_zero.enabled = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.chef_zero.chef_server_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://192.168.1.2:4000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.chef_zero.roles = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../../roles/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.chef_zero.cookbooks = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../../cookbooks/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.chef_zero.environments = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../../environments/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.chef_zero.data_bags = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../integration/default/data_bags/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.omnibus.chef_version = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;11.12.4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.vm.define &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |h|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  h.vm.box = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;opscode-ubuntu-14.04&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.box_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.hostname = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lg-head&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.network(:private_network, {:ip =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;10.42.41.1&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.synced_folder &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/vagrant&amp;#34;&lt;/span&gt;, disabled: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.provider :virtualbox &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |p|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--memory&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2048&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--cpus&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;nat&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;intnet&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic3&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic4&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nictype1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Am79C970A&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nictype2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Am79C970A&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--intnet2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    h.vm.provision :chef_client &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |chef|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.environment = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;pull_requests&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.run_list = [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;role[lg-head-nocms]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;recipe[lg_live_build]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;recipe[lg_tftproot]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;recipe[lg_projectX]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;recipe[lg_test]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;recipe[test_mode::bats]&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;recipe[hostsfile::projectX]&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.json = {:sysctl=&amp;gt;{:params=&amp;gt;{:vm=&amp;gt;{:swappiness=&amp;gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;}, :net=&amp;gt;{:ipv4=&amp;gt;{:ip_forward=&amp;gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;}}}}, :test_mode=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :call_ep=&amp;gt;{:keyname=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ProjectX CI Production&amp;#34;&lt;/span&gt;, :fwdport=&amp;gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;33299&lt;/span&gt;}, :tftproot=&amp;gt;{:managed=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;}, :tags=&amp;gt;[], :lg_cms=&amp;gt;{:remote=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;github&amp;#34;&lt;/span&gt;}, :monitor=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :lg_grub=&amp;gt;{:cmdline=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;nomodeset biosdevname=0&amp;#34;&lt;/span&gt;}, :projectX=&amp;gt;{:repo_branch=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;development&amp;#34;&lt;/span&gt;, :display_host=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;42-a&amp;#34;&lt;/span&gt;, :kiosk_host=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;42-b&amp;#34;&lt;/span&gt;, :sensors_host=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;42-b&amp;#34;&lt;/span&gt;, :maps_url=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://www.google.com/maps/@8.135687,-75.0973243,17856994a,40.4y,1.23h/data=!3m1!1e3?esrch=Tactile::TactileAcme,Tactile::ImmersiveModeEnabled&amp;#34;&lt;/span&gt;}, :liquid_galaxy=&amp;gt;{:touchscreen_link=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/dev/input/lg_active_touch&amp;#34;&lt;/span&gt;, :screenshotd=&amp;gt;{:screen_rows=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;, :screen_columns=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;}, :screenshot_service=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :display_nodes=&amp;gt;[{:hostname=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;42-c&amp;#34;&lt;/span&gt;}, {:allowed_pages=&amp;gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Google Maps&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Pacman Doodle&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;jellyfish&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Doodle Selection&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ProjectX Video Player&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Composer kiosk&amp;#34;&lt;/span&gt;], :hostname=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;42-b&amp;#34;&lt;/span&gt;, :mac=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;5c:a1:ab:1e:00:02&amp;#34;&lt;/span&gt;, :features=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mandatory_windows, plain_gray, starry_skies&amp;#34;&lt;/span&gt;, :bad_windows_names=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Google Earth - Login Status&amp;#34;&lt;/span&gt;, :mandatory_windows_names=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;awesome&amp;#34;&lt;/span&gt;, :screens=&amp;gt;[{:display=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;:0&amp;#34;&lt;/span&gt;, :crtc=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt;, :grid_order=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;}], :screen_rotation=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;normal&amp;#34;&lt;/span&gt;, :audio_device=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{type hw; card DGX; device 0}&amp;#34;&lt;/span&gt;, :onboard_enable=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :keyboard_enable=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :mouse_enable=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :cursor_enable=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :background_extension=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;jpg&amp;#34;&lt;/span&gt;, :background_mode=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;zoom-fill&amp;#34;&lt;/span&gt;, :projectX=&amp;gt;{:extensions=&amp;gt;{:kiosk=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ProjectX Kiosk&amp;#34;&lt;/span&gt;, :google_properties_menu=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Google Properties Menu&amp;#34;&lt;/span&gt;, :onboard=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Onboard&amp;#34;&lt;/span&gt;, :no_right_click=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Right Click Killer&amp;#34;&lt;/span&gt;, :render_statistics=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Render Statistics&amp;#34;&lt;/span&gt;}, :browser_slug=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lgS0&amp;#34;&lt;/span&gt;, :urls=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://www.google.com/maps&amp;#34;&lt;/span&gt;, :ros_nodes=&amp;gt;[{:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;rfreceiver_reset&amp;#34;&lt;/span&gt;, :pkg=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;rfreceiver&amp;#34;&lt;/span&gt;, :type=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;kill_browser.py&amp;#34;&lt;/span&gt;}, {:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;proximity&amp;#34;&lt;/span&gt;, :pkg=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;maxbotix&amp;#34;&lt;/span&gt;, :type=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sender.py&amp;#34;&lt;/span&gt;}, {:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;spacenav&amp;#34;&lt;/span&gt;, :pkg=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;spacenav_node&amp;#34;&lt;/span&gt;, :type=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;spacenav_node&amp;#34;&lt;/span&gt;}, {:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;leap&amp;#34;&lt;/span&gt;, :pkg=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;leap_motion&amp;#34;&lt;/span&gt;, :type=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sender.py&amp;#34;&lt;/span&gt;}, {:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX_nav&amp;#34;&lt;/span&gt;, :pkg=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX_nav&amp;#34;&lt;/span&gt;, :type=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX_nav&amp;#34;&lt;/span&gt;}, {:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;onboard&amp;#34;&lt;/span&gt;, :pkg=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;onboard&amp;#34;&lt;/span&gt;, :type=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;listener.py&amp;#34;&lt;/span&gt;}, {:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;rosbridge&amp;#34;&lt;/span&gt;, :pkg=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;rosbridge_server&amp;#34;&lt;/span&gt;, :type=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;rosbridge_websocket&amp;#34;&lt;/span&gt;, :params=&amp;gt;[{:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;certfile&amp;#34;&lt;/span&gt;, :value=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/home/lg/etc/ros.crt&amp;#34;&lt;/span&gt;}, {:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;keyfile&amp;#34;&lt;/span&gt;, :value=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/home/lg/etc/ros.key&amp;#34;&lt;/span&gt;}]}]}, :browser_infinite_url=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://lg-head/projectX-loader.html&amp;#34;&lt;/span&gt;}, {:hostname=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;42-a&amp;#34;&lt;/span&gt;, :mac=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;5c:a1:ab:1e:00:01&amp;#34;&lt;/span&gt;, :features=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mandatory_windows, plain_gray, starry_skies, erroneous_text&amp;#34;&lt;/span&gt;, :bad_windows_names=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Google Earth - Login Status&amp;#34;&lt;/span&gt;, :mandatory_windows_names=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;awesome&amp;#34;&lt;/span&gt;, :screens=&amp;gt;[{:display=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;:0&amp;#34;&lt;/span&gt;, :crtc=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt;, :grid_order=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;}], :keyboard_enable=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :mouse_enable=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :cursor_enable=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :background_extension=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;jpg&amp;#34;&lt;/span&gt;, :background_mode=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;zoom-fill&amp;#34;&lt;/span&gt;, :nvidia_mosaic=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :manual_layout=&amp;gt;{:&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;default&lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1024x768+0+0&amp;#34;&lt;/span&gt;}, :projectX=&amp;gt;{:extensions=&amp;gt;{:display=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ProjectX Large Display&amp;#34;&lt;/span&gt;, :pacman=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;pacman&amp;#34;&lt;/span&gt;, :render_statistics=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Render Statistics&amp;#34;&lt;/span&gt;}, :browser_slug=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lgS0&amp;#34;&lt;/span&gt;, :urls=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://www.google.com/maps&amp;#34;&lt;/span&gt;, :ros_nodes=&amp;gt;[{:name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;geodata&amp;#34;&lt;/span&gt;, :pkg=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;geodata&amp;#34;&lt;/span&gt;, :type=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;geodata_server.py&amp;#34;&lt;/span&gt;}]}, :browser_infinite_url=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://lg-head/projectX-loader.html&amp;#34;&lt;/span&gt;, :default_browser_bin=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;google-chrome&amp;#34;&lt;/span&gt;, :allowed_pages=&amp;gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Google Maps&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Pacman Doodle&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;jellyfish&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ProjectX Video Player&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Composer wall&amp;#34;&lt;/span&gt;]}], :has_cec=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;, :google_office=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;, :viewsync_master=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;42-b&amp;#34;&lt;/span&gt;, :has_touchscreen=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;, :has_spacenav=&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;, :support_name=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-ci&amp;#34;&lt;/span&gt;, :podium_interface=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://lg-head&amp;#34;&lt;/span&gt;, :podium_display=&amp;gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;42-b:default&amp;#34;&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.chef_server_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://192.168.1.2:4000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.validation_key_path = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;~/.chef/validation.pem&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.encrypted_data_bag_secret_key_path = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;~/.chef/encrypted_data_bag_secret&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.verbose_logging = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.log_level = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      chef.node_name = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;lg-head-projectXtest.liquid.glx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;#&lt;/span&gt;display nodes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.vm.define &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests-kiosk&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |dn_config|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.box_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.hostname = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;kiosk&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.box = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;opscode-ubuntu-14.04&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.synced_folder &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/vagrant&amp;#34;&lt;/span&gt;, disabled: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.boot_timeout = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.provider :virtualbox &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |p|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--memory&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2048&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--cpus&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;floppy&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;net&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot3&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot4&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--intnet1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nicpromisc1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;allow-all&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;intnet&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic3&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic4&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nictype1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Am79C970A&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--ioapic&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--macaddress1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;5ca1ab1e0001&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;createhd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--filename&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../files/projectX-pull-requests-kiosk.vmdk&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--size&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;80&lt;/span&gt;*&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1024&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;storageattach&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--storagectl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;IDE Controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--device&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hdd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--medium&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;storageattach&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--storagectl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;IDE Controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--device&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hdd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--medium&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../files/projectX-pull-requests-kiosk.vmdk&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;storagectl&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SATA Controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--add&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sata&amp;#34;&lt;/span&gt;,  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;IntelAHCI&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--hostiocache&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;storageattach&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--storagectl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SATA Controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--device&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hdd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--medium&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../files/ipxe_projectX-pull-requests-kiosk.vmdk&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config.vm.define &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests-display&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |dn_config|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.box_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-14.04_chef-provisionerless.box&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.hostname = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;display&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.box = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;opscode-ubuntu-14.04&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.synced_folder &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/vagrant&amp;#34;&lt;/span&gt;, disabled: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.boot_timeout = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dn_config.vm.provider :virtualbox &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; |p|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--memory&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;2048&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--cpus&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;floppy&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;net&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot3&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--boot4&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--intnet1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nicpromisc1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;allow-all&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;intnet&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic2&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic3&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nic4&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--nictype1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Am79C970A&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--ioapic&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--macaddress1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;5ca1ab1e0002&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;createhd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--filename&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../files/projectX-pull-requests-display.vmdk&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--size&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;80&lt;/span&gt;*&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1024&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;storageattach&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--storagectl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;IDE Controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--device&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hdd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--medium&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;none&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;storageattach&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--storagectl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;IDE Controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--device&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hdd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--medium&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../files/projectX-pull-requests-display.vmdk&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;storagectl&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SATA Controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--add&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sata&amp;#34;&lt;/span&gt;,  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;IntelAHCI&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--hostiocache&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      p.customize [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;storageattach&amp;#34;&lt;/span&gt;, :id, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--storagectl&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SATA Controller&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--port&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--device&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hdd&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;--medium&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../files/ipxe_projectX-pull-requests-display.vmdk&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  end
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally we have the environment stored in one Vagrantfile. The missing part is to how to run tests on it.&lt;/p&gt;
&lt;p&gt;The testing script does the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#/bin/bash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;set&lt;/span&gt; -e
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# FUNCTIONS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; halt_vm () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;vms&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;vboxmanage list vms | grep &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;vagrant_&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$1&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;_&amp;#34;&lt;/span&gt; | awk {&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;print $1&amp;#39;&lt;/span&gt;} | sed s/&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;//g&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Stopping VM &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$vms&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;stop_result&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(for&lt;/span&gt; vm in &lt;span style=&#34;color:#369&#34;&gt;$vms&lt;/span&gt; ; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; vboxmanage controlvm &lt;span style=&#34;color:#369&#34;&gt;$vm&lt;/span&gt; poweroff; &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$?&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;done)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Output of stopping VM &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$1&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; : &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$stop_result&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; boot_vm () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;vms&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;vboxmanage list vms | grep &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;vagrant_&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$1&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;_&amp;#34;&lt;/span&gt; | awk {&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;print $1&amp;#39;&lt;/span&gt;} | sed s/&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;//g&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Booting VM &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$vms&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;start_result&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(for&lt;/span&gt; vm in &lt;span style=&#34;color:#369&#34;&gt;$vms&lt;/span&gt; ; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; vboxmanage startvm &lt;span style=&#34;color:#369&#34;&gt;$vm&lt;/span&gt; --type headless; &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$?&lt;/span&gt;; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;done)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Output of booting VM &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$1&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; : &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$start_result&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Sleeping additional 15 secs after peacefull boot&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sleep &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; add_keys () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; i in &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;find /var/lib/jenkins/.ssh/id_rsa* | grep -v &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;.pub&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt; ; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;do&lt;/span&gt; ssh-add &lt;span style=&#34;color:#369&#34;&gt;$i&lt;/span&gt; ; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#vars&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;knifeclient_name&lt;/span&gt;=lg-head-projectXtest.liquid.glx
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;=projectX-pull-requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# TEST SCENARIO&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; test/vagrant
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# teardown of previous sessions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vagrant destroy projectX-pull-requests-kiosk -f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vagrant destroy projectX-pull-requests-display -f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vagrant destroy &lt;span style=&#34;color:#369&#34;&gt;$headnode_name&lt;/span&gt; -f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Not managing knife client because =&amp;gt; chef_zero &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;All ssh keys presented below&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh-add -l
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# headnode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vagrant up &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# displaynodes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;result&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;vboxmanage convertfromraw ../files/ipxe.usb ../files/ipxe_projectX-pull-requests-kiosk.vmdk --format=VMDK ; vagrant up projectX-pull-requests-kiosk; &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$?&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests-kiosk : &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;result&lt;/span&gt;=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;$(&lt;/span&gt;vboxmanage convertfromraw ../files/ipxe.usb ../files/ipxe_projectX-pull-requests-display.vmdk --format=VMDK ; vagrant up projectX-pull-requests-display; &lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$?&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projectX-pull-requests-display : &lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# test phase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;OPTIONS&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;vagrant ssh-config  &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; | grep -v &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; | awk -v &lt;span style=&#34;color:#369&#34;&gt;ORS&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;{print &amp;#34;-o &amp;#34; $1 &amp;#34;=&amp;#34; $2}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scp &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;OPTIONS&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; ../integration/projectX-pr/bats/*.bats vagrant@&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;:/tmp/bats_tests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;OPTIONS&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/usr/local/bin/bats /tmp/bats_tests/pre_build_checks.bats&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;halt_vm projectX-pull-requests-kiosk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;halt_vm projectX-pull-requests-display
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Building teh ISO (it may take a long time)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;OPTIONS&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/usr/local/bin/bats /tmp/bats_tests/build_iso.bats&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;OPTIONS&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/usr/local/bin/bats /tmp/bats_tests/set_grub_to_make_partitions.bats&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Booting nodes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;boot_vm projectX-pull-requests-kiosk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;boot_vm projectX-pull-requests-display
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Sleeping 30 secs for the DNS to boot and setting the grub to boot the ISO&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sleep &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;OPTIONS&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/usr/local/bin/bats /tmp/bats_tests/set_grub_to_boot_the_iso.bats&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Sleeping for 4 mins for the displaynodes to boot fresh ISO&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sleep &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;240&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Running the tests inside the headnode:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;OPTIONS&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;headnode_name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/usr/local/bin/bats /tmp/bats_tests/post_checks.bats&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So finally we get the following pipeline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone Chef pull request from GitHub&lt;/li&gt;
&lt;li&gt;Create Vagrantfile on the basis of Vagrantfile template&lt;/li&gt;
&lt;li&gt;Create run_tests.sh script for running the tests&lt;/li&gt;
&lt;li&gt;Destroy all previously created Vagrant boxes&lt;/li&gt;
&lt;li&gt;Create one Chef Vagrant box&lt;/li&gt;
&lt;li&gt;Create ISO Vagrant boxes with ipxe bootloader&lt;/li&gt;
&lt;li&gt;Converge the Vagrant box with Chef&lt;/li&gt;
&lt;li&gt;Copy BATS tests onto the headnode&lt;/li&gt;
&lt;li&gt;Run initial BATS tests that build an ISO&lt;/li&gt;
&lt;li&gt;Boot display nodes with the newly created ISO&lt;/li&gt;
&lt;li&gt;Run final integration tests on the stack&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Elapsed time—​between 40 and 50 minutes.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Cron Wrapper: Keep your cron jobs environment sane</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/02/cron-wrapper-keep-your-cron-jobs/"/>
      <id>https://www.endpointdev.com/blog/2015/02/cron-wrapper-keep-your-cron-jobs/</id>
      <published>2015-02-06T00:00:00+00:00</published>
      <author>
        <name>Richard Templet</name>
      </author>
      <content type="html">
        &lt;p&gt;It is becoming more common for developers to not use the operating system packages for programming languages. Perl, Python, Ruby, and PHP are all making releases of new versions faster than the operating systems can keep up (at least without causing compatibility problems).
There are now plenty of tools to help with this problem. For Perl we have &lt;a href=&#34;https://perlbrew.pl/&#34;&gt;Perlbrew&lt;/a&gt; and &lt;a href=&#34;https://github.com/tokuhirom/plenv&#34;&gt;plenv&lt;/a&gt;. For Ruby there is &lt;a href=&#34;https://github.com/sstephenson/rbenv&#34;&gt;rbenv&lt;/a&gt; and &lt;a href=&#34;https://rvm.io/&#34;&gt;RVM&lt;/a&gt;. For Python there is &lt;a href=&#34;https://virtualenv.pypa.io/en/latest/&#34;&gt;Virtualenv&lt;/a&gt;. For PHP there is &lt;a href=&#34;https://github.com/wilmoore/php-version&#34;&gt;PHP version&lt;/a&gt;.
These tools are all great for many different reasons but they all have issues when being used with cron jobs. The cron environment is very minimal on purpose. It has a very restrictive path, very few environment variables and other issues. As far as I know, all of these tools would prefer using the env command to get the right version of the language you are using. This works great while you are logged in but tends to fail bad as a cron job. The cron wrapper script is a super simple script that you put before whatever you want to run in your crontab which will ensure you have the right environment variables set.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#!/bin/bash -l
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;exec&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$@&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The crontab entry would look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;34&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;12&lt;/span&gt; * * * bin/cron-wrapper bin/blog-update.pl&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The -l on the executing of bash makes it act like it is logging in. Therefore it picks up anything in the ~/.bash_profile and has that available to the env command. This means the cron job runs in the same environment that is setup when you run it from the command line, helping to stop those annoying times where it works fine from the command line but breaks in cron. Jon Jensen went into much greater detail on the benefits of using the -l &lt;a href=&#34;/blog/2013/05/login-shells-in-scripts-called-from-cron/&#34;&gt;here&lt;/a&gt;.
Hope this helps!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>CentOS 7 on Hetzner server with more than 2 TB disk</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/01/centos-7-on-hetzner-server-with-more/"/>
      <id>https://www.endpointdev.com/blog/2015/01/centos-7-on-hetzner-server-with-more/</id>
      <published>2015-01-22T00:00:00+00:00</published>
      <author>
        <name>Spencer Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;We use a variety of hosting providers for ourselves and our clients, including &lt;a href=&#34;https://www.hetzner.de/&#34;&gt;Hetzner&lt;/a&gt;.  They provide good servers for a great price, have decent support, and we’ve been happy with them for our needs.&lt;/p&gt;
&lt;p&gt;Recently I was given the task of building out a new development server for one of our clients, and we wanted it to be set up identically to another one of their servers but with CentOS 7. I placed the order for the hardware with Hetzner and then began the procedure for installing the OS.&lt;/p&gt;
&lt;p&gt;Hetzner provides a scripted install process that you can kick off after booting the machine into rescue mode. I followed this process and selected CentOS 7 and proceeded through the whole process without a problem. After rebooting the server and logging in to verify everything, I noticed that the disk space was capped at 2 TB, even though the machine had two 3 TB drives in it (in hardware RAID 1). I looked at the partitions and found the partition table was “msdos”. Ah ha!&lt;/p&gt;
&lt;p&gt;At this point &lt;a href=&#34;/blog/2013/11/installing-centos-5-on-3tb-drive/&#34;&gt;painful memories of running into this problem before&lt;/a&gt; hit me. I reviewed our notes of what we had done last time, and felt like it was worth a shot even though this time I’m dealing with CentOS 7. I went through the steps up to patching anaconda and then found that anaconda for CentOS 7 is newer and the files are different. I couldn’t find any files that care about the partition table type, so I didn’t patch anything.&lt;/p&gt;
&lt;p&gt;I then tried to run the CentOS 7 install as-is. This only got me so far because I then ran into trouble with NetworkManager timing out and not starting.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2015/01/centos-7-on-hetzner-server-with-more/image-0.png&#34;&gt;&lt;img alt=&#34;screen shot of CentOS 7 installer failing&#34; height=&#34;300&#34; src=&#34;/blog/2015/01/centos-7-on-hetzner-server-with-more/image-0.png&#34; width=&#34;400&#34;/&gt;&lt;/p&gt;
&lt;div style=&#34;font-size:11px; font-style:italic; clear: both; margin-bottom:15px;&#34;&gt;A screenshot of the CentOS 7 installer failing (anaconda) similar to what I was seeing.&lt;/div&gt;&lt;/a&gt;
&lt;p&gt;Baffled, I looked into what may have been causing the trouble and discovered that the network was not set up at all and it looked as if no network interfaces existed. WHAT?? At this point I dug through dmesg and found that the network interfaces did indeed exist but udevd had renamed them. Ugh!&lt;/p&gt;
&lt;p&gt;Many new Linux distributions are naming network interfaces based on their physical connection to the system: those embedded on the motherboard get named em1, em2, etc. Apparently I missed the memo on this one, as I was still expecting eth0, eth1, etc. And from all indications, so was NetworkManager because it could not find the network interfaces!&lt;/p&gt;
&lt;p&gt;Rather than spend more time going down this route, I decided to change gears and look to see if there was any way to patch the Hetzner install scripts to use a GPT partition table with my install instead of msdos. I found and read through the source code for their scripts and soon stumbled on something that just might solve my problem. In the file /root/.oldroot/nfs/install/functions.sh I found mention of a config variable &lt;strong&gt;FORCE_GPT&lt;/strong&gt;.  If this is set to “1” then it will try to use a GPT partition table unless it thinks the OS won’t like it, and it thinks that CentOS won’t like it (no matter the version). But if you set &lt;strong&gt;FORCE_GPT&lt;/strong&gt; to “2” it will use a GPT partition table no matter what. This config setting just needs to be added to the file you edit where you list out your partitions and LVM volumes.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FORCE_GPT 2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PART /boot ext3 512M
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PART lvm   vg0  all
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LV  vg0  swap swap   swap  32G
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LV  vg0  root  /     ext4 100G
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LV  vg0  home  /home ext4 400G&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I then ran the installer script and added the secret config option and&amp;hellip; Bingo! It worked perfectly! No need to manually patch anything or install manually. And now we have a CentOS 7 server with full 3 TB of disk space usable.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(parted) print
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Model: DELL PERC H710 (scsi)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Disk /dev/sda: 3000GB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Sector size (logical/physical): 512B/512B
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Partition Table: gpt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Disk Flags: pmbr_boot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Number  Start   End     Size    File system  Name  Flags
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 3      1049kB  2097kB  1049kB                     bios_grub
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 1      2097kB  539MB   537MB   ext3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 2      539MB   3000GB  2999GB                     lvm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      </content>
    </entry>
  
    <entry>
      <title>Highlights of OpenWest conference 2014</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/05/highlights-of-openwest-conference-2014/"/>
      <id>https://www.endpointdev.com/blog/2014/05/highlights-of-openwest-conference-2014/</id>
      <published>2014-05-16T00:00:00+00:00</published>
      <author>
        <name>Spencer Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Last week I was able to not only attend the &lt;a href=&#34;https://www.openwest.org/&#34;&gt;OpenWest&lt;/a&gt; conference, held in Orem, UT at the &lt;a href=&#34;https://www.uvu.edu/&#34;&gt;UVU&lt;/a&gt; campus, but I was also able to be a presenter. This year’s conference had their largest attendance to date and also their most diverse list of tracks and presentations. Many of the presentations are posted on &lt;a href=&#34;https://www.youtube.com/channel/UC0wbcfzV-bHhABbWGXKHwdg&#34;&gt;YouTube&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My own presentation was on &lt;strong&gt;Git Workflows That Work&lt;/strong&gt; (&lt;a href=&#34;/blog/2014/05/git-workflows-that-work/&#34;&gt;previous blog post&lt;/a&gt;, &lt;a href=&#34;https://speakerdeck.com/localfilmmaker/git-workflows-that-work&#34;&gt;talk slides&lt;/a&gt;). It was part of the “Tools” track and went pretty well. Rather than recap my own presentation, I’d like to briefly touch on a few others I enjoyed.&lt;/p&gt;
&lt;h3 id=&#34;building-a-scalable-codebase-using-domain-driven-design&#34;&gt;Building a Scalable Codebase using Domain Driven Design&lt;/h3&gt;
&lt;p&gt;The folks at &lt;a href=&#34;https://www.insidesales.com/&#34;&gt;Insidesales.com&lt;/a&gt; presented on Domain Driven Design. This was something I had heard of but wasn’t very familiar with. The main principles of DDD are: understand the business purpose (not just the app requirements), rigorous organization (of code, services, design, etc.), distinct layers, and functional cohesion. The biggest point I got was that all business logic should be located in a “domain” separated from the application and separated from the data. The application is then just a thin wrapper around API calls to the business logic within the domain. DDD is different from MVC though because each layer is distinct and could have its own design. Thus MVC could be a smaller piece within a given layer.&lt;/p&gt;
&lt;p&gt;As I listened to this presentation I remembered a project I worked on at a previous job building a Feeds Admin to create and manage product feeds to third parties (search engines, shopping portals, etc). I had built that using DDD without realizing it at the time. It came naturally as I wanted to cleanly organize and separate the logic for each feed (with different data pulled from the database, different feed formats, different transport methods, etc.). So I definitely have seen the benefits and principles of DDD in practice.&lt;/p&gt;
&lt;h3 id=&#34;mentoring-devs-into-devops&#34;&gt;Mentoring Devs into DevOps&lt;/h3&gt;
&lt;p&gt;This presentation by Justin Carmony discussed how his team has been going through a transition of empowering developers to also do some Ops work. They had roughly 30 developers, 2 operations admins, and over 300 servers in their infrastructure. They also had several different tools and ways of doing things between the different teams of developers; for example, some used Capistrano and some used Ansible, and some used Jenkins and others Travis CI, some used Vagrant and some didn’t, etc.). They considered hiring for a new DevOps position to own and mentor everyone in standardizing tools and processes, but instead promoted one of their star developers to that role. This was a tough decision because it meant that they would not be able to use his skills a developer. But Justin said it was definitely worth it because within just 3 months this developer had helped everyone to standardized on tools and release procedures, making the entire team more productive and efficient.&lt;/p&gt;
&lt;p&gt;They also switched to using &lt;a href=&#34;https://www.saltstack.com&#34;&gt;Salt&lt;/a&gt; for configuration management for all servers at this same time. And to empower the developers and expose them to the world of Ops, they switched to using Vagrant managed by Salt for all development environments. Developers were able to make some Salt changes if needed to their own environment, and then submit a git pull request to Ops for peer review of the Salt configuration and Ops would then merge it and deploy it to production if they accepted the changes.&lt;/p&gt;
&lt;p&gt;Justin mentioned that it is still an on-going transition for them. Ultimately the main points he presented are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is a long continuum or gradient of skill level and access level between Dev and Ops and it is better to think of DevOps as a continuum as well and not a strict role with strict access levels. Some Devs can have access to dashboards and graphs of systems while others can manage build tools and configuration. “DevOps” can be spread out.&lt;/li&gt;
&lt;li&gt;Team culture matters. It can be very difficult to improve processes and get people to embrace change for the better, so positive influences by everyone involved can make a big difference. Business owners need to be supportive as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;retrospectus&#34;&gt;Retrospectus&lt;/h3&gt;
&lt;p&gt;Daniel Evans gave an interesting presentation on retrospective meetings. For those not familiar with these meetings, they are for discussing “what went well” and “what could be improved” over the last project/sprint/time period/etc. I’ve participated in these types of meetings for many years and have seen the benefits that can come from them. Daniel also talked about what these meetings are &lt;strong&gt;not&lt;/strong&gt;: they are not meant for deep dives into problems, blame, or griping. They should be focused on the good that has been done and on solutions to fix problems going forward. And even then, if the solutions are not quickly apparent then you should not spend too much time trying to find them. Those conversations should happen outside of this meeting.&lt;/p&gt;
&lt;p&gt;His presentation was quite short and didn’t really cover much that I didn’t know already. However, at the end he then turned the rest of the time into a retrospective meeting reviewing his presentation, with everyone participating. This turned out to be pretty fun and was a good exercise, and covered a lot more then a regular Q &amp;amp; A session would have.&lt;/p&gt;
&lt;h3 id=&#34;other-links&#34;&gt;Other Links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=TiEddaKOwo4&#34;&gt;Keynote speech by Lt. Governor of Utah, Spencer J. Cox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.openwest.org/schedule/&#34;&gt;Conference schedule and list of presenters&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Puppet, Salt, and DevOps (a review of the MountainWest DevOps conference)</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/03/puppet-salt-and-devops-review-of/"/>
      <id>https://www.endpointdev.com/blog/2014/03/puppet-salt-and-devops-review-of/</id>
      <published>2014-03-27T00:00:00+00:00</published>
      <author>
        <name>Spencer Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Last week I attended the MountainWest DevOps conference held in Salt Lake City, Utah. This was a one day conference with a good set of presenters and lightning talks. There were several interesting topics presented, but I’ll only review a few I wanted to highlight.&lt;/p&gt;
&lt;h3 id=&#34;i-serve-no-master&#34;&gt;I Serve No Master!&lt;/h3&gt;
&lt;p&gt;Aaron Gibson of &lt;a href=&#34;http://www.adaptivecomputing.com/&#34;&gt;Adaptive Computing&lt;/a&gt; discussed a very common problem with Puppet (and other configuration management systems): they work well in the scenario they were designed for but what about when the situation isn’t typical? Aaron had a situation where developers and QA engineers could instantiate systems themselves via OpenStack, however the process for installing their company’s software stack on those VMs was inconsistent, mostly manual, and took many hours. One of the pain points he shared, which I related to, was dealing with registering a puppet node with a puppet master—​the sometimes painful back and forth of certificate issuing and signing.&lt;/p&gt;
&lt;p&gt;His solution was to remove the puppet master completely from the process. Instead he created a bash wrapper script to execute a workflow around what was needed, still using puppet manifests on each system but run locally. This wrapper tool, called “Builder”, relies on property files to customize the config and allow the script to manage different needs. This new script allowed them to keep using puppet to manage these self-serve OpenStack servers gaining the benefits of consistency and removing manual setup steps, providing the ability to automate installs with Jenkins or other tools. But it freed them from having to use a puppet master for nodes that were disposable. It also helped to reduce software install time from 12 hours down to a 11 minutes.&lt;/p&gt;
&lt;p&gt;His Builder tool is still an internal only tool for his company, but he discussed some next steps he would like to add, including better reporting and auditing of executions. I pinged him after the conference on twitter and mentioned that &lt;a href=&#34;http://rundeck.org/&#34;&gt;Rundeck&lt;/a&gt; might be a good fit to fill that gap. I used Rundeck for 2 years at my last job, integrating nicely with other automation tools and providing reporting and auditing as well as access control of arbitrary jobs.&lt;/p&gt;
&lt;h3 id=&#34;automating-cloud-factories-and-the-internet-assembly-line-with-saltstack&#34;&gt;Automating cloud factories and the internet assembly line with SaltStack&lt;/h3&gt;
&lt;p&gt;Tom Hatch of &lt;a href=&#34;https://saltstack.com/&#34;&gt;Salt Stack&lt;/a&gt; spoke about Salt as an automation and remote execution platform. I’ve done quite a bit of work with Salt recently with a client and so I was pretty familiar with Salt. But one thing that he mentioned I didn’t know was that Salt was originally designed as a Cloud management tool, not necessarily a configuration management tool. However in the course of time configuration management became a higher priority for the Salt dev team to focus on. Tom mentioned that recently they have been working on Cloud management tools again—​providing integration with Rackspace, AWS, xen, and more. I’ll have to dig more into these tools and give them a try.&lt;/p&gt;
&lt;h3 id=&#34;how-i-learned-to-stop-worrying-and-love-devops&#34;&gt;How I Learned to Stop Worrying and Love DevOps&lt;/h3&gt;
&lt;p&gt;Bridget Kromhout of 8thBridge (since aquired by &lt;a href=&#34;https://web.archive.org/web/20150322054413/https://www.fluid.com/news/fluid-acquires-social-crm-platform-8thbridge&#34;&gt;Fluid&lt;/a&gt;) spoke on the culture of DevOps and her journey from a corporate, strictly siloed environment to a small start-up that embraced DevOps. One of the first things she brought up that was different was the focus and approach to goals of each organization. In an organization where Ops teams are strictly separate from Developers, they often butt heads and have a limited vision of priorities. Each focuses on the goals of their own team or department, and have little understanding of the goals of the other departments. This leads to an adversarial relationship and culture of not caring much about different teams or departments.&lt;/p&gt;
&lt;p&gt;In contrast, the organization that embraces DevOps as a culture will see to it that Ops and Devs work together on whatever solution best reaches the goals of the whole organization. In doing so, barriers will have to be questioned. Any “special snowflake” servers/applications/etc. that only one person knows and can touch can’t exist in this culture. Instead, any unique customizations need to be minimized through automation, documentation (sharing knowledge), and reporting/monitoring. This doesn’t mean root access for all—​but it means reducing barriers as much as possible. Good habits from the Ops world to keep include: monitoring, robustness, security, scaling, and alerting.&lt;/p&gt;
&lt;p&gt;The main pillars of DevOps are: culture, automation, measurement, and sharing. Culture is important and can be supportive or rejecting of the other pillars. Without a culture in the organization that supports DevOps, it will fizzle back into siloed “us vs. them” enmity.&lt;/p&gt;
&lt;p&gt;Thanks to all the presenters and those that put on the conference. It was a great experience and I am glad I attended.&lt;/p&gt;
&lt;h3 id=&#34;links&#34;&gt;Links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/biggiemac/mtnwest_prez/blob/master/I%20Serve%20No%20Master!%20-%20Aaron%20Gibson%20-%20MTN%20West%20DevOps%202014.pdf&#34;&gt;Slide deck for Aaron Gibson: “Serve No Master!”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.slideshare.net/bridgetkromhout/how-i-learnedtostopworryingandlovedevops201403&#34;&gt;Slide deck for Bridget Kromhout: DevOps culture&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Provisioning a Development Environment with Packer, Part 2</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/03/provisioning-development-environment_14/"/>
      <id>https://www.endpointdev.com/blog/2014/03/provisioning-development-environment_14/</id>
      <published>2014-03-14T00:00:00+00:00</published>
      <author>
        <name>Mike Farmer</name>
      </author>
      <content type="html">
        &lt;p&gt;In my &lt;a href=&#34;/blog/2014/03/provisioning-development-environment/&#34;&gt;previous post&lt;/a&gt; on provisioning a development environment with &lt;a href=&#34;https://www.packer.io&#34;&gt;Packer&lt;/a&gt; I walked through getting a server setup with an operating system installed. This post will be focused setting up &lt;a href=&#34;https://www.ansible.com&#34;&gt;Ansible&lt;/a&gt; so that I can setup my development environment just the way I like it. Packer supports many different methods for provisioning. After playing with some of them, I decided that Ansible was a good mix of simplicity and functionality.&lt;/p&gt;
&lt;p&gt;A Packer provisioner is simply a configuration template that is added to the json configuration file. The “provisioners” section of the configuration file takes an array of json objects which means that you aren’t stuck with just one kind of provisioner. For example, you could run some shell scripts using the &lt;a href=&#34;https://www.packer.io/docs/provisioners/shell.html&#34;&gt;shell provisioner&lt;/a&gt;, then upload some files using the &lt;a href=&#34;https://www.packer.io/docs/provisioners/file.html&#34;&gt;File Uploads&lt;/a&gt; provisioner, followed by your devops tool of choice (&lt;a href=&#34;https://web.archive.org/web/20150706082720/https://www.packer.io/docs/provisioners/puppet-masterless.html&#34;&gt;puppet&lt;/a&gt;, &lt;a href=&#34;https://web.archive.org/web/20190508114914/http://packer.io/docs/provisioners/salt-masterless.html&#34;&gt;salt&lt;/a&gt;, &lt;a href=&#34;https://web.archive.org/web/20210615181646/https://www.packer.io/docs/provisioners/chef-solo&#34;&gt;chef&lt;/a&gt;, or &lt;a href=&#34;https://web.archive.org/web/20210615182101/https://www.packer.io/docs/provisioners/ansible-local&#34;&gt;ansible&lt;/a&gt;). You can even &lt;a href=&#34;https://web.archive.org/web/20160405051550/https://www.packer.io/docs/extend/provisioner.html&#34;&gt;roll-your-own&lt;/a&gt; provisioner if desired. Here’s an example provisioner setup for the shell provisioner:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;variables&amp;#34;: {...},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;builders&amp;#34; : [...],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;provisioners&amp;#34; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;type&amp;#34;: &amp;#34;shell&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;inline&amp;#34;: [ &amp;#34;echo foo&amp;#34; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;sudo-and-user-considerations&#34;&gt;Sudo and User Considerations&lt;/h3&gt;
&lt;p&gt;Packer will login to the server over ssh and run your provisioners. The big headache that always comes out of this is some provisioners require sudo, or being logged in as root, to run their commands. Packer, however, will login as the user that was created during the build stage (see the “builders” section in the code snippet in the &lt;a href=&#34;/blog/2014/03/provisioning-development-environment/&#34;&gt;previous post&lt;/a&gt;). There are a couple of ways to handle this. First, you can do everything in packer as root. I don’t love this approach because I like to simulate the way that I setup a machine by hand and I never login as root if I can help it. The second method is to grant your user sudo access. This gets a little tricky so I’ll just show the a code snippet and then explain it below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;type&amp;#34;: &amp;#34;shell&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;execute_command&amp;#34;: &amp;#34;echo &amp;#39;{{user `ssh_pass`}}&amp;#39; | {{ .Vars }} sudo -E -S sh &amp;#39;{{ .Path }}&amp;#39;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;inline&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;echo &amp;#39;%sudo    ALL=(ALL)  NOPASSWD:ALL&amp;#39; &amp;gt;&amp;gt; /etc/sudoers&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Utilizing the shell provisioner, the execute_command option is used to specify to the provisioner that whenever a command is run, use this command. The commands provided to the inline array are compiled into a single shell script which is injected as the .Path variable. To quote the Packer &lt;a href=&#34;https://www.packer.io/docs/provisioners/shell.html&#34;&gt;documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The -S flag tells sudo to read the password from stdin, which in this case is being piped in with the value of [the user variable ssh_pass.] The -E flag tells sudo to preserve the environment, allowing our environmental variables to work within the script.&lt;/p&gt;
&lt;p&gt;By setting the execute_command to this, your script(s) can run with root privileges without worrying about password prompts.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Taking advantage of this trick, a command can now be placed in the inline section that will add all members of the sudo group to the sudoers file granting them permission to use sudo without a password. Now I know this isn’t secure but for my purpose, which is to create a custom development enviornment on a Virtual Machine running only on my machine, this will be just fine. I also use this example to illustrate how to run commands as sudo.&lt;/p&gt;
&lt;h3 id=&#34;the-ansible-provisioner&#34;&gt;The Ansible Provisioner&lt;/h3&gt;
&lt;p&gt;Once the shell provisioner is working, Ansible can be installed on the new machine and then executed using Packer’s Ansible provisioner. The easiest way to do this that I found was to have the shell provisioner install Ansible on the virtual machine as well as upload an ssh public key so that the Ansible user could log in. My “provisioners” section looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;provisioners&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;type&amp;#34;: &amp;#34;shell&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;inline&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;mkdir .ssh&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;echo &amp;#39;{{user `public_key`}}&amp;#39; &amp;gt;&amp;gt; .ssh/authorized_keys&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;type&amp;#34;: &amp;#34;shell&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;execute_command&amp;#34;: &amp;#34;echo &amp;#39;{{user `ssh_pass`}}&amp;#39; | {{ .Vars }} sudo -E -S sh &amp;#39;{{ .Path }}&amp;#39;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;inline&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;add-apt-repository ppa:rquillo/ansible&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;apt-get update&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;apt-get install -y ansible&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;echo &amp;#39;%sudo    ALL=(ALL)  NOPASSWD:ALL&amp;#39; &amp;gt;&amp;gt; /etc/sudoers&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;type&amp;#34;: &amp;#34;ansible-local&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;playbook_file&amp;#34;: &amp;#34;site.yml&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This configuration uses a variable in which I placed an ssh public key (I could also have used the File Uploads provisioner for this), installs Ansible from an updated PPA, and grants the user sudo priveliges via the sudo group as explained above. This also shows how you can execute more than one statement at a time using the “shell” provisioner.&lt;/p&gt;
&lt;h3 id=&#34;ansible-provisioning-tip&#34;&gt;Ansible Provisioning Tip&lt;/h3&gt;
&lt;p&gt;This tip could probably apply to any of the devops tools you’d like to use. If you are creating your Ansible yml files for the first time, you will likely run into the issue where you spend a lot of time waiting for the machine to build and provision only to discover your Ansible script is wrong. Troubleshooting becomes a problem because if anything fails during the provision, Packer will stop running and delete the Virtual Machine leaving you with no option other than fixing your mistake and then waiting for the entire process to run again.&lt;/p&gt;
&lt;p&gt;One way I found around this is to take the last section of the provisioners array out, build your machine, and then move the base machine into a new directory once it’s been successfully built. From there you can start the machine manually and then run the ansible-playbook command from your local machine while you develop your playbook. Once you have a working playbook, add the ansible-local section back to the provisioners array and rebuild your machine with Packer. That should speed up your development and troubleshooting cycles.&lt;/p&gt;
&lt;h3 id=&#34;a-hiccup-with-ansible-and-packer&#34;&gt;A Hiccup with Ansible and Packer&lt;/h3&gt;
&lt;p&gt;Ansible allows you to create template files that can be used for configuration files and the like. According to the documentation, you can specify the location of the templates and other files using the playbook_paths option in the provisioner. I could not get this to work and after a lot of troubleshooting and looking at the code for the provisioner I am convinced there is a bug copying the playbook_paths directories to the remote machine. I’ve &lt;a href=&#34;https://groups.google.com/forum/#!topic/packer-tool/RrIGFH3K1bE&#34;&gt;posted on the Packer discussion group&lt;/a&gt; about this but haven’t had any response on it yet. Once I get to the bottom the issue I’ll post an update here.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Packer has turned out to be a fabulous resource for me in quickly ramping up development environments. Ansible has also been a breath for fresh air for provisioning those machines. I’ve previously used &lt;a href=&#34;https://www.getchef.com/chef/&#34;&gt;chef&lt;/a&gt; and other devops tools which only led to a great deal of frustration. I’m happy to have some new tools in my belt that took very little time to learn and to get working.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Provisioning a Development Environment with Packer, Part 1</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/03/provisioning-development-environment/"/>
      <id>https://www.endpointdev.com/blog/2014/03/provisioning-development-environment/</id>
      <published>2014-03-12T00:00:00+00:00</published>
      <author>
        <name>Mike Farmer</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently needed to reconstruct an old development environment for a project I worked on over a year ago. The codebase had aged a little and I needed old versions of just about everything from the OS and database to Ruby and Rails. My preferred method for creating a development environment is to setup a small virtual machine (VM) that mimics the production environment as closely as possible.&lt;/p&gt;
&lt;h3 id=&#34;introducing-packer&#34;&gt;Introducing Packer&lt;/h3&gt;
&lt;p&gt;I have been hearing a lot of buzz lately about &lt;a href=&#34;https://www.packer.io/&#34;&gt;Packer&lt;/a&gt; and wanted to give it a shot for setting up my environment. Packer is a small command line tool written in the increasingly popular &lt;a href=&#34;https://golang.org/&#34;&gt;Go&lt;/a&gt; programming language. It serves three primary purposes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Building a machine based on a set of configuration parameters&lt;/li&gt;
&lt;li&gt;Running a provisioner to setup the machine with a desired set of software and settings&lt;/li&gt;
&lt;li&gt;Performing any post processing instructions on that machine&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Packer is really simple to install and I would refer you to their &lt;a href=&#34;https://www.packer.io/docs/installation.html&#34;&gt;great documentation&lt;/a&gt; to get it set up. Once set up, you will have the &lt;code&gt;packer&lt;/code&gt; command at your disposal. To build a new machine, all you need to is call:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;packer build my_machine.json&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The file my_machine.json can be the name of any json file and contains all the information Packer needs to setup your machine. The configuration json has three major sections: variables, builders, and provisioners. Variables are simply key value pairs that you can reference later in the builders and provisioners sections.&lt;/p&gt;
&lt;h3 id=&#34;the-builder-configuration&#34;&gt;The Builder Configuration&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://web.archive.org/web/20160318014945/https://www.packer.io/docs/templates/builders.html&#34;&gt;Builders&lt;/a&gt; takes an array of JSON objects that specify different ways to build your machines. You can think of them as instructions on how to get your machine setup and running. For example, to get a machine up and running you need to create a machine, install an operating system (OS) and create a user so that you can login to the machine. There are many different types of builders, but for the example here, I’ll just use the &lt;code&gt;vmware-iso&lt;/code&gt; machine type. Here’s a working JSON configuration file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;variables&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;ssh_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mikefarmer&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;ssh_pass&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mikefarmer&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;hostname&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;packer-test&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;builders&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;vmware-iso&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;iso_url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;os/ubuntu-12.04.4-server-amd64.iso&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;iso_checksum&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;e83adb9af4ec0a039e6a5c6e145a34de&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;iso_checksum_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;md5&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;ssh_username&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{{user `ssh_name`}}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;ssh_password&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{{user `ssh_pass`}}&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;ssh_wait_timeout&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;20m&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;http_directory&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;preseeds&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;http_port_min&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9001&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;http_port_max&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9001&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;shutdown_command&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;echo {{user `ssh_pass`}} | sudo -S shutdown -P now&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;boot_command&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;esc&amp;gt;&amp;lt;esc&amp;gt;&amp;lt;enter&amp;gt;&amp;lt;wait&amp;gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/install/vmlinuz noapic &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/precise_preseed.cfg &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;debian-installer=en_US auto locale=en_US kbd-chooser/method=us &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hostname={{user `hostname`}} &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;fb=false debconf/frontend=noninteractive &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;keyboard-configuration/variant=USA console-setup/ask_detect=false &amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;initrd=/install/initrd.gz -- &amp;lt;enter&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&#34;https://web.archive.org/web/20160318014945/https://www.packer.io/docs/templates/builders.html&#34;&gt;documentation&lt;/a&gt; for these settings is really good but I want to point out a few things that weren’t immediately clear. Some of these pertain mostly to the vmware-iso builder type, but I believe they are worth pointing out because some of them apply to other builder types as well.&lt;/p&gt;
&lt;p&gt;First, the &lt;code&gt;iso_url&lt;/code&gt; setting can be either an absolute path, a relative path, or a fully qualified url. The relative path is relative to the directory where you run the &lt;code&gt;packer&lt;/code&gt; command. So here, when I run &lt;code&gt;packer&lt;/code&gt;, I need to make sure that I do so from a directory that has an os subdirectory with the ubuntu iso located therein.&lt;/p&gt;
&lt;p&gt;Next, once the ISO is downloaded, Packer will automatically start up your VMWare client and boot the virtual machine. Immediately after that, Packer will start up a &lt;a href=&#34;https://en.wikipedia.org/wiki/Virtual_Network_Computing&#34;&gt;VNC client and server&lt;/a&gt; along with a mini web server to provide information for your machine. The &lt;code&gt;http_port_min&lt;/code&gt; and &lt;code&gt;http_port_max&lt;/code&gt; specify which ports to use for the VNC clients. Setting them to the same will allocate just that port for it to use. The &lt;code&gt;http_directory&lt;/code&gt; setting provides the name of a local directory to use for the mini web server as the document root. This is important for providing your VM with a preseed file, more about the preseed file will be discussed below.&lt;/p&gt;
&lt;p&gt;Since we are using Ubuntu as our main machine, we will need to use sudo to send the shutdown command. The &lt;code&gt;shutdown_command&lt;/code&gt; setting is used to gracefully shut down the machine at the conclusion of the run and provisioning of the machine.&lt;/p&gt;
&lt;h3 id=&#34;installing-your-os&#34;&gt;Installing your OS&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;boot_command&lt;/code&gt; is a series of keystrokes that you can send to the machine via VNC. If you have set up a Linux machine from scratch you know that you have to enter in a bunch of information to the machine about how to set it up for the first time such as time zone, keyboard layout, how to partition the hard drive, host name, etc. All these keystrokes needed to set up your machine can be used here.&lt;/p&gt;
&lt;p&gt;But if you think about it, that’s a ton of keystrokes and this command could get quite long. A better way to approach this is to use a preseed file. A &lt;a href=&#34;https://web.archive.org/web/20210518144703/https://help.ubuntu.com/lts/installation-guide/s390x/apb.html&#34;&gt;preseed.cfg&lt;/a&gt; file contains the same information you enter when you setup a machine for the first time. This isn’t something provided by Packer, but it is provided by the operating system to automatically provision machines. For Ubuntu, a preseed file is used like so:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you boot from the startup media (in this case an ISO image), you can choose the location of the preseed file via a URL.&lt;/li&gt;
&lt;li&gt;The preseed file is uploaded into memory and the configuration is read.&lt;/li&gt;
&lt;li&gt;The installation process begins using information from the preseed file to enter the values where the user would normally enter them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So how do we get the preseed file up to the machine? Remember that little web server that Packer sets up? Well, the IP address and port is made available to the virtual machine when it boots from the ISO. The following line tells the OS where to find the web server and the configuration file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/precise_preseed.cfg&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Strings in Packer can be interpolated using a simple template format similar to &lt;a href=&#34;https://mustache.github.io/&#34;&gt;mustache&lt;/a&gt;. The double curly braces tell Packer to insert a variable here instead of text. The HTTPIP and HTTPPort variables are made available by Packer to the template.&lt;/p&gt;
&lt;p&gt;One more important note about the preseed file: You need to make sure that the settings for the username and password are the same as listed in your variables section so that you can login to the machine once it is built. Where do you get a preseed file? I found &lt;a href=&#34;https://kappataumu.com/articles/creating-an-Ubuntu-VM-with-packer.html&#34;&gt;one on a blog titled Packer in 10 Minutes&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/kappataumu&#34;&gt;@kappataumu&lt;/a&gt;. I only had to modify a few settings that were specific to my setup.&lt;/p&gt;
&lt;p&gt;Remember that &lt;code&gt;http_directory&lt;/code&gt; mentioned above? Well, that directory needs to include your preseed file. I’ve named mine &lt;code&gt;precise_preseed.cfg&lt;/code&gt; for Ubuntu 12.04 Precise Pangolin.&lt;/p&gt;
&lt;p&gt;Next up is provisioning but that is such a big topic by itself that I’ll move that into a separate blog post. The config file above will work as-is and once run it should setup a basic Ubuntu server for you. Go ahead and give it a try and let me know in the comments how it worked out for you.&lt;/p&gt;
&lt;h3 id=&#34;super-powers&#34;&gt;Super Powers&lt;/h3&gt;
&lt;p&gt;I said that Packer has 3 primary purposes earlier. Well, I lied.&lt;/p&gt;
&lt;p&gt;Packer’s superpower is that it can perform those 3 purposes over any number of machines, whether virtual, hosted, or otherwise in parallel. Supported machines are currently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Amazon EC2 (AMI)&lt;/li&gt;
&lt;li&gt;DigitalOcean&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Google Compute Engine&lt;/li&gt;
&lt;li&gt;OpenStack&lt;/li&gt;
&lt;li&gt;QEMU&lt;/li&gt;
&lt;li&gt;VirtualBox&lt;/li&gt;
&lt;li&gt;VMware&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consider for a moment that you can now automatically setup and provision multiple machines with the same environment using a single command. Now you are seeing the power of Packer.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Setting a server role in Salt (comparing Puppet and Salt)</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/12/setting-server-role-in-salt-comparing/"/>
      <id>https://www.endpointdev.com/blog/2013/12/setting-server-role-in-salt-comparing/</id>
      <published>2013-12-23T00:00:00+00:00</published>
      <author>
        <name>Spencer Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;There are many ways to solve a given problem, and this is no truer than with configuration management. Salt (&lt;a href=&#34;https://saltstack.com&#34;&gt;https://saltstack.com&lt;/a&gt;) is a fairly new tool in the configuration management arena joining the ranks of Puppet, Chef, and others. It has quickly gained and continues to grow in popularity, boasting its scalable architecture and speed. And so with multiple tools and multiple ways to use each tool, it can get a little tricky to know how best to solve your problem.&lt;/p&gt;
&lt;p&gt;Recently I’ve been working with a client to convert their configuration management from Puppet to Salt. This involved reviewing their Puppet configs and designs and more-or-less mapping them to the equivalent for Salt. Most features do convert pretty easily. However, we did run into something that didn’t at first/assigning a role to a server.&lt;/p&gt;
&lt;p&gt;We wanted to preserve the “feeling” of the configs where possible. In Puppet they had developed and used a convention for using some custom variables in their configs to assign an “environment” and a “role” for each server. These variables were assigned in the node and role manifests. But in Salt we struggled to find a similar way to do that, but here is what we learned.&lt;/p&gt;
&lt;p&gt;In Puppet, once a server’s “role” and “environment” variables were set, then they could be used in other manifest files to select the proper source for a given config file like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    file    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;#34;/etc/rsyslog.conf&amp;#34;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            source  =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;puppet:///rsyslog/rsyslog.conf.$hostname&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;puppet:///rsyslog/rsyslog.conf.$system_environment-$system_role&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;puppet:///rsyslog/rsyslog.conf.$system_role&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;puppet:///rsyslog/rsyslog.conf.$system_environment&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;puppet:///rsyslog/rsyslog.conf&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ensure  =&amp;gt; present,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            owner   =&amp;gt; &amp;#34;root&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            group   =&amp;gt; &amp;#34;root&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            mode    =&amp;gt; &amp;#34;644&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Puppet will search the list of source files in order and use the first one that exists. For example, if $hostname = &amp;lsquo;myniftyhostname&amp;rsquo; and $system_environment = &amp;lsquo;qa&amp;rsquo; and $system_role = &amp;lsquo;sessiondb&amp;rsquo;, then it will use rsyslog.conf.myniftyhostname if it exists on the Puppet master, or if not then use rsyslog.conf.qa-sessiondb if it exists, or if not then rsyslog.conf.sessiondb if it exists, or if not then rsyslog.conf.qa if it exists, or if not then rsyslog.conf.&lt;/p&gt;
&lt;p&gt;In Salt, environment is built into the top.sls file, where you match your servers to their respective state file(s), and can be used within state files as {{ env }}. Salt also allows for multiple sources for a managed file to be listed in order and it will use the first one that exists in the same way as Puppet. We were nearly there; however, setting the server role variable was not as straight forward in Salt.&lt;/p&gt;
&lt;p&gt;We first looked at using Jinja variables (which is the default templating system for Salt), but soon found that setting a Jinja variable in one state file does not carry over to another state file. Jinja variables remain only in the scope of the file they were created in, at least in Salt.&lt;/p&gt;
&lt;p&gt;The next thing we looked at was using Pillar, which is a way to set custom variables from the Salt master to given hosts (or minions). Pillar uses a structure very similar to Salt’s top.sls structure- matching a host with its state files. But since the hostnames for this client vary considerably and don’t lend themselves to pattern matching easily, this would be cumbersome to manage both the state top.sls file and the Pillar top.sls file and keep them in sync. It would require basically duplicating the list of hosts in two files, which could get out of sync over time.&lt;/p&gt;
&lt;p&gt;We asked the salt community on #salt on Freenode.net how they might solve this problem, and the recommended answer was to set a custom grain. Grains are a set of properties for a given host, collected from the host itself- such as, hostname, cpu architecture, cpu model, kernel version, total ram, etc. There are multiple ways to set custom grains, but after some digging we found how to set them from within a state file. This meant that we could do something like this in a “role” state file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# sessiondb role
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# {{ salt[&amp;#39;grains.setval&amp;#39;](&amp;#39;server_role&amp;#39;,&amp;#39;sessiondb&amp;#39;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;include:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - common
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - postgres&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then within the common/init.sls and postgres/init.sls state files we could use that server_role custom grain in selecting the right source file, like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/etc/rsyslog.conf:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  file.managed:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - source:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - salt://rsyslog/files/rsyslog.conf.{{ grains[&amp;#39;host&amp;#39;] }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - salt://rsyslog/files/rsyslog.conf.{{ env }}-{{ grains[&amp;#39;server_role&amp;#39;] }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - salt://rsyslog/files/rsyslog.conf.{{ grains[&amp;#39;server_role&amp;#39;] }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - salt://rsyslog/files/rsyslog.conf.{{ env }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - salt://rsyslog/files/rsyslog.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - mode: 644
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - user: root
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - group: root&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This got us to our desired config structure. But like I said earlier, there are probably many ways to handle this type of problem. This may not even be the best way to handle server roles and environments in Salt, if we were more willing to change the “feeling” of the configs. But given the requirements and feedback form our client, this worked fine.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Use Ansible/Jinja2 templates to change file content based on target OS</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/12/use-ansiblejinja2-templates-to-change/"/>
      <id>https://www.endpointdev.com/blog/2013/12/use-ansiblejinja2-templates-to-change/</id>
      <published>2013-12-19T00:00:00+00:00</published>
      <author>
        <name>Emanuele “Lele” Calò</name>
      </author>
      <content type="html">
        &lt;p&gt;In the End Point hosting team we really love automating repetitive tasks, especially when it involves remembering many little details which can over time be forgotten, like differences of &lt;em&gt;coreutils&lt;/em&gt; location between some versions of Ubuntu (Debian), CentOS (Red Hat) and OpenBSD variants.&lt;/p&gt;
&lt;p&gt;In our environment we bind the backup SSH user authorized_keys entry to a custom command in order to have it secured by being, among other aspects, tied to a specific rsync call.&lt;/p&gt;
&lt;p&gt;So in our case the content of our &lt;strong&gt;CentOS&lt;/strong&gt; authorized_keys would be something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;command&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/bin/nice -15 /usr/bin/rsync --server --daemon .&amp;#34;&lt;/span&gt;,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAB3[...]&lt;span style=&#34;color:#369&#34;&gt;Q&lt;/span&gt;== endpoint-backup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sadly that’s only true for CentOS systems so that if you want to &lt;strong&gt;automate the distribution of authorized_keys&lt;/strong&gt; (as we’ll show in another post) to different Linux distributions (like &lt;strong&gt;Ubuntu&lt;/strong&gt;) you may need to tweak it to comply to the new standard “/usr/bin” location, which will be eventually adopted by all new Linux versions overtime.. RHEL 7.x onward included.&lt;/p&gt;
&lt;p&gt;To do the OS version detection we decided to use an &lt;strong&gt;Ansible&lt;/strong&gt;/&lt;strong&gt;Jinja2&lt;/strong&gt; template by placing the following line in the Ansible task:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- name: Deploy /root/.ssh/authorized_keys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  template: &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=all/root/.ssh/authorized_keys.j2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;dest&lt;/span&gt;=/root/.ssh/authorized_keys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;owner&lt;/span&gt;=root
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;group&lt;/span&gt;=root
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;mode&lt;/span&gt;=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0600&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And inside the actual file place a slightly modified version of the line above:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;command&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{% if ansible_os_family != &amp;#34;&lt;/span&gt;RedHat&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34; %}/usr{% endif %}/bin/nice -15 /usr/bin/rsync --server --daemon .&amp;#34;&lt;/span&gt;,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAB3[...]&lt;span style=&#34;color:#369&#34;&gt;Q&lt;/span&gt;== endpoint-backup&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So that if the target OS is not part of the “RedHat” family it will add the “/usr” in front of the “/bin/nice” absolute path.&lt;/p&gt;
&lt;p&gt;Easy peasy, ain’t it?&lt;/p&gt;
&lt;p&gt;Now go out there and exploit this feature to all your needs.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
