<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[MISSION IMPOSSIBLE CODE]]></title><description><![CDATA[MISSION IMPOSSIBLE CODE]]></description><link>https://missionimpossiblecode.io</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1657921341170/cs4hFZkR-.png</url><title>MISSION IMPOSSIBLE CODE</title><link>https://missionimpossiblecode.io</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 12:17:03 GMT</lastBuildDate><atom:link href="https://missionimpossiblecode.io/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[DevSecOps Engineering for Developers: Automated Least Privilege IAM Policy Generation for AWS From User Activity]]></title><description><![CDATA[Why Least Privilege Security Engineering Is Frequently Skipped or Done Loosely
In a previous life, I was on a team that reviewed the IAM policies specified by developers when they created new Cloud applications or required additional permissions for ...]]></description><link>https://missionimpossiblecode.io/devsecops-engineering-for-developers-automated-least-privilege-iam-policy-generation-for-aws-from-user-activity</link><guid isPermaLink="true">https://missionimpossiblecode.io/devsecops-engineering-for-developers-automated-least-privilege-iam-policy-generation-for-aws-from-user-activity</guid><category><![CDATA[Security]]></category><category><![CDATA[DevSecOps]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Least Privilege]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Tue, 12 Sep 2023 13:41:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694525344048/77827482-505c-42d2-ac2f-bb203dd79756.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-why-least-privilege-security-engineering-is-frequently-skipped-or-done-loosely">Why Least Privilege Security Engineering Is Frequently Skipped or Done Loosely</h2>
<p>In a previous life, I was on a team that reviewed the IAM policies specified by developers when they created new Cloud applications or required additional permissions for application updates that used new cloud features on AWS. The review was for both Infrastructure as Code automation for deployment as well as for Application runtime operations.</p>
<p>This task generally falls to the developer of the code because only they know the code operation well enough to ensure all code-paths are accommodated in least privilege discovery and testing during security engineering.</p>
<p>While reviewing the developer-authored permissions was very intense - the process developers used to determine the permissions was more intensive. A common methodology is to lock down the application or automation runtime permissions and go from failure to failure to discover the minimum permissions that are needed. This is incredibly labor intensive and on very large application or automation code bases it can add weeks to the release cycle. There is a much better way to do this that is much more productive and nearly eliminates deep rabbit hole explorations on permission dependencies in complex code.</p>
<h2 id="heading-a-well-kept-secret">A Well Kept Secret</h2>
<p>AWS <a target="_blank" href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-policy-generation.html#access-analyzer-policy-generation-console">IAM Access Analyzer (Least Privilege) Policy Generator</a> is already one of the best-kept secrets for AWS. The most tedious way to discover the least privileges for an application or infrastructure as code is to lock down a profile and work your way through all the failures one by one. There are multiple problems with the approach:</p>
<ul>
<li><p>The sheer number of hours.</p>
</li>
<li><p>Many situations where deep dependencies between permissions make it difficult to understand exactly what permission is needed. This can lead to many failure iterations to isolate a single permission.</p>
</li>
<li><p>This is tough enough on small stacks, but at the level of production scaling stacks I've worked with - it becomes impossible.</p>
</li>
<li><p>This task is generally pushed to Application Developers in the case of applications - but it is not only out of many of their daily proficiencies - it is a very specialized area of cloud computing - permissions.</p>
</li>
</ul>
<h2 id="heading-a-better-approach">A Better Approach</h2>
<p>If one could log EVERY permissions request, a much better approach is to give an application admin permissions, run it to exercise its capabilities and then aggregate the log information. There are many 3rd party scripts and utilities to do this for various permission systems. However, in AWS, there is a specific feature known as "Policy Generation" within the IAM Access Analyzer service. It analyzes CloudTrail logs (which have records for every required permission) to determine the least amount of permissions required for a given application or automation to run. The catch is the documentation and all blogs I've seen before always talk about using it against an IAM Role. This makes the setup more challenging because, unless you've done it before, running your application or automation using a role may require new skills.</p>
<h2 id="heading-an-even-better-kept-secret">An Even Better Kept Secret</h2>
<p>However, IAM Access Analyzer has an even better-kept secret hidden within. Someone can correct me if I'm wrong, but I cannot find AWS documentation, blogs or videos on the following nuance of this feature.</p>
<p>When you are viewing an IAM User, clicking the "Generate policy" button in that user profile allows IAM Access Analyzer Policy Generator to use a USER ID to analyze the logs. This is very useful because using AWS Keys to exercise the application or automation is familiar to many more code developers - this means that one does not need to be as much of an AWS security specialist to leverage this automated security engineering feature. Using keys is also sufficient for security engineering since you can delete the keys or the entire user after doing your engineering.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694523829450/7d11abfa-c325-4295-b03d-e7fa0b938357.png" alt class="image--center mx-auto" /></p>
<p>Generate Policy button at the bottom of the default Permissions tab in an IAM user.</p>
<h2 id="heading-practices-to-consider">Practices to Consider</h2>
<p>Additional good practices for using IAM Access Analyzer include:</p>
<ul>
<li><p>Making it Comprehensive</p>
<ul>
<li><p>Since IAM Access Analyzer is reverse engineering from log data - the entire application or automation must be exercised in order to discover all permissions.</p>
</li>
<li><p>For automation it can be easy to forget that it may create alternative infrastructure based on input parameters.</p>
</li>
<li><p>Since access analyzer takes a time range, you can run the automation many times to surface all the required permissions.</p>
</li>
<li><p>It may be prudent to create a test plan to ensure nothing is missed.</p>
</li>
<li><p>For Infrastructure as Code, the Initial run must be captured because IaC reports "true" for things that are already configured as desired ("Desired State") and does not attempt to configure them - so there will be no CloudTrail log data for things that are skipped due to already being configured.</p>
</li>
<li><p>For Infrastructure as Code, the teardown must also be captured if clean removal of resources with the least privilege permissions is desired.</p>
</li>
</ul>
</li>
<li><p>Making it Faster:</p>
<ul>
<li><p>Create a new role or user for the purpose of least privilege discovery so that there will be no other activity on the user id or role. This helps ensure that unneeded permissions are not added to the least privilege permissions since there simply isn't any unrelated activity to track.</p>
</li>
<li><p>Consider creating a new Cloud Trail to also limit the amount of data that will need to be analyzed. This also allows you to analyze account level data even if your organization has Cloud Trail implemented and collecting to a bucket the local account does not have access to. Don't forget to disable or delete it when done to prevent duplicate data collection.</p>
</li>
<li><p>When running the Access Analyzer you can also bound it by region that it examines - so if the entire application or automation runs can be in a single region, the data analysis will go even faster.</p>
</li>
</ul>
</li>
<li><p>Making it Cleaner:</p>
<ul>
<li><p>If you created a dedicated Cloud Trail, when done, deconfigure it and delete the bucket that houses the data.</p>
</li>
<li><p>When done, delete either the KEYS or the entire User or Role that was used for least privilege discovery.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-variation-for-working-examples-and-templates">Variation for Working Examples and Templates</h2>
<p>The tightest least privilege IAM permissions are coded to specific resources. However, if your automation needs to handle many regions or many naming schemes, then you may need to replace the generated policy's variables with '*', these include any references with curly braces such as <code>${region}</code> and <code>${account}</code>.</p>
<h2 id="heading-documentation-and-references">Documentation and References</h2>
<p>The following do not cover the user id functionality as far as I can tell:</p>
<ul>
<li><p><a target="_blank" href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-policy-generation.html#access-analyzer-policy-generation-console">IAM Access Analyzer (Least Privilege) Policy Generator</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=SJQSWeogUWs">How to use IAM Access Analyzer policy generation | Amazon Web Services</a></p>
</li>
</ul>
<p><a target="_blank" href="https://www.pexels.com/photo/two-women-looking-at-the-code-at-laptop-1181263/">Photo by Christina Morillo</a></p>
]]></content:encoded></item><item><title><![CDATA[No More Blurring - Securely Auto Block AWS Account Info For Tutorial Screenshots and Videos]]></title><description><![CDATA[Obscuring sensitive information like AWS account IDs in screenshots and videos is tedious and error-prone. Even with video editing tools that simplify the process, I still have to repeatedly add and remove blurring boxes as the view changes.
You know...]]></description><link>https://missionimpossiblecode.io/securely-auto-block-aws-account-info-for-tutorial-screenshots-and-videos</link><guid isPermaLink="true">https://missionimpossiblecode.io/securely-auto-block-aws-account-info-for-tutorial-screenshots-and-videos</guid><category><![CDATA[AWS]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[AWS Community Builder]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Fri, 21 Jul 2023 19:25:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690029136076/96a1e393-e7b7-426d-a086-6176ba621740.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Obscuring sensitive information like AWS account IDs in screenshots and videos is tedious and error-prone. Even with video editing tools that simplify the process, I still have to repeatedly add and remove blurring boxes as the view changes.</p>
<p>You know what I mean, it generally looks something like this when redacted:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689967880756/9ea27b67-2697-443e-9718-8df978a6f065.png" alt class="image--center mx-auto" /></p>
<p>Fortunately, there's a simple solution for hiding the AWS account information in screenshots and videos. Ad blockers that use filter lists like AdBlock Plus can be configured with a custom rule to automatically remove the account ID span. Here is the AdBlock formatted rule that will hide the information.</p>
<pre><code class="lang-plaintext">[Adblock Plus 2.0]
! Version:
! Title: CustomList
! Last modified:
! Expires:
! Homepage:
!
console.aws.amazon.com##span[data-testid="awsc-nav-account-menu-button"]
</code></pre>
<p>Note: Instead of trying to manually configure this file or a rule, see the section below "Direct Sourcing by URL"</p>
<p>So it will now display like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689966329385/81277510-d3e9-46e5-b1ec-8e8d782ecb24.png" alt class="image--center mx-auto" /></p>
<p>The custom ad blocker rule obscures AWS account IDs in screenshots, but still allows easy access to view them when necessary. The account information drop-down arrow remains visible and clicking it reveals the account number, your current IAM user and all the regular links that are normally on this menu.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689967000022/9dd0216e-36bb-4376-9e87-c21997b2bbe0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-adblock-extensions-disable-acceptable-ads">AdBlock Extensions - Disable "Acceptable Ads"</h3>
<p>If you are using either the AdBlock or AdBlock Plus extension, you will likely need to disable the built-in list "Acceptable Ads" as discussed in this <a target="_blank" href="https://helpcenter.getadblock.com/hc/en-us/articles/9738549743251-Seeing-unblocked-ads-Start-here-">AdBlocks Support Solution</a>. When I did not disable this list, the AWS Account information would continue to be visible.</p>
<h3 id="heading-built-in-ad-blocker-in-vivaldi">Built In Ad Blocker in Vivaldi</h3>
<p>I generally use Vivaldi which supports AdBlocker rules natively. Since it does not depend on third-party browser extensions to remove the AWS Account data display, it does not open one up to additional security implications of third-party extensions that have page-level access to your AWS console. It also is not configured with the "Acceptable Ads" list.</p>
<h3 id="heading-direct-sourcing-by-url">Direct Sourcing By URL</h3>
<p>Most ad blockers can be configured with an import URL. If you would like to import this rule and any improvements to it in the future, you can import from this URL: <a target="_blank" href="https://gitlab.com/missionimpossiblecode/adblock-for-tutorials/-/raw/main/adblockconfig.txt">https://gitlab.com/missionimpossiblecode/adblock-for-tutorials/-/raw/main/adblockconfig.txt</a></p>
<p>Here are the <a target="_blank" href="https://helpcenter.getadblock.com/hc/en-us/articles/9738523403027-Introduction-to-Filter-Lists">instructions for configuring filter lists for AdBlock</a>.</p>
]]></content:encoded></item><item><title><![CDATA[AWS CloudShell "Run From Web" Configuration Scripts]]></title><description><![CDATA[AWS CloudShell joins the ranks of hostless shells for operating in your cloud environment. Cloud shells are a huge help to training and enablement scenarios because they remove the pain of fussy configuration of a user-owned endpoint - which can have...]]></description><link>https://missionimpossiblecode.io/aws-cloudshell-run-from-web-configuration-scripts</link><guid isPermaLink="true">https://missionimpossiblecode.io/aws-cloudshell-run-from-web-configuration-scripts</guid><category><![CDATA[Mission Impossible Code]]></category><category><![CDATA[Bash]]></category><category><![CDATA[AWS]]></category><category><![CDATA[amazon-linux]]></category><category><![CDATA[AWS CloudShell]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Tue, 04 Apr 2023 18:47:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-iXjUZlCsd0/upload/c32714d63cd15b15ad743397163654c6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://docs.aws.amazon.com/cloudshell/latest/userguide/welcome.html">AWS CloudShell</a> joins the ranks of hostless shells for operating in your cloud environment. Cloud shells are a huge help to training and enablement scenarios because they remove the pain of fussy configuration of a user-owned endpoint - which can have an endless variety of challenges installing utilities for something like managing a Kubernetes cluster. Additionally, once you get through the process of getting new utilities working, you often have to wonder what you broke that you don't know about yet.</p>
<p>On the flip side, AWS CloudShell cannot include every possible utility you may need or want to use. AWS CloudShell is currently based on Amazon Linux 2 and so the relevant commands apply.</p>
<h2 id="heading-know-before-you-install">Know Before You Install</h2>
<p>Here are some tips to make note of when considerating doing installations and configurations within AWS CloudShell:</p>
<ul>
<li><p>AWS CloudShell is based on Amazon Linux, which means you must use yum. When you are reading installation documentation you want to look at the yum commands. The Amazon Linux yum repos are on by default.</p>
</li>
<li><p>Since it is Amazon Linux, favor <a target="_blank" href="https://repost.aws/knowledge-center/ec2-install-extras-library-software">installing from Amazon Linux Extras</a> if the software you are looking for is available there. These extras instructions also tell how to add the RHEL extras repo to Amazone Linux.</p>
</li>
<li><p>Check if what you want is already installed on this <a target="_blank" href="https://docs.aws.amazon.com/cloudshell/latest/userguide/vm-specs.html#pre-installed-software">list of AWS CloudShell Pre-installed Software</a>.</p>
</li>
<li><p>AWS CloudShell allows passwordless sudo by default - so using sudo to install things is easy.</p>
</li>
<li><p>Making installations idempotent makes sure they are quick and error free. This essentially means checking if something is already present and only installing or configuring it if it is missing.</p>
</li>
<li><p>AWS CloudShell does not persist configuration changes after you log off of the AWS account. They are also remembered on a per-account, per-region basis - so an installation into CloudShell while viewing us-east-1 is not retained if you switch to eu-central-1. This really means that at any given time you use CloudShell it will not be likely to have the configuration you need.</p>
</li>
<li><p>For data storage (not installs and configuration) the $HOME folder can store up to 1GB of data per-region and is deleted after 120 days from the end of the last session.</p>
</li>
<li><p>The $HOME directory will give a disk error at 1GB. A workaround is to use sudo in the /home directory for commands that will download more than 1GB. This content will not be retained, but if it is just temporary dependencies (like with complex terraform stacks) then at least you can get the work done.</p>
</li>
<li><p>With regard to Kubernetes administration, the <a target="_blank" href="https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html#create-kubeconfig-automatically">automatic kubeconfig setup using AWS CLI</a> works great in AWS CloudShell. The code below includes config-kubectl.sh which do this configuration for you. It will enumerate the EKS clusters and prompt for one if you do not provide one.</p>
</li>
</ul>
<h2 id="heading-run-from-web-to-the-rescue">Run From Web To The Rescue</h2>
<p>For some years now I've been using what I call "Run From Web" code in both Bash and PowerShell to enable anyone to quickly use scripted solutions directly from git without a lot of fuss. This makes them ideal for helping with the fact that AWS CloudShell forgets configuration changes on logoff and only remembers them on a per-account, per-region basis in the first place.</p>
<p>The specific scripts also check if something is installed and don't install it if already present, making them lightning fast when doing incremental configuration.</p>
<h2 id="heading-code-review">Code Review</h2>
<p>Below is the code block for installing terraform, notice:</p>
<ul>
<li><p>It self-documents how to run it from the web. (including hard lessons learned about not piping downloaded scripts directly into bash.)</p>
</li>
<li><p>The <code>if</code> statement ensures we only do work if we need to. Also if you run this terraform configuration and then later run the add-all.sh script - which also includes terraform - the installs layer up without duplicate work or errors (idempotency).</p>
</li>
<li><p>The final <code>echo</code> command tells us the installed version. It functions as both a test of whether an install worked as well as a visual confirmation that something is already installed when no install was required.</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-comment"># Run this directly from the web with:</span>
<span class="hljs-comment"># curl -sSL https://gitlab.com/guided-explorations/aws/aws-cloudshell-configs/-/raw/main/add-terraform.sh -o $HOME/add-terraform.sh; chmod +x $HOME/add-terraform.sh; bash $HOME/add-terraform.sh</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"AWS Cloudshell Configuration"</span>

<span class="hljs-keyword">if</span> [[ -z $(<span class="hljs-built_in">command</span> -v terraform) ]]; <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"adding terraform"</span>
  sudo yum install -y yum-utils 
  sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
  sudo yum -y install terraform
<span class="hljs-keyword">fi</span> 
<span class="hljs-built_in">echo</span> <span class="hljs-string">"TERRAFORM VERSION: <span class="hljs-subst">$(terraform --version)</span>"</span>
</code></pre>
<h2 id="heading-managing-terraform-templates-and-state">Managing Terraform Templates and State</h2>
<p>One of the main things I will be using AWS CloudShell for is running the <a target="_blank" href="https://github.com/aws-ia/terraform-aws-eks-blueprints">Terraform templates of EKS Blueprints</a>. These are likely to have more than 1GB of downloading dependencies, so operating in the $HOME directory of AWS CloudShell causes an out of disk space error. However, operating anywhere else causes the directory to be tossed as well as introduces some challenges with constantly use sudo to be able to change the disk.</p>
<p>The work around is to create a root directory and permission it for the cloud shell user and then user the <code>-state</code> switch of terraform to store the state in $HOME where it will persist for up to 120 days.</p>
<p>The below code repository includes <code>prep-for-terraform.sh</code> that will prepare the setup for you and shows commented terraform commands to go with a default setup.</p>
<h2 id="heading-code-for-this-article"><strong>Code For This Article</strong></h2>
<p>Here is the open source code: <a target="_blank" href="https://gitlab.com/guided-explorations/aws/aws-cloudshell-configs">AWS CloudShell Run From Web Configuration Scripts</a></p>
<h4 id="heading-mission-impossible-code-series-inclusion">Mission Impossible Code Series Inclusion</h4>
<ul>
<li><p>The solution is concise.</p>
</li>
<li><p>The solution is idempotent (only takes steps necessary when things are missing).</p>
</li>
<li><p>The solution runs directly from the web.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[DevOps Checklists: A Multiplatform Toolset for Markdown Checklists (Part 3)]]></title><description><![CDATA[This article is the third and final of a series. Part 1 justified that human-performed DevOps checklists are essentially source code, and according to GitOps principles, belong in Git just like any other code required for successfully managing a soft...]]></description><link>https://missionimpossiblecode.io/devops-checklists-a-multiplatform-toolset-for-markdown-checklists-part-3</link><guid isPermaLink="true">https://missionimpossiblecode.io/devops-checklists-a-multiplatform-toolset-for-markdown-checklists-part-3</guid><category><![CDATA[Devops]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><category><![CDATA[GitLab]]></category><category><![CDATA[deployment]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Wed, 03 Aug 2022 11:05:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/n8Qb1ZAkK88/upload/v1659956022976/zPorW8THx.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This article is the third and final of a series. <a target="_blank" href="https://missionimpossiblecode.io/devops-checklists-why-human-processed-source-code-belongs-in-git-part-1">Part 1</a> justified that human-performed DevOps checklists are essentially source code, and according to GitOps principles, belong in Git just like any other code required for successfully managing a software stack. <a target="_blank" href="https://missionimpossiblecode.io/devops-checklists-how-checklists-on-github-and-gitlab-can-help-team-collaboration-part-2">Part 2</a> dug into the rich but often overlooked, support for interactive checklists on GitHub and GitLab. In this article, we dive into the why and how of using rich desktop editing tools for checklist creation and completion.</p>
<h3 id="heading-checklist-authoring-navigation-and-completion-without-dual-pain-markdown-editors">Checklist Authoring, Navigation and Completion without Dual-Pain Markdown Editors</h3>
<p>“Dual-pain” is not misspelled here — while developers may consider a live rendering window an upgrade to coding in raw markdown, it is very cumbersome for those who do not have a developer role, but need to author Template Checklists or complete Tracking Checklists.</p>
<p>The interactivity of checklists on GitHub and GitLab should generally be used for Tracking Checklists as they facilitate collaboration through immediate, shared updates and do not require Git commits or merges during real-time operational events.</p>
<p>There may be times when visual, desktop-based utilities offer advantages over this model, such as:</p>
<ul>
<li>For easy authoring of checklists.</li>
<li>For navigation of complex checklists In an outline mode while completing them.</li>
<li>For completing visual markdown structures that are not interactive on GitHub and GitLab (such as fill-in fields or tables).</li>
<li>For implementation of more elaborate “completed marking” (for example, adding date-time stamps and/or colors).</li>
</ul>
<h3 id="heading-typora-copyq">Typora + CopyQ</h3>
<p>Combining Typora and CopyQ expands checklist execution and authoring options. CopyQ allows the insertion of standardized markdown snippets using hotkeys and Typora can directly receive markdown and immediately renders it. This allows adding the following additional completion marking capabilities:</p>
<ul>
<li>Apply Markdown formatting completion mark (such as bold).</li>
<li>Insert the current date and time as part of “Completion marking.”</li>
<li>Prompt for information and include it as part of the insertion text. In the provided CopyQ code there are examples of prompting for both Template Checklist authoring and Tracking Checklist execution.</li>
<li>Apply HTML or CSS formatting — for instance, colored text background. HTML and CSS formatting render nicely in Typora, but not in GitLab and GitHub.</li>
</ul>
<h2 id="heading-cumbersome-markdown-editing-eliminated"><strong>Cumbersome Markdown Editing Eliminated</strong></h2>
<p>In the past I have had my favorite code editor (VS Code) loaded up with many markdown extensions — each one completed different parts of the puzzle and did it in different ways. Sometimes they conflicted or even prevented editing the markdown by no longer recognizing specific keystrokes. I have removed those plugins and instead run a plugin that simply opens a file type in my operating system’s default application for markdown files and configure Typora to handle markdown extensions in the operating system.</p>
<h3 id="heading-recording-information-in-checklists-and-cognitive-loading">Recording Information in Checklists and Cognitive Loading</h3>
<p>The elimination of cognitive load during a stressful activity, such as managing IT changes, is also a huge benefit. No matter how sharp you are, humans have limits as to how many things they can juggle in any situation — but the sheer risk of production changes saps even some of that capability away because we are being extra vigilant to ensure everything is done just as planned. Under these conditions, getting some markdown code wrong or having to keep recalling the need to properly format can be very distracting.</p>
<p>A primary way around this dilemma is to prepare a copy of a template checklist in advance of the change event to the degree that it is possible. Filling in versions, titles, change dates can usually be done a few days ahead of time. Typora can be used for this by simply copying the body portion of an Issue or Merge/Pull Request in, and when done, pasting it over the body content when done editing.</p>
<p>If your checklist execution procedures require the insertion of screen captures, log data or inserting names and dates and versions — then editing the markdown will be required and this is where Typora will shine by making it nearly as easy as using Google Docs or your favorite word processor. Using Typora for execution does not allow easy real-time collaboration — so there is a potential trade-off if the checklist is used collaboratively.</p>
<h3 id="heading-multiplatform-developer-workstations-for-devops">Multiplatform Developer Workstations for DevOps</h3>
<p>Across my professional and personal projects, I use all of Windows 10, Mac OS (Catalina) and LinuxMint for editing code and markdown. I use Visual Studio Code on all of them as my primary text editor. These operating systems are also common across DevOps teams — many times the same team will have at least two of these.</p>
<p>Typora and CopyQ both run on all three OSes as well and provide a consistent user experience on all platforms.</p>
<h3 id="heading-cost-of-this-solution">Cost of This Solution</h3>
<p>Paid for checklist SaaS solutions can represent a lot of adoption friction for team tooling — not just because of cost, but due to:</p>
<ul>
<li>Establishing new system users in a third-party system.</li>
<li>Having important audit records in the care of yet another third-party.</li>
<li>Can not integrate directly with the built-in checklist interactivity of GitLab and GitHub.</li>
</ul>
<p>Typora is $15 lifetime for 3 devices and CopyQ are free and there are no other elements to the solution that carries a cost. The package manager for your OS may have the last free version of Typora (0.11.18) available - but I have to say that at $15 Typora pays for itself in many times over. For instance, without Typora I don't bother doing markdown tables at all and with it, they are a breeze.</p>
<h3 id="heading-copyq-for-standard-markdown-checklist-snippets">CopyQ for Standard Markdown Checklist Snippets</h3>
<p>If you have team members or teams who do not like Typora, CopyQ can provide value by itself in that a standardized set of Markdown insertions can be defined and shared via a CopyQ configuration text file in Git. This can allow the establishment of some conventions for authoring and executing checks regardless of the editor in use.</p>
<h3 id="heading-per-step-time-date-stamps-on-done-marking-required">Per-Step Time-Date Stamps on “Done Marking” Required</h3>
<p>Time-date stamps on “done marking” may be desirable or required by your checklist execution methodology. Some checklists may be multiday or long-running — even if completed at one time. Time-date stamps add value by creating a historical time record of completion time and elapsed time — which allows for predicting ops events duration and event correlation if there are problems with a change.</p>
<p>As mentioned earlier, GitLab’s markdown interactivity does provide these stamps in the discussion log — however, if you ever had to audit a checkbox for completion or timing — you must review the entire log for all toggles of the checkbox in question to get the information. In addition, if you have checkboxes that have the same text repeated, it will be difficult to sort out which ones are being manipulated in the audit log.</p>
<h3 id="heading-when-to-use-typora-over-other-editors-or-web-editors">When to Use Typora Over Other Editors or Web Editors</h3>
<ul>
<li>For authoring checklists — even when they are stored as issue or merge/pull request templates.</li>
<li>When advanced formatting is desired or required — you will be more willing to author helpful constructs such as tables and inserting graphics. Inserting graphics using Typora is especially compatible with git by allowing you to seamlessly copy the inserted graphic to the same directory as the markdown where you are inserting it.</li>
<li>For completing checklists that have a lot of data recording or where more sophisticated done marking is required. This inhibits real-time Collaboration on the document because you have to go into edit mode in the issue or merge/pull request.</li>
</ul>
<h3 id="heading-working-example-of-what-is-possible-with-typora-and-copyq-with-code">Working Example of What Is Possible with Typora and CopyQ (with Code)</h3>
<p>The premade stamps provided here all have CSS colors — but also use standard markdown bold so that they stand out reasonably well with or without CSS rendering.</p>
<p><img src="https://cdn.thenewstack.io/media/2020/03/32c04d3a-gitlab1-580x1024.png" alt /></p>
<h3 id="heading-setting-up-typora-andor-copyq">Setting Up Typora and/or CopyQ</h3>
<p>The following instructions have been tested on Windows 10, OSX and Linux (LinuxMint 19.x).</p>
<p>Installation on Windows (Chocolatey)</p>
<p><img src="https://cdn.thenewstack.io/media/2020/03/a7fd80ff-gitlab2.png" alt /></p>
<p>Installation on Mac (Brew)</p>
<p><img src="https://cdn.thenewstack.io/media/2020/03/1f9e45fd-gitlab3.png" alt /></p>
<h2 id="heading-installation-on-linux-ubuntu">Installation on Linux (Ubuntu)</h2>
<p>Typora is only <a target="_blank" href="https://support.typora.io/Typora-on-Linux/">tested on Ubuntu</a>.</p>
<p>Here are the Linux installation instructions:  http://support.typora.io/Typora-on-Linux/</p>
<p>CopyQ in public package repos is <a target="_blank" href="https://copyq.readthedocs.io/en/latest/installation.html">published as “CopyQ”</a>: https://copyq.readthedocs.io/en/latest/installation.html</p>
<h2 id="heading-configure-typora">Configure Typora</h2>
<p>These settings are optional, but helpful for the concept of Markdown Checklists. Access these settings from File =&gt; Preference:</p>
<p>**Images Insert** =&gt; “Copy image to current folder (./)”<br />**Save &amp; Recover** =&gt; “Auto Save”<br />**Privacy** =&gt; “Send Anonymous Usage Info”<br />Configure CopyQ with TODO, DONE, SKIPPED and RECORD HERE Stamps</p>
<p>The following snippets give you a minimum to start with and also serve as examples to customize for your own work. Using the technique of distributing your own command <em>ini</em> allows you to standardize across your team.</p>
<p>While we are pairing CopyQ with Typora, it works fine with any Markdown editor.</p>
<p>Import this CopyQ settings import file: <a target="_blank" href="https://gitlab.com/darwinjs-ideas/GitOpsforHumanProcessedChecklists/-/blob/master/copyq-command-import.ini">https://gitlab.com/darwinjs-ideas/GitOpsforHumanProcessedChecklists/-/blob/master/copyq-command-import.ini</a></p>
<ol>
<li>Start CopyQ from the menu and it becomes memory resident.</li>
<li>If the UI does not show, open CopyQ from your “tray” area by clicking the icon and selecting “Show/Hide.”</li>
<li>In the CopyQ UI, Open “File =&gt; Commands / Global Shortcuts.”</li>
<li>Use the “Load Commands” button to load the INI.</li>
</ol>
<p>Here are the hotkeys. They render with background colors in Typora (renders CSS) and without the background color on websites like Github and Gitlab. The bold and square brackets make the stamps recognizable when colors are not rendered.</p>
<ul>
<li>ALT-SHIFT-S: **[TODO]**</li>
<li>ALT-SHIFT-D: **[DONE 07/28 @ 10:42]**</li>
<li>ALT-SHIFT-Z: **[SKIPPED 07/28 @ 10:42 ]**</li>
<li>ALT-SHIFT-X: **[RECORD RESULT HERE]**</li>
<li>ALT-SHIFT-R: <code>_REPLACE_WITH_ &lt;whatever was typed in response to the prompt&gt;</code></li>
</ul>
<h2 id="heading-tested-on">Tested On</h2>
<ul>
<li>Windows 10</li>
<li>MacOS</li>
<li>LinuxMint 19.1</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article series has set out to communicate some foundational ideas about managing DevOps procedures in a GitOps world.</p>
<p>First, we established that the “human code” we find in our DevOps solutions is just as critical as computer code and therefore belongs in git.</p>
<p>We went on to claim that “human code” can be recognized because it takes the form of checklists, which reassemble computer code in that they are exacting, ordered and sometimes conditional instructions for accomplishing the desired outcome.</p>
<p>We touched on the evidence-backed claims in the highly recommended book  <a target="_blank" href="http://atulgawande.com/book/the-checklist-manifesto/">“The Checklist Manifesto”</a> that the helpfulness and necessity of checklists positively affect all individuals and teams no matter how smart they might be.</p>
<p>The templating and interactive checkbox features of GitHub and GitLab were highlighted as critical enablers for integrating of checklists into GitOps software change-management workflows and serve as working example repositories on both were also provided.</p>
<p>Finally, we discussed some rich multiplatform desktop tools to help with ease of editing and standardization of Template Checklist creation and Tracking Checklist completion. Working code examples for configuring CopyQ were also provided.</p>
<p>It is my hope that this three-part deep dive will help you, your team and your organization to experience the massive quality improvements that the simple concept of collaborative checklists has to offer.</p>
<h2 id="heading-code-examples-and-architecture-for-this-article-series">Code, Examples and Architecture for This Article Series</h2>
<p><a target="_blank" href="https://gitlab.com/darwinjs-ideas/GitOpsforHumanProcessedChecklists">“GitOps for Human Processed Checklists (Part 1)”</a></p>
<p><a target="_blank" href="https://thenewstack.io/part-2-how-checklists-on-github-and-gitlab-can-help-team-collaboration/">“GitOps for Human-Processed Code (Part 2): Interactive Markdown Checklists on GitHub and GitLab”</a></p>
<p><a target="_blank" href="https://gitlab.com/darwinjs-ideas/GitOpsforHumanProcessedChecklists/-/blob/master/copyq-command-import.ini">CopyQ command import file for stamps discussed in this article.</a></p>
<p><a target="_blank" href="https://gitlab.com/darwinjs-ideas/GitOpsforHumanProcessedChecklists/-/blob/master/DesktopTools.md">Product Selection Architecture Map For This Solution</a></p>
<p><a target="_blank" href="https://gitlab.com/darwinjs-ideas/GitOpsforHumanProcessedChecklists/-/blob/master/DesktopTools.md#appendix-architecture-heuristics-requirements-constraints-desirements-serendipities-applicability-limitations-and-alternatives">“Architecture Heuristics for This Solution: Requirements, Constraints, Desirements, Serendipities, Applicability, Limitations and Alternatives”</a></p>
<p><a target="_blank" href="https://gitlab.com/darwinjs-ideas/GitOpsforHumanProcessedChecklists">GitLab Sample Repository</a></p>
<p><a target="_blank" href="https://github.com/darwinjs/GitOpsforHumanProcessedChecklists">GitHub Sample Repository</a></p>
<p><a target="_blank" href="https://www.amazon.com/dp/B0030V0PEW/">“The Checklist Manifesto”</a></p>
]]></content:encoded></item><item><title><![CDATA[DevOps Checklists: How Checklists on GitHub and GitLab Can Help Team Collaboration (Part 2)]]></title><description><![CDATA[There are always those who feel checklists are an unnecessary waste of time because they think they can always remember the basics of the steps involved to complete a task. Many are also not aware of the huge, cross-discipline benefits that can come ...]]></description><link>https://missionimpossiblecode.io/devops-checklists-how-checklists-on-github-and-gitlab-can-help-team-collaboration-part-2</link><guid isPermaLink="true">https://missionimpossiblecode.io/devops-checklists-how-checklists-on-github-and-gitlab-can-help-team-collaboration-part-2</guid><category><![CDATA[Devops]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><category><![CDATA[GitLab]]></category><category><![CDATA[deployment]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Tue, 26 Jul 2022 10:07:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658397142032/rJoQ7nARz.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are always those who feel checklists are an unnecessary waste of time because they think they can always remember the basics of the steps involved to complete a task. Many are also not aware of the huge, cross-discipline benefits that can come from their use. If you have not studied checklists in their own right, you may think they are only for exceedingly complex scenarios that simple ones do not benefit. If anyone you know feels this way, I would encourage them to read Atul Gawande’s <a target="_blank" href="https://www.barnesandnoble.com/w/checklist-manifesto-atul-gawande/1100358644#/">“The Checklist Manifesto.”</a> The key takeaways include:</p>
<ul>
<li>The checklists in question are for highly routinized procedures that everyone should implicitly know and execute automatically.</li>
<li>The frequency of measured mistake avoidance by using checklists for these standardized procedures is very significant.</li>
<li>The very professionals who are considered the sharpest in the room (lead surgeons, airplane pilot captains, etc.) were not immune to needing checklists as many had assumed they might be.</li>
<li>Most complex activities are tackled by a team — many breakdowns in proper outcomes come from assumptions about what others have done or who has the right to point out problems during a procedure. Checklists resolve this because they ensure communication and give anyone the right to point out missed items.</li>
</ul>
<p>The “Checklist Manifesto” demonstrates that checklists don’t just prevent faults due to known issues — they also shift the human relational and communication dynamics of the team so that the can consistently produce better outcomes. So, if anyone ever tells you they don’t need a checklist because what they are doing “ain’t brain surgery,” you can let them know real brain surgeons actually use checklists for the easy stuff that also has life-threatening consequences if mistakes are made.</p>
<h2 id="heading-full-checklist-lifecycle">Full Checklist Lifecycle</h2>
<p>There is not a standard set of terminology for the checklist lifecycle, so we’ll apply a little of our own and tack it toward DevOps checklists with the terms “Template Checklist” and “Tracking Checklist.” As you’ll see, the important differences between these checklist types has implications for what tooling is used to create and update template checklists versus tooling for processing tracking checklists.</p>
<p>What we will call a “Template Checklist” is where we express the following:</p>
<ul>
<li>What needs to be done.</li>
<li>Required order of step completion and/or parallel step execution allowances.</li>
<li>How to complete procedures (or pointers to procedures if too long to be contained inline).</li>
<li>Why a specific approach or order is important (to facilitate procedure adjustments if problems with the standard procedure are experienced).</li>
<li>Fill-in fields that may need to be tailored before the checklist starts or field engineering tracking checklist completion.</li>
<li>Optional and context-specific procedure blocks that may only need to be done in certain cases or done differently depending on something like the target environment.</li>
</ul>
<p>What we will call a “Tracking Checklist” is used to perform the checklist without losing your place.  It is where we track:</p>
<ul>
<li>The current real-time completion status of checklist items as the checklist is being processed.</li>
<li>Who completed the checklist.</li>
<li>When they completed it.</li>
<li>What target they completed the checklist against (if there are multiple possibilities).</li>
<li>Recording of variances compared to expected outputs or results.</li>
<li>Whether variances were experienced that imply or directly indicate a template checklist update should be considered.</li>
</ul>
<p>Template Checklists frequently need preparatory customization to be used as a Tracking Checklist. For example, the same template might be used to push changes to one of five environments because the process is identical except for a few bits of variable data, such as where the environment is located and the configuration details of that environment. These may be as simple as completing a table of details at the top, or may require more elaborate edits throughout. The preparation stage can also be important to the approval process — anyone whose pre-approval is required will want to see the specific details in a Prepared Tracking Checklist before giving the okay. Finally, Completed Tracking Checklists are an official record of the change event and generally need to be retained for audits and process-improvement studies.</p>
<p>The entire checklist lifecycle allows for continuous collaborative feedback against the Template Checklist. The value of a checklist is greatly enhanced as it aggregates more and more knowledge of what to do under various circumstances. If exception processes or infrequently executed sections get large, it may be necessary to break them out into their own sub-checklist template that it only invoked when needed.</p>
<p><img src="https://cdn.thenewstack.io/media/2019/12/a26eb33e-screen-shot-2019-12-19-at-16.57.02.png" alt /></p>
<h2 id="heading-plain-markdown">Plain Markdown</h2>
<p>If you are doing documentation of any type that is destined for Git, Markdown is, of course, the format of choice. The solution presented here focuses on flavorless, standard Markdown so as to be the most applicable to the most rendering environments as possible. The one small departure is the use of the SPAN or MARK html tag to add a color background to stamps when the rendering engine supports CSS. However, not only is this simply ignored in environments that don’t render CSS, the markings that use it also have standard markdown formatting to make them stand out sufficiently.</p>
<h2 id="heading-tracking-checklist-tools-and-storage">Tracking Checklist Tools and Storage</h2>
<p>In Part 1 of this series, the concept of a “Master” checklist was discussed in passing. Due to the features of GitLab and GitHub, you may elect to treat storage of Template Checklists differently from Tracking Checklists.</p>
<h2 id="heading-checklist-interactivity-on-gitlab-and-github">Checklist Interactivity on GitLab and GitHub</h2>
<p>The two major source-control systems support checkbox interactivity in specific item types of the system. These item types consist of Issues, Comments and Pull/Merge requests.  Interactivity means you can directly click to check and uncheck checkboxes without having to first put the item in full “edit” mode. This makes checklist execution via the web site seem much more natural.</p>
<p>Since code should only be changed by a code review process, neither GitLab nor GitHub support checkbox interactivity in source code files.</p>
<p>This interactivity has a positive implication for real-time collaboration on checklist execution in that status updates to the checklist are realtime and do not require a commit-push cycle to update status. There is also no risk of merge conflicts if multiple individuals are updating the same checklist. Issues also have the advantage of tracking overall work visibility and completion and are regularly used for auditing completed work.</p>
<p>The interactivity allows non-coders to be a part of collaborative completion of checklists formatted in “markdown” — which is a standard markup language. This means they do not have to replicate repositories, configure editors, learn markdown syntax, learn how to commit and push code, etc.  Even with the available online editors, users have to learn markdown and have to close out individual checkbox edits quickly if more than one user is processing or viewing a checklist at once.</p>
<p>When it comes to auditability of checkboxes in Issues, GitLab has a distinguishing feature in that it adds a comment to the issue discussion log that tracks what actions are taken on checkboxes (check or uncheck), who took the action and at what time the action occurred. This can be a massive help for general auditability. If checkboxes are carefully checked exactly when actions or wait states are completed, it can also help provide metrics for how long specific parts of the process take. Elapsed time information can help when attempting to optimize slow automation processes and/or slow human-performed steps. It is a further help on checklists that are executed by more than one person at a time as you can know who completed which steps.</p>
<h2 id="heading-template-checklist-tools-and-storage">Template Checklist Tools and Storage</h2>
<p>Since changes to template checklists should most likely be reviewed by more than the individual making the changes, they should be stored directly in git so that changes can be subject to collaboration and review requirements the same way machine code is managed.</p>
<p>How you intend to keep completed checklist records may affect where you store the masters. If completed checklists are considered permanent records by remaining in completed issues and pull/merge requests, you should keep in mind they are not part of the git records of the project and won’t transfer if you move the git file system. If “historic records” are part of your GitOps discipline, then you may want at least a copy of the completed checklists in a source code folder. However, if the working checklists are stored in Git, they are not interactive on the Web, which makes them less functional — especially for real-time collaboration.</p>
<p>Here are some storage options to consider for completed tracking checklists (template checklists will always be somewhere in source):</p>
<ol>
<li>Only in source code.</li>
<li>Only in completed Issues and pull/merge requests.</li>
<li>Both as an Issue or pull/merge request with a copy into git for record-keeping sake.</li>
</ol>
<p>The third option of storing the checklist in both places allows easy discoverability and real-time collaboration on execution. It also allows you to be selective about which checklists become a permanent record — perhaps only “Promote to Production Checklist” needs to be kept rather than all the pre-prod environment checklists as well?</p>
<h2 id="heading-template-storage-in-github-and-gitlab">Template Storage in Github and Gitlab</h2>
<p>The reason that completed checklist storage matters when considering storage of template checklists is that both GitLab and Github support placing markdown files in special locations in the git file system that make them into templates. This allows them to both be under collaborative source control, but also function as easy to discover templates for Issues and pull/merge requests in the web UI of each system. These are also the same subsystems that support interactive checklist usage. So you can imagine having template checklist templates like “Release Preparation Checklist”, “Promote to Staging Checklist” that are easily selectable when creating Issues or pull/merge requests and that require collaborative review in order to update.</p>
<p>Here are the special subfolder locations that enable markdown documents to be template — although some alternate locations are also possible. These specific locations in each product allow for multiple templates for each supported type and are also hidden when making local repository clones.</p>
<p>GitLab:</p>
<ul>
<li>.gitlab/issue_templates/</li>
<li>.gitlab/merge_request_templates/</li>
</ul>
<p>Github:</p>
<ul>
<li>.github/ISSUE_TEMPLATE/</li>
<li>.github/PULL_REQUEST_TEMPLATE/</li>
</ul>
<h2 id="heading-pullmerge-requests-for-preparing-checklists">Pull/Merge Requests for Preparing Checklists</h2>
<p>While GitLab and GitHub support checklist processing in both issues and pull/merge requests, both systems only support approvals on merger/pull requests. You may want to consider the merging of something benign, such as a token text file update, as a method of providing approvals for the checklist preparation. The resulting prepared checklist could then be inserted into an issue or a real merge request.</p>
<h2 id="heading-pullmerge-requests-approvals-for-official-gating-of-checklist-completion">Pull/Merge Requests Approvals for Official Gating of Checklist Completion</h2>
<p>GitHub and GitLab both support the concept of creating approval workflows for pull/merge requests, which enable checklist completion to be a much stronger control when the pull/merge request consists of a checklist that must be completed. Due to its embrace of GitOps, GitLab is notably more feature-rich in this area. Since GitLab also manages environment deployments, merge requests play a key role, not only in code review and signoff but also in deployment review and signoff. Multiple merge approval rules, electronic signatures, builds for merge commits and over ten other controls are available in various editions of GitLab.</p>
<h2 id="heading-github-nuances">GitHub Nuances</h2>
<p>GitHub templates must have YAML front matter added to the beginning of the document in order to appear as a selectable template when creating issues. The following repositories contain examples of merge requests and issue templates in the above locations as well as in-progress checklists as an issue and merge request.</p>
<p>GitHub currently does not have a selector for multiple pull request templates, you must <a target="_blank" href="https://help.github.com/en/github/managing-your-work-on-github/about-automation-for-issues-and-pull-requests-with-query-parameters">use http query parameters to select one even in the GUI</a>.</p>
<p>GitLab only requires that the files be placed in the appropriate folder and then they will be available in the template selector in the web UI. This also means the user only has the title by which to initially judge the suitability of a template — file naming that clearly expresses and distinguishes the purpose of each template is necessary.</p>
<h2 id="heading-creating-placeholder-tokens-for-information-insertion">Creating Placeholder Tokens for Information Insertion</h2>
<p>A great way to indicate the need to insert specific data in a checklist is to use a code fence with  a unique, standardized token like this: <code>_REPLACE_WITH_ (some text)</code> This gives multiple advantages over your own custom solution to indicating where to record result information:</p>
<ul>
<li>Code fences are already supported with unique highlighting (usually inverse) in most markdown editors and rendering engines.</li>
<li>They can be placed within the context of almost any other markdown (tables, bullets, blockquotes, etc).</li>
<li>The use of a replacement token can be directly used by humans or an editor’s search and replace functionality or used by automation (like CopyQ).</li>
<li>The replacement token method can be used for rapid checklist preparation of information that repeats throughout the checklist, such as searching and replacing all occurrences of ‘_REPLACE_WITH_ X.Y’ to the actual changeset version number in one or more checklists in one operation.</li>
<li>In addition to checklist preparation, the replacement token method works equally well for values that will be completed during execution of the checklist, such as <code>_REPLACE_WITH_ screenshot-of-messages-log</code>.</li>
</ul>
<h2 id="heading-sample-repositories">Sample Repositories</h2>
<p>These sample repositories include templates as well as in-progress issues and merge requests containing partially complete checklists to help you see how issue and pull/merge request templates work and how interactive checkboxes work:</p>
<p><a target="_blank" href="https://gitlab.com/missionimpossiblecode/gitops-for-human-processed-checklists">https://gitlab.com/missionimpossiblecode/gitops-for-human-processed-checklists</a></p>
<p><a target="_blank" href="https://github.com/darwinjs/GitOpsforHumanProcessedChecklists">https://github.com/darwinjs/GitOpsforHumanProcessedChecklists</a></p>
<h2 id="heading-sometimes-you-need-even-more">Sometimes You Need Even More</h2>
<p>This article and the sample repositories give a fairly comprehensive view of what is possible with the templates and interactive checklist support built into the most common git collaboration systems. Our final article in this series will look at multiplatform desktop utilities for a much richer markdown editing experience and the ability to insert standardized markdown formatted snippets during Template Checklist creation or Tracking Checklist completion.</p>
<p>Photo by Darlene Alderson: https://www.pexels.com/photo/person-sitting-on-sofa-using-laptop-7971328/</p>
]]></content:encoded></item><item><title><![CDATA[DevOps CheckLists: Why Human-Processed Source Code Belongs in Git (Part 1)]]></title><description><![CDATA[It is easy to think of code as only a computer thing, but in truth, humans have been codifying all kinds of knowledge for centuries. There is one type of human knowledge that is more like computer code than anything else: and that is process executio...]]></description><link>https://missionimpossiblecode.io/devops-checklists-why-human-processed-source-code-belongs-in-git-part-1</link><guid isPermaLink="true">https://missionimpossiblecode.io/devops-checklists-why-human-processed-source-code-belongs-in-git-part-1</guid><category><![CDATA[Devops]]></category><category><![CDATA[#codenewbies]]></category><category><![CDATA[automation]]></category><category><![CDATA[Git]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Tue, 19 Jul 2022 13:13:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/RLw-UC03Gwc/upload/v1658236457263/pgzg4iDOq.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It is easy to think of code as only a computer thing, but in truth, humans have been codifying all kinds of knowledge for centuries. There is one type of human knowledge that is more like computer code than anything else: and that is process execution knowledge.</p>
<p>We are all familiar with the concept of a computer executing code — it takes a structured instructions and executes them in order to produce a desired end result. Computers are very good at this meticulous duty and perform instructions flawlessly many times over.</p>
<p>So what would the equivalent be in the human realm? How about a detailed set of ordered steps that must be completed to achieve a desired result? That seems like an apt description of procedural checklists.</p>
<p>The book <a target="_blank" href="https://en.wikipedia.org/wiki/The_Checklist_Manifesto">“The Checklist Manifesto”</a> paints a picture of how checklist usage creates a dramatic impact on quality outcomes by achieving consistency in meticulous and repetitive human-performed tasks. Checklists literally save lives in many high stakes circumstances like flying airplanes and performing surgery.</p>
<p>GitOps is a commitment to ensure everything needed for success in building and operating software is stored in Git. In fact, Git ultimately serves as the single source of truth. However, most of the time we secretly limit that “everything” to “everything the computer performs” and leave out the things that humans do to ensure the integrity of the software.</p>
<p>While a true commitment to the “everything in Git” principle would encourage the storage of checklists that humans perform in Git, there are some much deeper connections between the human code and the computer code that eventually automates these procedures.</p>
<p>The real focus here is operational checklists for the success of software (although we’ll discuss a lot about checklists in general). Think of them as build, deployment or testing steps.</p>
<h3 id="heading-the-genesis-of-all-process-oriented-work">The Genesis of All Process Oriented Work</h3>
<p>Process-oriented computer work has its roots in work done by humans. We want to be relieved of the tedium of precise, ensured execution of extremely detailed, order-sensitive steps. If that work is done in a repeating cycle, we have all the more motivation to be rid of and to quality ensure it by giving the work to machines that don’t experience mental drift due to tedium.</p>
<h3 id="heading-the-best-process-discovery-mechanism">The Best Process Discovery Mechanism</h3>
<p>I have had periods in my career where overnight air travel was frequent. Since I have a busy mind, the possibility of forgetting to bring something was very real and caused a lot of stress. Overnight air travel involves several “point of no return” milestones because at a certain point you no longer have available time to double back to your home to grab an item and later, you can no longer double back to your vehicle to grab something either.</p>
<p>The power of a master checklist to solve this has been amazing. Over the years of keeping this checklist, I’ve noticed something else: that the process of creating and executing the checklist causes things to come to mind that need to be in it (or removed from it).</p>
<p>Sometimes these are completely new types of things that I hadn’t previously thought of putting on the checklist. This checklist is now ordered by T-minus transitions — the same way spacecraft launch checklists are tracked. Things like “Before Leaving the House” and “Last Minute Grabs From the Car.” It also has conditionals like If International Travel, If Empty House.</p>
<h3 id="heading-brain-dumping-and-mental-associations">Brain Dumping and Mental Associations</h3>
<p>It seems when we create a concrete checklist from our tacit knowledge of a process, it activates our minds to find related items. This is a natural part of human cognition, but is especially helpful with regard to discovering edge cases and conditional steps. The long term maintenance of a master checklist also continues to add value in this area as there is someplace to put new ideas about what ought to be done to make a checklist more effective at assuring its proper execution.</p>
<h3 id="heading-tacit-process-knowledge-made-explicit">Tacit Process Knowledge Made Explicit</h3>
<p>When we build software to take the place of human process-driven work, the human checklist is an invaluable source of information. Even if the checklist has to be made specifically for a software build, it provides a full extraction of the tacit knowledge of those performing the processes because it can be validated by someone with no tacit knowledge — which is exactly what computer code has to do because it does not have tacit knowledge either. Tacit process knowledge is the knowhow that experts have a hard time explaining to you because it has become so reflexive to them they do not notice that it is an important part of process execution for those who are unfamiliar.</p>
<h3 id="heading-development-backlog-refinement">Development Backlog Refinement</h3>
<p>The building and refining of checklists really represents a somewhat automatic refinement of process automation requirements. Rather than trying to empirically discover <em>what</em> should be done while coding <em>how</em> to do it — discovery and coding can be separated. This helps when there are different individuals doing each activity, but it also helps when the same individual does the activity because separating the flow states of “coding” and of “focused checklist execution” yields the top-notch awareness that improves the quality of outcomes for both activities.</p>
<h3 id="heading-discerning-low-and-negative-automation-roi">Discerning Low and Negative Automation ROI</h3>
<p>While the “Automate All The Things” meme is funny, it is also a commentary on a philosophical position taken too far. Automation is about efficiency, which in turn is about return on investment. Several brave writers and bloggers have published guidelines on situations where the ROI is not there for automation. These tend to be the areas where human judgment and/or strong variability between executions are an inherent characteristic of a process. Checklists help discern where automation ROI may be low because the attempt to create a checklist with a lot of judgment calls helps articulate whether the code required to make the same judgments is possible and cost-effective. Another indicator is that a checklist needs new custom steps for every execution. These characteristics indicate there would be more time spent maintaining the code of the automation than is involved in humans performing the tasks — perhaps by orders of magnitude. The process of attempting to automate can also weed out which sub-processes which naturally have a low automation ROI, while also automating anything that is a candidate for automation. Discerning low or negative ROI can be done during the overall automation discovery process — but the existence of low/negative ROI must be believed in and filtered for to avoid the “automate it no matter the ROI” trap.</p>
<h3 id="heading-human-code-update-review-and-adoption">Human Code Update, Review and Adoption</h3>
<p>Just as computer code benefits from collaborative-maintenance updates, so human code — in the form of master checklists — can also benefit from this collaborative approach — whether or not it can ever be boiled down to automation or not. Collaborative coding is a main feature of Git and gaining the benefit is as simple as ensuring checklists are stored in Git, in a text format so that Git can perform human-readable diffs on changes. Essentially this naturally leads choosing Markdown as the document format.</p>
<h3 id="heading-human-code-auditability">Human Code Auditability</h3>
<p>Since humans have a challenge performing meticulous, repetitive activities with high fidelity, we tend to want them to provide proof that they performed the actions as prescribed or describe what alternative action was taken. By storing completed checklists in Git, we can facilitate auditability.  As discussed later, we can determine what type of “DONE” marking is used in order to improve the auditability — such as whether to include a date time stamp in the DONE marking of each step.</p>
<h3 id="heading-automation-and-checklists-go-better-together">Automation and Checklists Go Better Together</h3>
<p>Some DevOps Engineers might experience frustration over spending effort on codifying checklists rather than directly creating computer code to do those activities. The above list shows very valid reasons why it makes sense to acknowledge and purposely incorporate checklists into the process of initial and ongoing maintenance of automation. The risk that something might not be automated because it is too well documented as a human procedure should not lead us to ignore the fact that non-codified human procedures are a large scale risk to the integrity of the full DevOps lifecycle of a software solution.</p>
<h3 id="heading-live-checklist-status-sharing">Live Checklist Status Sharing</h3>
<p>During a change event, it is very handy to understand where everything is in the process — especially if multiple checklists must be performed simultaneously. While some of the SaaS checklist solutions offer live sharing as a primary value, Git-stored lists can come close if the checklist being watched is frequently pushed to a git server and individuals viewing it refresh their view.  You could even set up a looping “git commit and push” script on the workstation executing the checklist.</p>
<h3 id="heading-code-is-code-is-code-and-should-be-stored-in-git">Code Is Code Is Code and Should Be Stored in Git</h3>
<p>It’s common to think of “code” as being computer code — but human code fits the model in the form of checklists, with very detailed, order-specific and interdependent execution instructions. When we record human code in a way that leads to effective execution — we naturally arrive at the concept of a checklist. As we’ve discussed human code has many similarities to computer code — including how it benefits from the visibility and collaborative capabilities Git offers.</p>
<p>When human code is critical to the development, build, testing and operation of a software stack — it fits the definitions of GitOps — the single source of truth for success in managing a software stack.</p>
<p>Well-written and -maintained human code is the best starting point for creating computer code.</p>
<p>In a follow-up post we will look at two free, open source and multiplatform tools that make the creation and execution of markdown-based checklists much easier. It will also include configuration code that gets you started with easy “done marking” to keep the stress of checklist execution as low as possible and consistent between team members.</p>
]]></content:encoded></item><item><title><![CDATA[Solution for Reverse Engineering Linux Config Deltas Via System-wide Diffing]]></title><description><![CDATA[For many years it was my main work to reverse engineer software installation and configuration for hyper-scaled deployment automation and for OS provisioning for Windows.
Early on it was evident that for OS and software provisioning it was extremely ...]]></description><link>https://missionimpossiblecode.io/solution-for-reverse-engineering-linux-config-deltas-via-system-wide-diffing</link><guid isPermaLink="true">https://missionimpossiblecode.io/solution-for-reverse-engineering-linux-config-deltas-via-system-wide-diffing</guid><category><![CDATA[Linux]]></category><category><![CDATA[Bash]]></category><category><![CDATA[automation]]></category><category><![CDATA[tools]]></category><category><![CDATA[Mission Impossible Code]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Mon, 29 Nov 2021 16:19:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657978146193/6TRxRrhue.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For many years it was my main work to reverse engineer software installation and configuration for hyper-scaled deployment automation and for OS provisioning for Windows.</p>
<p>Early on it was evident that for OS and software provisioning it was extremely important to be able to prove that you had documented the exact checklist of ordered steps to take a system from “pristine OS deployment” to “working configuration” as an input to solid automation code.</p>
<p>Over time, I developed proficiency in this type of reverse engineering, explored the hundreds of available free and commercial tools, blogged about it and eventually developed a business around multiple advanced training courses on reverse engineering Windows OS and software installation for the purposes of provisioning and deployment automation.</p>
<p>There is an entire eco system of commercial and free tools for system-wide diffing on Windows, so it was a bit shocking to recently rediscover that comprehensive system-wide configuration diffing tools for Linux are rare indeed.</p>
<p>Whether you are porting Windows reverse engineering skills to Linux, or are a Linux Engineer that has not experienced the productivity benefits of system-wide config diffing for reverse engineering - this post is for you ;)</p>
<h3 id="heading-reversing-a-working-raspian-configuration">Reversing a Working Raspian Configuration</h3>
<p>For a robotics control project, I was recently setting up Raspian (the Raspberry Pi distro) to enable Bluetooth audio access via a running service. Bluetooth audio on Raspian needed a lot of work just to be functional. The requirement to simultaneously using the GPIO serial port to control an ardruino based robot created some knock-on complexity in figuring things out. Linux Bluetooth audio is also very per-user oriented (e.g. uses a user based systemd service and config files) because Bluetooth audio devices are frequently personal to users. However, it needed to work from the context of a systemd service with a special user logon so that the Bluetooth speaker could appear to give the robot commands.</p>
<p>The end result worked great - so I began the work to build “from scratch” instructions so others could do the same. However, the path to getting it working was extremely unclear due to the many changes (including unrelated ones) made along the way.</p>
<p>The last few steps to get Bluetooth audio working under a service remained unclear no matter how many things I tried or compared or repeated.</p>
<h3 id="heading-devising-a-system-wide-linux-config-diffing-solution">Devising a System-Wide Linux Config Diffing Solution</h3>
<p>I mistakenly thought there would be a couple obvious solutions available to do a system-wide diff to quickly isolate the differences between the working and from scratch build.</p>
<p>I was unable to find any obvious options, but eventually found configsnap - created by the venerable Rackspace support team.</p>
<p>While it looked very promising, I hit additional snags - the help and documentatoin did not cover whether the concept of comparing two different machines was a valid use case and the repository docs did not show compare commands at all. Searching the Internet did not yield any how to documents or videos.</p>
<p>Whenever there is a gap this large (lack of linux system-wide diffing solutions in general and lack of how to information for the one I found) I can’t help but create a bit of Mission Impossible Code to bridge the gap.</p>
<h3 id="heading-the-mission-objectives-and-parameters">The Mission Objectives and Parameters</h3>
<blockquote>
<p>![](https://missionimpossiblecode.io/img/darwinterberg30tip.png#floatleft "Tip of the Iceberg - Concise Summary Discussion") <strong>Mission Objectives and Parameters</strong> articulate the final objectives that emerged from both the <strong>preplanning</strong> and <strong>build process</strong>. <strong>Code Summary</strong> gives an out line of the code fragments. <strong>Code Call Outs</strong> highlights significant constraints, innovations and possible alternatives in the code.</p>
</blockquote>
<ol>
<li><strong>Objective:</strong> Use tooling to find system-wide configuration differences between two seperate linux installations to quickly isolate the differences between a known good and non-working system.</li>
<li><strong>Desirable Constraints In Meeting Objective:</strong><ol>
<li>Origination Priorities:<ol>
<li><strong>Source</strong> a ready-made solution, but if that fails…</li>
<li><strong>Assemble</strong> a solution from existing bits and pieces, but I don’t have the time to</li>
<li><strong>Build</strong> a solution from scratch.</li>
</ol>
</li>
<li>Leverage a “Zero Footprint” methodology where the use of the diffing tool does not create substantial configuration changes.</li>
<li>Work on as many distros as possible (Configsnap is packaged for only some distros)</li>
</ol>
</li>
</ol>
<h3 id="heading-code-summary">Code Summary</h3>
<ol>
<li><p>Using a Zero Footprint approach, use minimal code to bring down configsnap and run a before snapshot.</p>
<ol>
<li>If possible, enable “run from web”.</li>
</ol>
</li>
<li><p>Perform a “known good” snapshot on the working reference system.</p>
<ol>
<li>Use a custom snapshot name that self-identifies its purpose (“crossmachinecompare”)</li>
<li>Use a custom stage name that self-identifies its purpose (“knowngoodconfig”)</li>
</ol>
</li>
<li>Provide sample commands for direct compare on the “compare-to” system.</li>
</ol>
<h3 id="heading-code-call-outs">Code Call Outs</h3>
<h4 id="heading-running-directly-from-web">Running Directly from Web</h4>
<ul>
<li>The provided commands works around the fact that some systems block a directly downloaded script from being piped directly into bash - while a bit longer, this command works on a broader array of linux machines.</li>
</ul>
<h4 id="heading-zero-footprint">Zero Footprint</h4>
<ul>
<li>Zero Footprint is a constraint that avoids changing any system-wide configuration to use code on a target system. Generally everything runs out of a directory and package managers are not used. This is more important for diffing utilities since they should not place resources that end up in the diff itself - especially if they are assessing production systems. In the past, I have had to create a zero footprint install of the CIS tool used to assess CIS Benchmark Hardening for both Windows and Linux.</li>
</ul>
<h4 id="heading-provided-system-wide-additionalconf-example">Provided System-Wide ‘additional.conf’ Example</h4>
<ul>
<li>By default the tool does not compare all of /etc/ nor any user configuration files. The example shows the most basic level of including these important configuration areas</li>
<li>The example configuration provides an easy to extend example for refining the scope of comparison.</li>
</ul>
<h4 id="heading-complete-single-script-solution">Complete Single Script Solution</h4>
<ul>
<li>By creating it’s own configuration file and emitting the commands to use on “compare-to” target systems, the single script is fully self-contained and self-documenting.</li>
<li>Single script solutions are frequently easier to automate since many management systems allow embedded transport of scripts (but not of packages, support binaries, etc).</li>
</ul>
<h4 id="heading-the-code-itself">The Code Itself</h4>
<pre><code>#Run <span class="hljs-built_in">this</span> directly <span class="hljs-keyword">from</span> <span class="hljs-built_in">this</span> location <span class="hljs-keyword">with</span>: curl https:<span class="hljs-comment">//gitlab.com/missionimpossiblecode/MissionImpossibleCode/-/raw/master/ConfigsnapCreateKnownGood.sh -O /tmp/ConfigsnapCreateKnownGood.sh ; sudo bash /tmp/ConfigsnapCreateKnownGood.sh</span>

#Zerofootprint <span class="hljs-keyword">for</span> both known good and compare-to systems - just <span class="hljs-keyword">delete</span> /tmp/configsnap

<span class="hljs-keyword">if</span> [[ -z <span class="hljs-string">"$(command -v python)"</span> ]]; then 
  echo <span class="hljs-string">"Python must be installed and working, exiting..."</span>
  echo <span class="hljs-string">"If you cannot install python on this or the compare-to system, read here about building it in an isolated directory: https://stackoverflow.com/a/42903156"</span>
  exit <span class="hljs-number">5</span>
fi
mkdir -p /tmp/configsnap
curl https:<span class="hljs-comment">//raw.githubusercontent.com/rackerlabs/configsnap/master/configsnap -o /tmp/configsnap/configsnap</span>
chmod +x /tmp/configsnap/configsnap

cat &gt; <span class="hljs-regexp">/tmp/</span>configsnap/additional.conf &lt;&lt;<span class="hljs-string">'EOF_CONFIG'</span>
[allmachineconfig]
Type: directory
Directory: <span class="hljs-regexp">/etc/</span>

[userconfigs]
Type: directory
Directory: <span class="hljs-regexp">/home/</span>
File_Pattern: \..*
EOF_CONFIG

sudo ./configsnap --basedir=<span class="hljs-regexp">/tmp/</span>configsnap/snaps --verbose --tag=crossmachinecompare --phase=knowngoodconfig

cat &lt;&lt;- EndOfMessage

Next Steps:

<span class="hljs-number">1.</span> Sample scp command to pull <span class="hljs-built_in">this</span> on a system to compare to: 
   scp -r user_on_this_system<span class="hljs-meta">@thissystemdnsorip</span>:<span class="hljs-regexp">/tmp/</span>configsnap /tmp/configsnap
<span class="hljs-number">2.</span> Sample auto-compare command on compare-to system:
   sudo /tmp/configsnap/configsnap --basedir=<span class="hljs-regexp">/tmp/</span>configsnap/snaps --verbose --tag=crossmachinecompare --pre=knowngoodconfig --phase=post

To use <span class="hljs-keyword">as</span> a known good snapshot managed <span class="hljs-keyword">in</span> a centralized location, copy <span class="hljs-string">"/tmp/configsnap"</span> to a shared location (or use git to commit to a repository) where you can pull it onto <span class="hljs-built_in">any</span> system you wish to test <span class="hljs-keyword">for</span> drift or changes.

To clean the zero footprint install <span class="hljs-keyword">from</span> <span class="hljs-built_in">any</span> systems, run <span class="hljs-string">"sudo rm -rf /tmp/configsnap"</span>

EndOfMessage
</code></pre><h2 id="heading-source-code-for-this-post">Source Code for This Post</h2>
<p>The code for this post is kept up to date and can be invoked directly from the web in this repository location: <a target="_blank" href="https://gitlab.com/missionimpossiblecode/MissionImpossibleCode/-/blob/master/ConfigsnapCreateKnownGood.sh">ConfigsnapCreateKnownGood.sh</a></p>
<h4 id="heading-mission-impossible-code-series-inclusion">Mission Impossible Code Series Inclusion</h4>
<ul>
<li>The solution sticks to the Boring Technology selection criteria.</li>
<li>The solution is implemented in a single script.</li>
<li>The solution is Zero Footprint.</li>
<li>The solution is portable between linux distros and comparison systems.</li>
</ul>
<h2 id="heading-solution-architecture-heuristics-requirements-constraints-desirements-serendipities-applicability-limitations-and-alternatives">Solution Architecture Heuristics: Requirements, Constraints, Desirements, Serendipities, Applicability, Limitations and Alternatives</h2>
<blockquote>
<p>![](https://missionimpossiblecode.io/img/darwinterberg30base.png#floatleft "Deep Dive - Below The Water Line Discussion") The following content is a deep dive below the waterline into the nitty gritty details of how to take a similar approach to building solutions.</p>
<p><strong>NOTE: You do not need this information to successfully leverage this solution.</strong></p>
</blockquote>
<h4 id="heading-what-does-mean">What Does “&lt;==&gt;” Mean?</h4>
<p>The notation “&lt;==&gt;”, which may contain logic like “&lt;= AND =&gt;” is an attempt to <strong>visually reflect the trade-offs inherent in using heuristics to commit to seleting a position on a spectrum of possibilities</strong>. By documenting these trade-offs below - the construction and serendipities of the final tuning are revealed. This seems to do at least three things for the consumer of this information:</p>
<ol>
<li>You get to see the iceberg below the waterline of something I have built that I hope is “As simple as possible, but not simpler.” So you get to see why I claim that “The Creation of Simplicity is Necessarily a Complex Undertaking.”</li>
<li>You can more easily customize key parts of the solution to your liking, and not suffer from unintended consequences of those changes.</li>
<li>You can more easily apply this pattern to new problems that may be similar, but not identical.</li>
</ol>
<h3 id="heading-solution-architecture-heuristics">Solution Architecture Heuristics</h3>
<p>The overall solution is solving for “<strong>Use tooling to find system-wide configuration differences between two seperate linux installations to quickly isolate the differences between a known good and non-working system.</strong>”</p>
<h4 id="heading-requirement-satisfied-be-self-contained-including-instructions"><strong>Requirement: (Satisfied)</strong> Be Self Contained (Including Instructions)</h4>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: Bring Everything You Depend On &lt;= AND =&gt; Pack Light (Reference System)</li>
<li><strong>Reason</strong>: The more local dependencies a solution requires, the less portable it is and the more challenging it is to reuse across varying configurations. The gold standard is if the script can be used without instructions via the code containing embedded configuration and embedded instructions as needed.</li>
<li><strong>Coding Decisions:</strong><ul>
<li>Bring Everything: Use curl to download a raw copy of the python code from the repository - thereby avoiding dependencies on package managers (only a RHEL package exists) and Git (in the case of cloning the entire repo).</li>
<li>Bring Everything: Use configuration as code via a heredoc that creates the configuration file. This enables the entire solution to be in a single, run-from-web script. Configuration as code is also self-documenting by nature which avoids the need for external instructions.</li>
<li>Bring Everything: The Reference System code emits the instructions to be used on the Target Systems - providing User Instructions as Code also enables the self-contained, self-documenting nature of the solution.</li>
<li>Pack Light: While some few linux images (esp containers) may not include python, if it cannot be found, ask the user to resolve the dependency - with a hint on how to build python from source and run it without updating the entire system with Python.</li>
</ul>
</li>
</ul>
<h4 id="heading-desirement-satisfied-zero-footprint-approach"><strong>Desirement: (Satisfied)</strong> Zero Footprint Approach</h4>
<ul>
<li><p><strong>Mission Impossible Heuristic</strong>: Leave No Trace Behind &lt;= AND =&gt; Make Fingerprint Wipe-Down Easy</p>
</li>
<li><p><strong>Reasons</strong>:</p>
<ul>
<li>Dependencies and system level configuration require permissions and soil the system. Drift detection on hardened systems (e.g. CIS Benchmark) or change management regulated systems (e.g. FDA Regulated) generally should not have their system level configuration or files changed .</li>
<li>The diffing tool itself must ensure that it’s own code, config and data do not become part of the comparison it is performing.</li>
</ul>
</li>
<li><p><strong>Coding Decisions:</strong></p>
<ul>
<li>Use directory tree in /tmp.</li>
<li>Store Code, Config (additional.conf) and Snapshot Data in the same directory tree.</li>
<li>Cleanup is as easy as removing the root of the directory tree containing the code, config and data.</li>
</ul>
</li>
</ul>
<h4 id="heading-serendipity-discovered-bring-everything-you-depend-on-pack-light-target-systems"><strong>Serendipity: (Discovered)</strong> : Bring Everything You Depend On &lt;= AND =&gt; Pack Light (Target System(s))</h4>
<ul>
<li><strong>Prior Requirement / Desirement</strong>: Be Self Contained, Zero Footprint Approach</li>
<li><strong>Prior Coding Decisions Result:</strong><ul>
<li>By storing Code, Config and Snapshot Data in the same directory heirarchy the resultant directory can be copied to a central location or repository or directly to a Target System and it is immediately runable with only two commands (even less prep than the Reference System)</li>
</ul>
</li>
</ul>
<h4 id="heading-serendipity-discovered-least-privilege-approach"><strong>Serendipity: (Discovered)</strong> Least Privilege Approach</h4>
<ul>
<li><strong>Prior Requirement / Desirement</strong>: Zero Footprint Approach</li>
<li><strong>Prior Coding Decisions Result:</strong><ul>
<li>By using /tmp and storing Code, Config and Snapshot Data, special permissions are only needed to run ‘Configsnap’ itself and then the operations of Configsnap only change a temporary, non-tracked area of the system.</li>
</ul>
</li>
</ul>
<h4 id="heading-serendipity-discovered-identical-configsnap-version-configuration-and-baseline-snapshot"><strong>Serendipity: (Discovered)</strong> Identical Configsnap Version, Configuration and Baseline Snapshot</h4>
<ul>
<li><p><strong>Diffing Heuristic</strong>: Two comparison targets should not have captured differences introduced by the comparison process.</p>
</li>
<li><p><strong>Reason:</strong> When diffing across two systems, the version of the diffing utility (Configsnap) and it’s configuration must be identical for results to be valid. This includes using older versions of the diffing utility even if they are no longer available from the original source.</p>
</li>
<li><p><strong>Prior Coding Decisions Result:</strong></p>
<ul>
<li>By storing Code, Config and Snapshot Data in one directory tree the version of all these components is frozen at the time point when the “Reference Snapshot” was taken.</li>
</ul>
</li>
</ul>
<h4 id="heading-requirement-satisfied-maximize-applicable-linux-systems-this-can-be-used-with"><strong>Requirement: (Satisfied)</strong> Maximize Applicable Linux Systems This Can Be Used With</h4>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: Optimize Your Choices &lt;= AND =&gt; DeOptimize To Match The Breadth Of Required Scope</li>
<li><strong>Reasons</strong>: The usefulness of system-wide snapshots is applicable to all linux distros and architectures, ensure the proposed solutions reaches for this same scope.</li>
<li><strong>Coding Decisions:</strong><ul>
<li>Use curl to download a raw copy of the python code from the repository - thereby avoiding dependencies on package managers. Package managers complicate things because<ul>
<li>The utility must have already been packaged for that package manager platform.</li>
<li>The utility must have a package per OS architecture (e.g. x86_64 and arm)</li>
<li>The package preparation must occur frequently enough to have the latest version of the software.</li>
<li>There are many different script commands to accomodate all possible package managers.</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Building a Best Practice CloudFormation Custom Resource Pattern]]></title><description><![CDATA[I was looking to add the ability for users of a CloudFormation template to be able to specify networking, but without overcomplicating the existing parameter set or the required information gathering. Meeting the requirement ended up being the gatewa...]]></description><link>https://missionimpossiblecode.io/building-a-best-practice-cloudformation-custom-resource-pattern</link><guid isPermaLink="true">https://missionimpossiblecode.io/building-a-best-practice-cloudformation-custom-resource-pattern</guid><category><![CDATA[Infrastructure as code]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Working Code Example]]></category><category><![CDATA[Deep Dive]]></category><category><![CDATA[automation]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Thu, 08 Apr 2021 07:25:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657976182846/AO2YL7Pk6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was looking to add the ability for users of a CloudFormation template to be able to specify networking, but without overcomplicating the existing parameter set or the required information gathering. Meeting the requirement ended up being the gateway to learning how to create CloudFormation Custom Resources backed by Lambda. While I was at it, I made sure the code could be reused for future Custom Resource needs. This article shares the simplest possible way I could devise for automatically gathering the right information from the fewest parameters. It also presents a pattern for a well written CloudFormation Custom Function with enhanced exception logging and compact code.</p>
<blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657976307149/crtBRuo16.png" alt="darwinterberg30.png" /> "The Whole Berg - Above and Below the Waterline" Posts in the “Mission Impossible Code” <strong>Series</strong> contain toolsmithing information that is not necessary to reuse the solution - use the iceberg glyphs to know when the content is diving below the water line into “<strong>How I Made This</strong>”. The content is also designed to be skim-read.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657976449143/RbyqOUlsE.png" alt="darwinterberg30tip.png" /> "Tip of the Iceberg - Concise Summary Discussion" The “<strong>Tip of the Iceberg</strong>” icon indicates as simple as possible info on why and what in order to assess and implement.</p>
<p><span class="image-left"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657976530573/2Gw6G9lLQ.png" alt="darwinterberg30base.png" /></span> "Deep Dive - Below The Water Line Discussion" The “<strong>Below The Water Line</strong>” icon indicates a deep dive into nitty gritty details of how to take a similar approach to building solutions.</p>
</blockquote>
<h3 id="heading-pearl-diving-to-learn-custom-resources-lambda-and-python">Pearl Diving To Learn Custom Resources, Lambda and Python</h3>
<p>During this effort I mused how we sometimes do the equivalent of Pearl Diving when taking on new skills. Its the idea of deep learning a stack of one or more new things while under the pressure of needing the end state code to reflect a maturity level significantly higher than your beginner expertise in that stack. I have captured the details about Perl Diving - and when you should use it (because you usually should not) - in a companion post titled <a target="_blank" href="https://missionimpossiblecode.io/post/pearl-diving-just-in-time-learning-of-mature-coding-habits-for-a-new-stack/">Pearl Diving - Just In Time Learning of Mature Coding Habits For a New Stack</a></p>
<h3 id="heading-the-mission-objectives-and-parameters">The Mission Objectives and Parameters</h3>
<blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657976449143/RbyqOUlsE.png" alt="darwinterberg30tip.png" /> "Tip of the Iceberg - Concise Summary Discussion" <strong>Mission Objectives and Parameters</strong> articulate the final objectives that emerged from both the <strong>preplanning</strong> and <strong>build process</strong>. <strong>Code Summary</strong> gives an out line of the code fragments. <strong>Code Call Outs</strong> highlights significant constraints, innovations and possible alternatives in the code.</p>
</blockquote>
<ol>
<li><strong>Objective:</strong> Ensure that AWS VPC / Networking can be specified, with minimal complication of the existing easiest user experience case</li>
<li><strong>Desirable Constraints In Meeting Objective:</strong><ol>
<li>Adding the minimum number of new parameters to give the ability to select a network location for the scaling group.</li>
<li>Retaining simplicity of previous version to automatically use the default VPC by default.</li>
<li>Do not exceed code size limitations that would complicate the solution beyond the current simplicity of a single CloudFormation template (just for the sake of code size). Limit for CloudFormation embedded Lambda functions: 4KB . Limit for CloudFormation template file size when loaded from S3: 460.8 KB (note: the limit when submitting from the command line is 51.2 KB)</li>
</ol>
</li>
</ol>
<h3 id="heading-code-summary">Code Summary</h3>
<ol>
<li>Shows minimal CloudFormation (CF) Custom Resource, consisting of 3 CloudFormation Resources<ol>
<li>The Custom Function declaration - which contains the Python code (“VPCInfoLambda:” in the below)</li>
<li>The IAM Role declaration - permissions used by the Lambda execution (“CFCustomResourceLambdaRole:” in the below)</li>
<li>The “call” to the Custom Function - the input of parameters and return of data through a Cloud Formation Resource interface (“LookupVPCInfo:” in the below)</li>
</ol>
</li>
<li>A fragment shows how the resource data is retrieved inside the definition of a scaling group. (“InstanceASG:” in the below)</li>
<li>A fragment shows the definition of the parameter (“Parameters:” in the below)</li>
</ol>
<h3 id="heading-code-call-outs">Code Call Outs</h3>
<h4 id="heading-using-the-code-for-subnet-enumeration">Using The Code For Subnet Enumeration</h4>
<ul>
<li>Note that the parameter “<strong>SpecifyVPCToUse</strong>” does not use the type “AWS::EC2::VPC::Id” to get a drop down list of actual VPCs because it would defeat the above Desirable Constraint “Retaining simplicity of previous version to automatically use the default VPC by default.” However, if your organization NEVER uses default VPCs or disables them, changing the parameter type to “AWS::EC2::VPC::Id” actually improves the experience because users do not have to lookup VPC ids in the console and the one they select will always exist.</li>
<li>Note that the CF functions that retrieve data for “<strong>AvailabilityZones:</strong>” and “<strong>VPCZoneIdentifier:</strong>” could add the !Select function to only use a specified number of AZs and Subnets rather than using all available Subnets in the VPC.</li>
</ul>
<h4 id="heading-reusing-the-code-as-the-pattern-for-other-custom-resources">Reusing The Code As The Pattern For Other Custom Resources</h4>
<ul>
<li>Note that Lambda logging to CloudFormation is configured (including security) and helpful account and region context are output whenever trapped or untrapped exceptions occur.</li>
<li>Note the building of the object “<strong>responseData</strong>” - this is showing how to return <strong>multiple values</strong> when many examples show a much simpler structure for returning only a single value.</li>
<li>Note lines with “<strong>raise Exception</strong>” reuse the global exception handling for trapped known exceptions to keep code compact by reusing the logging, tracing and verbose error output code containing context cues.</li>
<li>Note the lines with “<strong>signal.alarm</strong>” are timeout handling - which is important in serverless.</li>
<li>Note that “<strong>CFCustomResourceLambdaRole:</strong>” is the least privilege IAM permissions for this function. If you build a function to do other things, the permissions in this YAML will need to be updated to match - but should be kept least privilege.</li>
</ul>
<h2 id="heading-source-code-for-this-article">Source Code For This Article</h2>
<h4 id="heading-this-code-working-in-production">This Code Working In Production</h4>
<p>This code was created for the solution <a target="_blank" href="https://gitlab.com/guided-explorations/aws/gitlab-runner-autoscaling-aws-asg/-/blob/main/GitLabElasticScalingRunner.cf.yml">GitLab HA Scaling Runner Vending Machine for AWS</a></p>
<h4 id="heading-the-code-itself">The Code Itself</h4>
<pre><code><span class="hljs-comment">#Arguments: Vpc-id or "DefaultVPC"</span>
<span class="hljs-comment">#Returns: vpc-id, number of subnets and ordered list of subnetids and az ids.  </span>
<span class="hljs-comment"># The index of these two return lists are correlated if it is desirable to choose less than the whole list using the CloudFormation function "Select" against both lists.</span>
  VPCInfoLambda:
    Type: 'AWS::Lambda::Function'
    Properties:
      Description: Returns the lowercase version of a string
      MemorySize: 256
      Runtime: python3.8
      <span class="hljs-keyword">Handler</span>: index.handler
      <span class="hljs-keyword">Role</span>: !GetAtt CFCustomResourceLambdaRole.Arn
      <span class="hljs-keyword">Timeout</span>: <span class="hljs-number">240</span>
      Code:
        ZipFile: |
          <span class="hljs-keyword">import</span> <span class="hljs-keyword">logging</span>
          <span class="hljs-keyword">import</span> traceback
          <span class="hljs-keyword">import</span> signal
          <span class="hljs-keyword">import</span> cfnresponse
          <span class="hljs-keyword">import</span> boto3

          LOGGER = logging.getLogger()
          LOGGER.setLevel(logging.INFO)

          <span class="hljs-keyword">def</span> <span class="hljs-keyword">handler</span>(<span class="hljs-keyword">event</span>, <span class="hljs-keyword">context</span>):
              <span class="hljs-comment"># Setup alarm for remaining runtime minus a second</span>
              signal.alarm((<span class="hljs-built_in">int</span>(context.get_remaining_time_in_millis() / <span class="hljs-number">1000</span>)) - <span class="hljs-number">1</span>)
              try:
                  LOGGER.info(<span class="hljs-string">'REQUEST RECEIVED:\n %s'</span>, <span class="hljs-keyword">event</span>)
                  LOGGER.info(<span class="hljs-string">'REQUEST RECEIVED:\n %s'</span>, <span class="hljs-keyword">context</span>)
                  <span class="hljs-keyword">if</span> <span class="hljs-keyword">event</span>[<span class="hljs-string">'RequestType'</span>] == <span class="hljs-string">'Delete'</span>:
                      LOGGER.info(<span class="hljs-string">'DELETE!'</span>)
                      cfnresponse.send(<span class="hljs-keyword">event</span>, <span class="hljs-keyword">context</span>, <span class="hljs-string">"SUCCESS"</span>, {
                           <span class="hljs-string">"Message"</span>: <span class="hljs-string">"Resource deletion successful!"</span>})
                      <span class="hljs-keyword">return</span>
                  elif <span class="hljs-keyword">event</span>[<span class="hljs-string">'RequestType'</span>] == <span class="hljs-string">'Update'</span>:
                      LOGGER.info(<span class="hljs-string">'UPDATE!'</span>)
                      cfnresponse.send(<span class="hljs-keyword">event</span>, <span class="hljs-keyword">context</span>, <span class="hljs-string">"SUCCESS"</span>,{
                           <span class="hljs-string">"Message"</span>: <span class="hljs-string">"Resource update successful!"</span>})
                  elif <span class="hljs-keyword">event</span>[<span class="hljs-string">'RequestType'</span>] == <span class="hljs-string">'Create'</span>:
                      LOGGER.info(<span class="hljs-string">'CREATE!'</span>)
                      request_properties = event.get(<span class="hljs-string">'ResourceProperties'</span>, <span class="hljs-keyword">None</span>)

                      VpcToGet = <span class="hljs-keyword">event</span>[<span class="hljs-string">'ResourceProperties'</span>].get(<span class="hljs-string">'VpcToGet'</span>, <span class="hljs-string">''</span>)
                      ec2 = boto3.resource(<span class="hljs-string">'ec2'</span>)
                      VpcCheckedList = []
                      TargetVPC = <span class="hljs-keyword">None</span>
                      vpclist = ec2.vpcs.all()
                      <span class="hljs-keyword">for</span> vpc <span class="hljs-keyword">in</span> vpclist:
                          VpcCheckedList.append(vpc.id)
                          <span class="hljs-keyword">if</span> VpcToGet == <span class="hljs-string">"DefaultVPC"</span> <span class="hljs-keyword">and</span> vpc.is_default == <span class="hljs-literal">True</span>:
                              TargetVPC=vpc
                          elif vpc.vpc_id == VpcToGet:
                              TargetVPC=vpc

                      <span class="hljs-keyword">if</span> TargetVPC == <span class="hljs-keyword">None</span>:
                        <span class="hljs-keyword">raise</span> <span class="hljs-keyword">Exception</span>(f<span class="hljs-string">'VPC {VpcToGet} was not found among the ones in this account and region, VPC which are: {", ".join(VpcCheckedList)}'</span>)
                      <span class="hljs-keyword">else</span>:
                        VPCOutput = TargetVPC.id
                        subidlist = []
                        zoneidlist = []
                        subnets = <span class="hljs-keyword">list</span>(TargetVPC.subnets.all())
                        <span class="hljs-keyword">for</span> subnet <span class="hljs-keyword">in</span> subnets:
                          subidlist.append(subnet.id)
                          zoneidlist.append(subnet.availability_zone)
                        subidOutput = <span class="hljs-string">","</span>.join(subidlist)
                        zoneidOutput = <span class="hljs-string">","</span>.join(zoneidlist)
                        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> subnets:
                          <span class="hljs-keyword">raise</span> <span class="hljs-keyword">Exception</span>(f<span class="hljs-string">'There are no subnets in VPC: {VpcToGet}'</span>)
                        LOGGER.info(<span class="hljs-string">'subnet ids are: %s'</span>, subidOutput)
                        LOGGER.info(<span class="hljs-string">'zone ids are: %s'</span>, zoneidOutput)

                      responseData = {}
                      responseData[<span class="hljs-string">'VPC_id'</span>] = VPCOutput
                      responseData[<span class="hljs-string">'OrderedSubnetIdList'</span>] = subidOutput
                      responseData[<span class="hljs-string">'OrderedZoneIdList'</span>] = zoneidOutput
                      responseData[<span class="hljs-string">'SubnetCount'</span>] = <span class="hljs-keyword">len</span>(subidlist)
                      cfnresponse.send(<span class="hljs-keyword">event</span>, <span class="hljs-keyword">context</span>, cfnresponse.SUCCESS, responseData)                                  

              <span class="hljs-keyword">except</span> <span class="hljs-keyword">Exception</span> <span class="hljs-keyword">as</span> err:
                  AccountRegionInfo=f<span class="hljs-string">'Occured in Account {context.invoked_function_arn.split(":")[4]} in region {context.invoked_function_arn.split(":")[3]}'</span>
                  FinalMsg=<span class="hljs-keyword">str</span>(err) + <span class="hljs-string">' '</span> + AccountRegionInfo
                  LOGGER.info(<span class="hljs-string">'ERROR: %s'</span>, FinalMsg)
                  LOGGER.info(<span class="hljs-string">'TRACEBACK %s'</span>, traceback.print_tb(err.__traceback__))
                  cfnresponse.send(<span class="hljs-keyword">event</span>, <span class="hljs-keyword">context</span>, <span class="hljs-string">"FAILED"</span>, {
                      <span class="hljs-string">"Message"</span>: <span class="hljs-string">"{FinalMsg}"</span>})

          <span class="hljs-keyword">def</span> timeout_handler(_signal, _frame):
              <span class="hljs-string">'''Handle SIGALRM'''</span>
              <span class="hljs-keyword">raise</span> <span class="hljs-keyword">Exception</span>(<span class="hljs-string">'Time exceeded'</span>)

          signal.signal(signal.SIGALRM, timeout_handler)


  <span class="hljs-comment">#Custom Function IAM Role Declaration</span>
  CFCustomResourceLambdaRole:
    <span class="hljs-keyword">Type</span>: AWS::IAM::<span class="hljs-keyword">Role</span>
    Properties:
      AssumeRolePolicyDocument:
        <span class="hljs-keyword">Version</span>: <span class="hljs-string">"2012-10-17"</span>
        <span class="hljs-keyword">Statement</span>:
          - Effect: <span class="hljs-string">"Allow"</span>
            Principal:
              Service:
                - <span class="hljs-string">"lambda.amazonaws.com"</span>
            <span class="hljs-keyword">Action</span>:
              - <span class="hljs-string">"sts:AssumeRole"</span>
      Policies:
        - PolicyName: <span class="hljs-string">"lambda-write-logs"</span>
          PolicyDocument:
            <span class="hljs-keyword">Version</span>: <span class="hljs-string">"2012-10-17"</span>
            <span class="hljs-keyword">Statement</span>:
              - Effect: <span class="hljs-string">"Allow"</span>
                <span class="hljs-keyword">Action</span>:
                  - <span class="hljs-string">"logs:CreateLogGroup"</span>
                  - <span class="hljs-string">"logs:CreateLogStream"</span>  
                  - <span class="hljs-string">"logs:PutLogEvents"</span>
                <span class="hljs-keyword">Resource</span>: <span class="hljs-string">"arn:aws:logs:*:*"</span>
        - PolicyName: <span class="hljs-string">"describe-vpcs-and-subnets"</span>
          PolicyDocument:
            <span class="hljs-keyword">Version</span>: <span class="hljs-string">"2012-10-17"</span>
            <span class="hljs-keyword">Statement</span>:
              - Effect: <span class="hljs-string">"Allow"</span>
                <span class="hljs-keyword">Action</span>:
                  - <span class="hljs-string">"ec2:DescribeVpcs"</span>
                  - <span class="hljs-string">"ec2:DescribeSubnets"</span>
                <span class="hljs-keyword">Resource</span>: <span class="hljs-string">"*"</span>

  <span class="hljs-comment">#Calling Function to Retrieve Data</span>
  LookupVPCInfo:
    <span class="hljs-keyword">Type</span>: Custom::VPCInfo
    Properties:
      ServiceToken: !GetAtt VPCInfoLambda.Arn
      VpcToGet: !<span class="hljs-keyword">Ref</span> SpecifyVPCToUse
</code></pre><p>Fragment That Demonstrates Parameter Collection</p>
<pre><code><span class="hljs-comment">#Parameter declaration with important default</span>
<span class="hljs-attr">Parameters:</span>
  <span class="hljs-attr">SpecifyVPCToUse:</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">&gt;
      DefaultVPC - finds the VPC and configures all of its subnets for you. Otherwise type 
      in the VPC id of a VPC in the same region where you run the template. 
      All subnets and azs of the chosen vpc will be used.
      The VPC and chosen subnets must be setup in a way that allows the runner instances 
      to resolve the DNS name and connect to port 443 on the GitLab instance URL you provide.      
</span>    <span class="hljs-attr">Default:</span> <span class="hljs-string">DefaultVPC</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">String</span>
    <span class="hljs-comment"># While it is tempting to make the above parameter of type "AWS::EC2::VPC::Id" </span>
    <span class="hljs-comment"># this prevents automatic discovery and usage of the DefaultVPC.</span>
    <span class="hljs-comment"># However, if your organization NEVER uses default VPCs or disables them, changing </span>
    <span class="hljs-comment"># the type to AWS::EC2::VPC::Id actually improves the user experience because users do not have to </span>
    <span class="hljs-comment"># lookup VPC ids in the console.</span>

<span class="hljs-comment">#Fragment showing using the resultant data from the custom function</span>
  <span class="hljs-attr">InstanceASG:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::AutoScaling::AutoScalingGroup</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">AvailabilityZones:</span> <span class="hljs-type">!Split</span> [<span class="hljs-string">","</span>,<span class="hljs-type">!GetAtt</span> <span class="hljs-string">LookupVPCInfo.OrderedZoneIdList</span>]
      <span class="hljs-attr">VPCZoneIdentifier:</span> <span class="hljs-type">!Split</span> [<span class="hljs-string">","</span>,<span class="hljs-type">!GetAtt</span> <span class="hljs-string">LookupVPCInfo.OrderedSubnetIdList</span>]
</code></pre><h2 id="heading-solution-architecture-heuristics-requirements-constraints-desirements-serendipities-applicability-limitations-and-alternatives">Solution Architecture Heuristics: Requirements, Constraints, Desirements, Serendipities, Applicability, Limitations and Alternatives</h2>
<blockquote>
<p><span class="image-left"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657976530573/2Gw6G9lLQ.png" alt="darwinterberg30base.png" /></span> "Deep Dive - Below The Water Line Discussion" The following content is a deep dive below the waterline into the nitty gritty details of how to take a similar approach to building solutions.</p>
<p><strong>NOTE: You do not need this information to successfully leverage this solution.</strong></p>
</blockquote>
<p>The following list demonstrates the Architectural thrust of the solution. This approach is intended to be pure to simplicity of operation and maintenance, rather than purity of a language or framework or development methodology. It is also intended to have the least possible dependencies. The below is a mix of a) previously committed dispositions for the Overall Solution, b) predetermined design points and c) things discovered and adopted during the development process (emergent or organic solution architecture component).</p>
<h4 id="heading-what-does-mean">What Does “&lt;==&gt;” Mean?</h4>
<p>The notation “&lt;==&gt;”, which may contain logic like “&lt;= AND =&gt;” is my attempt to <strong>visually reflect the dynamic tension or trade-offs inherent in using heuristics to commit to fixing positions on a spectrum of possibilities</strong>. During the solution formulation these positions fluctuate as you try to simultaneously tune multiple, interacting vectors through trial and error. Even when I do it on purpose, I still can’t completely understand how I am tuning multiple vectors at once and why the results of the process repetitively turn out to effectively solve for multiple vectors. However the internals work, once you’ve produced a sufficiently satisfactory solution, their final positions reflect a complete tuning. They are sort of like custom presets on a sound equalizer. By documenting them as I have done here - I reveal my impression of the final tuning. I feel this does at least three things for the consumer of this information:</p>
<ol>
<li>You get to see the iceberg below the waterline of something I have built that I hope is “As simple as possible, but not simpler.” So you get to see why I claim that “The Creation of Simplicity is Necessarily a Complex Undertaking.”</li>
<li>You can more easily customize key parts of the solution to your liking, while retaining the value attached to other parts of the tuning.</li>
<li>You can more easily apply this pattern to new problems that may be like it, but not identical.</li>
</ol>
<h3 id="heading-solution-architecture-heuristics-for-the-overall-solution">Solution Architecture Heuristics for the Overall Solution</h3>
<p>The overall solution is solving for “<strong>Allow Network Configuration Selection, Using the Least New Parameters and Without Complicating the Existing Easiest User Experience Case</strong>”</p>
<ul>
<li><p><strong>Overall Solution Requirement: (Satisfied)</strong> Ensure that VPC / networking can be specified.</p>
<ul>
<li><strong>Benefits:</strong> Accommodate advanced AWS implementations generally have a design to their VPCs and may even disable the Default VPC. Most CloudFormation templates that are generalized to being a tool must provide this flexibility.<ul>
<li><strong>Coding Decisions:</strong> To actually make the VPC selection enhancement.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Overall Solution Requirement: (Satisfied)</strong> Add the minimum number of new parameters to give the ability to select a network location for the scaling group.</p>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: Make Sophisticated Spy Gadgets &lt;=AND=&gt; Have Simple Controls<ul>
<li><strong>Benefits:</strong> Adoption Driven Development: simple understanding of parameters, simple information collection, easy adoption of new version.</li>
<li><strong>Discarded: Innovation Over Defacto Alternatives</strong>: It is very common for these types of templates to expose two parameters to take a list of subnet IDs and a list of availability zone names. It is then incumbent on the user to collect these two lists of IDs, ensure they match the desired VPC and ensure that the zones list exactly correlates to the zones of the selected subnets.</li>
<li><strong>Coding Decisions:</strong> One parameter for the target VPC is used and defaults to the special value “DefaultVPC”. Users who need to use a specific VPC are more likely to know how to look it up or have been provided it by an Infrastructure Automation engineer. Another possibility is that experienced Infrastructure Automation engineer’s create code over this that forces specific VPCs to be used.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Overall Solution Requirement: (Satisfied)</strong> Retaining simplicity of previous version to automatically use the default VPC by default.</p>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: Make Sophisticated Spy Gadgets &lt;=AND=&gt; Have Simple Controls<ul>
<li><strong>Benefits:</strong> the solution is much simpler to use for beginners or when simply kicking the tires.</li>
<li><strong>Coding Decisions:</strong> The VPC parameter defaults to “DefaultVPC” - when this value is detected, it automatically locates the default VPC and enumerates it’s subnets and availability zones. This actually took a lot design and coding - compared to the previous very simple implementation which used a built-in CloudFormation function to lookup AZs.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Overall Solution Limitation:</strong> Cannot specify subnets / availability zones</p>
<ul>
<li><strong>Reason:</strong> the solution only allows selecting a VPC and then it uses all subnets of the VPC - this is for the sake of simplicity.</li>
<li><strong>Limitation Rationale:</strong> For a first MVC, being able to select VPC gives most of the benefits of enabling a user to use custom VPCs they have prepared according to their practices with a single parameter.</li>
<li><strong>Limitation Architecture</strong>: Sometimes subnet level selection is provided because a region only has two AZs - this problem is avoided by enumerating existing subnets. Sometimes subnet level selection is provided because certain instance types don’t exist or are constantly exhausted in an AZ - this problem is avoided because the underlying template is capable of selecting multiple instance types, the unavailability of an instance type in an AZ can be managed by providing multiple instance types in the list.</li>
<li><strong>Limitation Removal Anticipation</strong>: This solution was also engineered specifically to anticipate the removal of this limitation, yet with minimum parameters, by customizers or in a future release. The CloudFormation Custom Resource returns the total subnets found and “order guaranteed” lists of the subnets and availability zones. This would allow adding a single parameter “Number of Availability Zones To Use” - the !Select CF function could then only use that number of randomly selected subnets, yet with matching AZs in the AZ parameter to AutoScaling Groups.</li>
</ul>
</li>
<li><p><strong>Overall Solution Limitation:</strong> Cannot use AWS::EC2::VPC::Id to make VPCs list a drop down in UI based template execution.</p>
<ul>
<li><strong>Reason:</strong> This parameter type cannot indicate nor default to the Default VPC. This would inordinately complicate the simplest user experience case which must be retained.</li>
</ul>
</li>
</ul>
<h3 id="heading-solution-architecture-heuristics-for-the-cloudformation-custom-resource-working-pattern">Solution Architecture Heuristics for the CloudFormation Custom Resource Working Pattern</h3>
<p><strong>CF Custom Resource Requirement: (Satisfied)</strong> Be compact.</p>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: Bring Everything You Depend On &lt;= AND =&gt; Pack Light.</li>
<li><strong>Reason</strong>: Code and file size limitations above</li>
<li><strong>Coding Decisions:</strong> Reuse of the default exception handling for both unanticipated errors and for calling from trapped errors. Used boto3 “resources” over “clients” as code is less verbose and easier to read.</li>
</ul>
<p><strong>CF Custom Resource Requirement: (Satisfied)</strong> Implement proper exception handling &lt;==&gt; despite size concerns &lt;==&gt; be compact.</p>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: If You Are Going To Die, Write a Note About Who Done It and Where.</li>
<li><strong>Reasons</strong>: a) The call sack is deep and remote, b) debugging cycles are long for IaC</li>
<li><strong>Coding Decisions:</strong> Find out the most concise, functional Python exception handling (Pearl Dive) which is likely <strong>NOT</strong> the best practice exception handling. Call that exception handling for reporting of caught exceptions. This was implemented with Python exception handling and the Python traceback Python module.</li>
</ul>
<p><strong>CF Custom Resource Requirement: (Satisfied)</strong> Have exceptions report maximum context and, where possible, troubleshooting hints.</p>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: If You Are Going To Die, Write a Note About Who Done It and Where.</li>
<li><strong>Reasons</strong>: a) Improve troubleshooting of external problems (like a mismatch between account and/or region and a specified VPC). b) Like myself, other users of this function may not be experts in CF Custom Resources, Lambda or Python - they may be Deep Diving when trying to troubleshoot problems, c) To get more detail in Lambda logs for an error that was blocking progress - multiple blocking error conditions were immediately cleared upon implementing this.</li>
<li><strong>Coding Decisions:</strong> Extract the AWS Account ID and AWS Region from the Lambda execution context and report it in exceptions. When reporting a VPC Not Found type error, report the VPC list that was enumerated to give evidence that the VPC actually does not exist and a context hint because the enumerated VPCs can be found. Use the logging module.</li>
</ul>
<p><strong>CF Custom Resource Requirement: (Satisfied)</strong> Always build using least privileged security.</p>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: Disclose Everything Needed For Success &lt;=AND=&gt; Use a Need to Know Basis</li>
<li><strong>Reasons</strong>: a) It’s the right thing to do in all coding, but even more so when promoting or reusing patterns - bad sample code is a known attack vector, b) having “least privileged” security built-in fuels adoption and reuse</li>
<li><strong>Coding Decisions:</strong> Aside from standard CloudWatch logging access for Lambda, this function needed to read VPC data and enumerate subnets - so a new, clearly named, IAM policy was created and contained only “ec2:DescribeVpcs” and “ec2:DescribeSubnets”.</li>
</ul>
<p><strong>CF Custom Resource Desirement: (Satisfied)</strong> Leverage common and available Python modules to simplify the code.</p>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: Build The Best Tools &lt;=AND=&gt; Use Available Tools</li>
<li><strong>Reasons</strong>: Code is simpler to read and reuse and more compact.</li>
<li><strong>Coding Decisions:</strong> Use of modules: logging, traceback and signal. Use of module “cfnresponse” - many examples had a lot more code in order to manually implement a cloud formation response to the calling stack.</li>
</ul>
<p><strong>CF Custom Resource Desirement: (Satisfied)</strong> Have this function be the basis of a template for future reuse.</p>
<ul>
<li><strong>Mission Impossible Heuristic</strong>: Use The Best Implementation &lt;=AND=&gt; Use Available Tools</li>
<li><strong>Reasons</strong>: I just always try to do this because real world problems are the best source of raw matter for working examples.</li>
<li><strong>Coding Decisions:</strong> Demonstrate many required capabilities such as: passing parameters in, passing multiple data values out, timeout handling, exception handling, use generic IAM CF block for future reuse.</li>
</ul>
<p><strong>CF Custom Resource Serendipity: (Satisfied)</strong> Support timeout functionality for Lambda serverless.</p>
<ul>
<li><strong>Mission Impossible Heuristic:</strong> Use Ad-Hoc Innovation &lt;=AND=&gt; Use Existing Patterns</li>
<li><strong>Reasons:</strong> Eliminate the source of tough problems by leveraging the wisdom of examples.</li>
<li><strong>Coding Decisions:</strong> While looking through many coding examples I noticed a few were monitoring for a timeout and implemented it. It turned out that I had situations where I exceeded the timeout which informed tuning the maximum allowed run time. I also found out later from someone experienced in CF custom resources that the timeout is a critical bit of code. Most example code was missing this.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Pearl Diving - Just In Time Learning of Mature Coding Habits For a New Stack]]></title><description><![CDATA[I mused how we sometimes do the equivalent of Pearl Diving when taking on new skills. Its the idea of deep learning a stack of one or more new things while under the pressure of needing the end state code to reflect a maturity level significantly hig...]]></description><link>https://missionimpossiblecode.io/pearl-diving-just-in-time-learning-of-mature-coding-habits-for-a-new-stack</link><guid isPermaLink="true">https://missionimpossiblecode.io/pearl-diving-just-in-time-learning-of-mature-coding-habits-for-a-new-stack</guid><category><![CDATA[Mission Impossible Code]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Infrastructure as code]]></category><category><![CDATA[cloudformation]]></category><category><![CDATA[Deep Dive]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Thu, 08 Apr 2021 07:14:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657976063175/43j7zMYs3.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I mused how we sometimes do the equivalent of Pearl Diving when taking on new skills. Its the idea of deep learning a stack of one or more new things while under the pressure of needing the end state code to reflect a maturity level significantly higher than your beginner expertise in that stack. It’s a variation on deep dive, but with the treasure of learning multiple new technologies. I have captured the details about Pearl Diving - and when you should use it (because you usually should not) - in this post.</p>
<p>During the effort described in <a target="_blank" href="https://missionimpossiblecode.io/post/building-a-best-practice-cloudformation-custom-resource-pattern/">Building a Best Practice CloudFormation Custom Resource Pattern</a>, it was necessary to learn and create mature-ish code in the following stack: CloudFormation Custom Resources, Lambda (to back the custom resource), Python and Boto3.</p>
<p>Pearl Diving is also one of the Mission Impossible Code Diametric Spectrums: <strong>Have Seasoned Skills Through Experience and Practice &lt;=AND=&gt; Do Pearl Diving To Learn Skills Deeply and Quickly.</strong></p>
<p><a target="_blank" href="https://www.linkedin.com/in/timpoffenbarger">Tim Poffenbarger</a> and I were discussing what more universal human learning experience is similar to Pearl Diving that might help with understanding the idea. We came up with learning to drive a car. You have to simultaneously become fairly well skilled in: 1) operating the vehicle, 2) following unstated rules of the road, as well as 3) posted signs and 4) watch for the dynamic hazards posed by other vehicles. If you learned on a stick shift you also had a mini-stack inside of operating the vehicle as you got used 1a) easing the clutch, 1b) while easing the throttle and 1c) knowing when to shift. Should I even mention adding the emergency / parking break in order to do a uphill start in a standard? In any case, learning to drive seems to be another required Pearl Diving learning expedition that many of us go through - and then get to go through again when teaching our young ones to drive ;)</p>
<p>Pearl Diving is, in my opinion, usually a learning antipattern because it combines learning new languages and their best practices right away. So it is a place that I feel reluctance to go. To that end, I have documented some of the inherent problem / solution heuristics that forced the Pearl Diving path on this project.</p>
<p>The pressure of needing it to be fairly mature from the start included:</p>
<ul>
<li><strong>Small Code Required</strong>: A prime objective of this solution is to avoid breaking any size barriers that would force it to be more than one CloudFormation template. Those limits are 4KB for a Lambda function (when inline in a CloudFormation template) and the overall CloudFormation template size limit of 460KB (when sourced from S3). To leave room for future enhancements to the overall solution, a persistent “size minimizing” rule must be applied. To this end, some finer best practices for Python coding were regressed to keep the code small.</li>
<li><strong>Deep Stack Debugging</strong>: Another reason for Pearl Diving mode on this project was the call stack complexity required for final testing. It happens within Python, which is within Lambda, which is within CloudFormation. So this forced the issue of needing a mature implementation of exception handling and logging - because it is the only way to debug that deep. This investment paid off massively as soon as it was made. A previous post discussing this common problem described it as “5 miles out in a tunnel at the bottom of a mine shaft”: <a target="_blank" href="https://missionimpossiblecode.io/post/at-the-coal-face-code-for-debugging-deep-powershell-execution/">At the Coal Face: Code for Debugging Deep PowerShell Execution</a></li>
<li><strong>IaC Test Cycles Are Long</strong>: Personally I feel that Infrastructure as Code iteration cycles can be quite long compared to other types of testing and prototyping. Combined with how deep CloudFormation Custom Resources are in the call stack, this increases the desire to invest in a working pattern for future functions - especially with exception handling and logging for debugging.</li>
<li><strong>High Reuse Potential:</strong> The specific need for a CloudFormation template consumer to select their networking location is very recurrent and many solutions are overly complex in what they require the end user to provide for parameters. This sub-solution will likely be reused many times.</li>
<li><strong>Near-Future Feature Request Probability</strong>: The current solution allows one to pick the VPC, but then it uses all subnets of that VPC. I am pretty sure I will receive a feature request for either a) selecting specific subnets or b) selecting less than all the subnets of the target VPC. To these ends the implementation returns ordered lists of subnets and AZs so they can be predictably correlated and it returns the total subnets (index) - both of these are unused for this implementation at the current time. Some would argue this is an overly early optimization - I would argue it is a good medium term bet based on how many other of these types of templates I’ve worked on in the past, combined with the vast audience I hope can benefit from this automation.</li>
<li><strong>Crossing a Threshold of Implementation Complexity</strong>: Another, but important reason for Pearl Diving is to have a working example best practice template for new solutions. For a long time I have avoided the overhead of creating Custom Resources in CloudFormation - but the solution needs are only getting more complex and so having a pattern for creating all kinds of new Custom Resources is extremely valuable as it will accelerate future efforts substantially.</li>
</ul>
<p>It is important to know that in past Pearl Diving expeditions I have frequently done a strategic quit on servicing one or more of the Pearl Diving pressures similar to those listed above for this solution. As with Mission Impossible Code heuristics, it seems to be the constraints of the heuristics during experimentation that leads to innovating a solution that fits more of them than expected - but it is hard to predict a head of time what ones will be significantly deoptimized or abandoned.</p>
<h3 id="heading-pearl-diving-is-better-and-faster-with-an-expert-coach-along">Pearl Diving Is Better And Faster With an Expert Coach Along</h3>
<p>Pearl Diving can be done solo - but it takes much, much longer to work through unfamiliar syntax, object structures and handoffs than if you have a expert coach along! An individuals that help a Pearl Diver must have a coaching perspective because by definition the Pearl Diver will constantly ask questions that are out of their depth. Non-coaching experts will be frustrated with the lack of fundamental knowledge of the technologies in view. Coaching experts - especially with regard to learning code - tend to recognize the desire of someone who is proficient in high level best practices in other languages, will have a natural desire to acquire best practice knowledge quickly - especially the kind that is not easily revealed by search engines. For instance, in this Pearl Dive, it was very challenging to find examples that used boto3 “resources” rather than the more verbose “client” examples. It was hard to even come to an understanding that resources were more appropriate for my implementation needs than clients.</p>
<p>My expert coach in this effort was the Python Wizard <a target="_blank" href="https://www.linkedin.com/in/timpoffenbarger">Tim Poffenbarger</a>. He patiently offered wisdom and examples to the long sets of questions I had. Huge thanks to Tim!</p>
]]></content:encoded></item><item><title><![CDATA[Mission Impossible Code Heuristics for Creating Super-Spy Code That Always Gets the Job Done]]></title><description><![CDATA[Super action spies like Ethan Hunt, Jason Borne and Evelyn Salt live in an ethos of getting the job done no matter what! They complete their missions in vastly diverse conditions and in the face of the unexpected.
Super spies make use of specialized ...]]></description><link>https://missionimpossiblecode.io/mission-impossible-code-heuristics-for-creating-super-spy-code-that-always-gets-the-job-done</link><guid isPermaLink="true">https://missionimpossiblecode.io/mission-impossible-code-heuristics-for-creating-super-spy-code-that-always-gets-the-job-done</guid><category><![CDATA[Mission Impossible Code]]></category><category><![CDATA[automation]]></category><category><![CDATA[code]]></category><category><![CDATA[Deep Dive]]></category><category><![CDATA[Infrastructure as code]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Sat, 27 Feb 2021 09:20:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657975884817/hg2dSDVeo.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Super action spies like Ethan Hunt, Jason Borne and Evelyn Salt live in an ethos of getting the job done no matter what! They complete their missions in vastly diverse conditions and in the face of the unexpected.</p>
<p>Super spies make use of specialized tools and techniques when available (and working), but simple and pragmatic alternatives are always top of mind. They jump out of windows, walk across moving cars, use household objects as weapons and drive cars down staircases. They are consistently fashioning situational tools of whatever is found around them. They don’t think of objects and situations as having fixed purposes - but rather that objects and situations are flexible to serve their imposed purposes.</p>
<p>Is it possible to write code that acts like a super spy? Over time I have adhered to a set of coding design heuristics whose parallels to super spy behaviors are an intriguing.</p>
<p>The Super-spy’s hard bent commitment to hyper-pragmatism is counter balanced by an equal commitment to hyper-planning. Every action, every move and every breath will be planned to a tee if and when possible. To the super-spy, planning and opportunism are not seen as two opposite ways to approach a problem, but rather as perfectly complimentary.</p>
<p>A super-spy’s work is never put in an art gallery, nor does it underpin the workings of some great machine. Their work is judged by results alone. While super-spies appreciate beautiful art and love excellent machines - making such things is simply not the skills they bring to bear on their work.</p>
<p>As a coding disposition, pragmatism first, frequently means breaking conventions of coding style, verbosity or slavish adherence to best practices - to go for what works under the most diverse conditions.</p>
<h2 id="heading-recognize-when-an-effort-is-not-a-spy-mission">Recognize When an Effort is Not a Spy Mission</h2>
<p>Inevitably when one speaks with passion and advocacy of a certain approach - more than a few take it as the declaration of a universal manifesto. That the principles discussed are being claimed to apply to all circumstances, for all time. I will tell you right now that I love mission impossible coding. However, that love has also attracted me (self-selected me) to a specific area of experimentation. I am a Cloud DevOps Automation Developer and as such I am constantly called upon to knit together systems and requirements that span many different technologies, contexts and layers.</p>
<p>I admit that super-spies have the same self-selection - they are called upon to knit together a variety of diverse requirements, equipment and people to get the job done. We all intuitively understand that James Bond is the best guy to drive the 2 million dollar super-spy car. But we also understand that Q - not James Bond - is the best one to build the spy car.</p>
<p>Some problems require super-spy field methods and some problems require spy car engineering methods. The methods advocated here might not only fail miserably at building spy cars, most are likely to be anti-patterns to such an effort.</p>
<h2 id="heading-when-possible-drive-on-roads">When Possible, Drive on Roads</h2>
<p>So if you find yourself repulsed by the methods advocated here because they do not stay within the constructs of a language, do not obey rules for self-documentation or because the solution does not account for proper modularity or other similar details, then, please keep building super-spy cars and weapons, because they are very important in the grand scheme!</p>
<h2 id="heading-qualities-of-a-super-spy">Qualities of a Super-Spy</h2>
<p>The rest of this series will look at how rugged and opportunistic super-spies engage their work using specific characteristics that ensure they can get the job done no matter what. Some of these are:</p>
<ul>
<li>tenacious</li>
<li>resilient</li>
<li>resourceful</li>
<li>opportunistic</li>
<li>self-sufficient</li>
<li>self-reliant</li>
<li>flexible</li>
<li>pragmatic</li>
<li>ready</li>
<li>minimal assumptions</li>
<li>fault tolerant</li>
<li>predictive</li>
<li>perceptive</li>
<li>precise</li>
<li>meticulous</li>
</ul>
<h2 id="heading-train-your-super-spy-instincts-using-design-heuristics">Train Your Super-Spy Instincts Using Design Heuristics</h2>
<p>Do the above characteristics sometimes conflict with one another? <strong>Yes</strong></p>
<p>Is it sometimes difficult to discern which of these should be prioritized in a given situation? <strong>Yes</strong></p>
<p>Despite these challenges, do these heuristics generate results? <strong>YES</strong></p>
<p><strong>A design heuristic is like a needle on a gauge</strong> - it indicates which end of a spectrum of choice to favor - all other things being equal. However, when heuristics are combined, they have competitive tensions in the overall determination of what priorities should be optimized - precisely because all other things are NOT equal when combing them. (A combined set of heuristics are more like connected points in 3D space that define a shape within which are the possible solutions)</p>
<p>To get better at leveraging design heuristics, one must step in and practice - feeling and resolving these tensions again and again. It is not an unnatural learning mode for the human mind and it is supported by the ideas of Agile <strong>Discovery</strong>. The rewarding result is the training of your “instincts” so that they come up with pragmatic solutions given a complex set of input criteria. Design heuristics also account for the real-world experience that many problems are solvable with multiple different solutions. An approach of rigid laws can imply that there are few solutions or one solution to a given problem.</p>
<p>In the spirit of working examples, a good way to learn about how they are applied in Mission Impossible Code is to take a look at this blog <a target="_blank" href="https://missionimpossiblecode.io/post/mission-impossible-code-part-2-extreme-multilingual-iac-via-standard-code-for-preflight-tcp-connect-testing-a-list-of-endpoints-in-both-bash-and-powershell/">Mission Impossible Code Part 2: Extreme Multilingual IaC (via Standard Code for Preflight TCP Connect Testing a List of Endpoints in Both Bash and PowerShell)</a>, paying special attention to the section titled <a target="_blank" href="https://missionimpossiblecode.io/post/mission-impossible-code-part-2-extreme-multilingual-iac-via-standard-code-for-preflight-tcp-connect-testing-a-list-of-endpoints-in-both-bash-and-powershell/#architecture-heuristics-requirements-constraints-desirements-serendipities-applicability-limitations-and-alternatives">Architecture Heuristics: Requirements, Constraints, Desirements, Serendipities, Applicability, Limitations and Alternatives</a></p>
<h2 id="heading-lesson-1-right-now-degrade-your-implementation-for-simplicity-and-compatibility-reach">Lesson 1 Right Now - Degrade Your Implementation For Simplicity and Compatibility Reach</h2>
<p>Super-spies will quickly and purposely degrade their choices from the “best” or “purpose specific” option to older, at hand options. Don’t have a modern weapon like a gun? Use an ink pen instead!</p>
<p>Here is a case in point - PowerShell’s task scheduling CMDLets versus schtasks.exe. Knitting together three or more PowerShell CMDlets for scheduling a simple task is challenging - it takes hours or days the first time, but it seems to take hours or days to reliably update the same code for newly discovered requirements. In my opinion this is because the CMDLet set reflects the underlying object structures of scheduled tasks a little too directly (A place where PowerShell implementations <strong>usually</strong> hide the underlying complexity). Consequently scheduling most tasks requires creating 3 or more types of objects with CMDLets and parameters for each. To add to this, these CMDLets are not available on all versions of PowerShell and they are easy to confuse with PowerShell job scheduling CMDlets.</p>
<p>I’m thinking Ethan Hunt would prefer schtasks.exe - it is available on all versions of Windows and most tasks can be scheduled with a single line. For really complex scenarios an exported XML from a working task can be used to setup a new system.</p>
<p>Here is a case in point from: <a target="_blank" href="https://gitlab.com/missionimpossiblecode/MissionImpossibleCode/blob/master/ContinueYourAutomationAfterRestartingAHeadlessSystem.ps1">https://gitlab.com/missionimpossiblecode/MissionImpossibleCode/blob/master/ContinueYourAutomationAfterRestartingAHeadlessSystem.ps1</a></p>
<p>Using PowerShell CMDLets:</p>
<pre><code>$TaskTrigger <span class="hljs-operator">=</span> (New<span class="hljs-operator">-</span>ScheduledTaskTrigger <span class="hljs-operator">-</span>atstartup)
$TaskAction <span class="hljs-operator">=</span> New<span class="hljs-operator">-</span>ScheduledTaskAction <span class="hljs-operator">-</span>Execute Powershell.exe <span class="hljs-operator">-</span>argument <span class="hljs-string">"-ExecutionPolicy Bypass -File $scriptlocation"</span>
$TaskUserID <span class="hljs-operator">=</span> New<span class="hljs-operator">-</span>ScheduledTaskPrincipal <span class="hljs-operator">-</span>UserId System <span class="hljs-operator">-</span>RunLevel Highest <span class="hljs-operator">-</span>LogonType ServiceAccount
Register<span class="hljs-operator">-</span>ScheduledTask <span class="hljs-operator">-</span>Force <span class="hljs-operator">-</span>TaskName HeadlessRestartTask <span class="hljs-operator">-</span>Action $TaskAction <span class="hljs-operator">-</span>Principal $TaskUserID <span class="hljs-operator">-</span>Trigger $TaskTrigger
</code></pre><p>schtasks.exe is more concise to accomplish the same thing:</p>
<pre><code>schtasks.exe <span class="hljs-operator">/</span>create <span class="hljs-operator">/</span>f <span class="hljs-operator">/</span>tn HeadlessRestartTask <span class="hljs-operator">/</span>ru SYSTEM <span class="hljs-operator">/</span>sc ONSTART <span class="hljs-operator">/</span>tr <span class="hljs-string">"powershell.exe -file $scriptlocation"</span>
</code></pre><p>While I am aware there are ways to reduce the number of lines of the CMDLet version, the number of objects does not reduce which means condensing the lines makes the final result much more complex and challenging to build, understand and maintain. It also requires more refactoring when new requirements are discovered.</p>
<p>For me, this is a case where Mission Impossible coding guides me away from the preferred stance of staying within the built-in functionality of one language.</p>
<p>My perspective on scheduling tasks by the most pragmatic methods did not come from a single implementation attempt - I have repeatedly implemented using the CMDlets and repeatedly come up against it’s complexity challenges (both for initial build and code maintenance) and PowerShell version limitations - schtasks.exe simply does not have these problems.</p>
<p>The use of schtasks.exe has these super-spy benefits:</p>
<ul>
<li>the resultant code is simple and easy to understand.</li>
<li>the resultant code has excellent back reach (<em>ScheduledTask</em> CMDLets are available in PowerShell V4 and later).</li>
<li>when necessary, you can process schedule tasks XML which is readily exported from the task scheduler - so you can use the GUI task scheduler as an code builder for scheduled tasks.</li>
</ul>
<h2 id="heading-when-super-spys-make-soup-ingredients-cant-be-binary-considerations">When Super-Spys Make Soup, Ingredients Can’t Be Binary Considerations</h2>
<p>Many conversations about technical implementation details start with “Always” or “Never” and quickly cascade into log jams - whether in one mind or in a team discussion. What if you approached creating a new soup recipe with “in” or “out” logic for all the possible ingredients instead of considering how much of each should be used? I am guessing it would be a frustrating experience and the result would likely be disgusting to eat!</p>
<p>Best Practices of technology frameworks are like soup ingredients - the level of each has to be tuned together with the others and with the objectives and scope of the task at hand. When multiple frameworks are involved, the tuning approach is even more important. Like making soup, the right mix is figured out by tasting the various experiments at adjusting the amount of each ingredient.</p>
<p>So heuristics should generally carry intents like “Generally”, “When Possible”, “All Things Being Equal”, “A First Resort”, “A Last Resort”, “First Class Consideration”, etc. instead of “Always” or “Never”.</p>
<p>Heuristics also allow for keeping sight of fundamentals - you can think of this as never losing sight of what a fundamental ingredient like salt does for any soup regardless of what else is in it.</p>
<p>This approach can help reduce architectural tensions and fuel innovative solutions whether being create individuals or teams!</p>
<h2 id="heading-more-about-mission-impossible-code-philosophy-heuristics-and-examples">More About Mission Impossible Code Philosophy, Heuristics and Examples</h2>
<p><a target="_blank" href="https://MissionImpossibleCode.io/post/back-to-basics-testable-reference-pattern-manifesto-with-testable-sample-code/">Back to Basics: Testable Reference Pattern Manifesto (With Testable Sample Code)</a> - Mission Impossible Code samples are intended to be both 1) usable directly for production use and 2) a top notch pattern to use for your own innovation.</p>
<p><a target="_blank" href="https://MissionImpossibleCode.io/post/continue-your-automation-to-run-once-after-restarting-a-headless-windows-system/">Continue Your Automation To Run Once After Restarting a Headless Windows System</a></p>
<p><a target="_blank" href="https://MissionImpossibleCode.io/post/a-sufficiently-viable-implementation-svi-for-running-code-under-the-system-account-on-nano-server/">A Sufficiently Viable Implementation (SVI) for Running Code Under The System Account on Nano Server</a></p>
<p><a target="_blank" href="https://MissionImpossibleCode.io/post/a-sufficiently-viable-implementation-svi-for-running-code-under-the-system-account-on-nano-server/#requirements-and-desirements">Simplicity Manifesto Principles</a></p>
<p><a target="_blank" href="https://MissionImpossibleCode.io/post/no-7zip-allowed-extracting-oracles-gzipped-java-tarball-on-windows-to-create-an-isolated-zero-footprint-java-install-for-cis-cat-pro/#applying-infrastructure-as-code-principles-of-minimalism">Principles of Minimalism (with Code)</a></p>
]]></content:encoded></item><item><title><![CDATA[Released GitLab HA Scaling Runner Vending Machine for AWS (v1.4.0-alpha4)]]></title><description><![CDATA[For a long time I have been playing with the concept of Enablement Automation Code as a Product. The alternative is roughly “as a quick start template”. In this case, this is not actually a product - but the effort is managed with all the perspective...]]></description><link>https://missionimpossiblecode.io/released-gitlab-ha-scaling-runner-vending-machine-for-aws-v140-alpha4</link><guid isPermaLink="true">https://missionimpossiblecode.io/released-gitlab-ha-scaling-runner-vending-machine-for-aws-v140-alpha4</guid><category><![CDATA[GitLab]]></category><category><![CDATA[gitlab-runner]]></category><category><![CDATA[AWS]]></category><category><![CDATA[cloudformation]]></category><category><![CDATA[Mission Impossible Code]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Sun, 31 Jan 2021 10:43:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657975781529/uarGh6N13.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For a long time I have been playing with the concept of Enablement Automation Code <strong>as a Product</strong>. The alternative is roughly “as a quick start template”. In this case, this is not actually a product - but the effort is managed with all the perspectives as though it were one.</p>
<p>In my experience, this seemingly small shift in perspective causes a butterfly effect of positive outcomes in the work product. It can be seen in this effort with significant effort around “self-service” and “patching built-in”. In this specific case it also caused ML and AI Ops to be in view because many organizations use scaled CI compute to do model processing runs.</p>
<p>Other things Enablement Automation Code as a Product informed in this case include choosing <strong>Boring AWS Technology</strong>. In this case, sticking with the “Boring” choice of CloudFormation as the Infrastructure as Code (IaC) language enables thousands of IaC Automation Professionals to quickly implement, innovate and contribute. It may also allow this code to eventually be an <a target="_blank" href="https://aws.amazon.com/quickstart/architecture/github-enterprise/">AWS QuickStart</a> - a sort of Production-Grade IaC Templates Marketplace provided by AWS.</p>
<p>Studying competing offerings and implementing customer requirements are also hallmarks of product management. At a former employer, I managed a very similar effort specifically for GitLab Runner, where I started to experiment with the Enablement Automation Code as a Product perspective - those experiences have informed this solution.</p>
<p>Enablement Automation Code <strong>as a Product</strong> strongly relates to a previous blog post on why working examples are also the best learning aide: <a target="_blank" href="https://missionimpossiblecode.io/post/back-to-basics-testable-reference-pattern-manifesto-with-testable-sample-code/">Back to Basics: Testable Reference Pattern Manifesto (With Testable Sample Code)</a></p>
<h3 id="heading-features-checklist">Features Checklist</h3>
<p>Some features in this list are highlights of especially applicable code inherited from <a target="_blank" href="https://MissionImpossibleCode.io/post/the-ultimate-aws-autoscaling-group-asg-lab-kit/">The Ultimate AWS AutoScaling Group ASG Lab Kit</a>, but items marked “NEW” are specifically new compared to that code.</p>
<p>It is important to review the <a target="_blank" href="https://gitlab.com/guided-explorations/aws/gitlab-runner-autoscaling-aws-asg/-/blob/master/README.md">README.md</a> to understand if this code appropriate for your use case.</p>
<ul>
<li><strong>Self-Service</strong> - “Vending Machine” in the name indicates that there is a purposeful production orientation in developing this code to make it deployable by individuals who are not experts in either AWS or GitLab Runner setup. For instance, there is sufficient commentary in the code and in CloudFormation console forms to enable anyone to figure out how to deploy a runner. Finer points of figuring out a smooth autoscaling metric do require more knowledge in these areas - but standing up an HA runner of any of the types supported is fairly straight forward.</li>
<li><strong>NEW: Runner Management At Scale - Visibility</strong> - runner naming and AWS EC2 tagging are used to ensure it is always easy to know where a runner resides in scaled, multi-account AWS implementations. All runner tags are surfaced as a single comma separated EC2 Tag.</li>
<li><strong>NEW: Designed for Long Term Runner Management At Scale - Easy Patching and Updates</strong> - the “maintainability” built-in to the original Ultimate ASG AutoScaling Group Lab Kit allows runner ASGs to be easily update with all of:<ol>
<li>The latest AMI</li>
<li>the OS patches</li>
<li><strong>NEW</strong> The latest GitLab runner.</li>
</ol>
</li>
<li><strong>NEW: Spot / Ondemand Compute Type Surfaced as Runner Tags</strong> - CI/CD Automation developers can choose what type of compute is acceptable for their purposes at the per-job level of granularity.</li>
<li><strong>NEW: CloudWatch Instance Metrics Collected for Linux and Windows</strong> - mainly for Memory based scaling, but disk and network are also collected so that bottlenecks with these resources can be spotted for specialty workloads like ML Ops. They are also dimensioned on “Instance Type” for the same reason - analyzing bottlenecks by instance type in order to select the best one depending on workload.</li>
<li><strong>NEW: Scaling on Memory Utilization</strong> - workloads that have low CPU utilization, but still consume memory (for example GitLab jobs that just poll for status from another system), may be better scaled using memory utilization.</li>
<li><strong>NEW: Runner On/Off Scheduling</strong> - one stop and one start schedule (provided by ASG scheduled actions) are provided to completely shut the runner down when not being used. Use cases include: a development team specific CI runner that is used only when developers are on working hours, CD runners that are only needed during deployment events to specific environments, runners that are manually shutdown when done - but need to auto-start at a given time of day or week.</li>
<li><strong>NEW: Provides Linux “docker” Runner Executor for Primary Use Case of docker+machine Scaling Replacement</strong> - docker+machine GitLab Runner executor is deprecated because docker machine is deprecated by docker. This replaces the docker level scaling with</li>
<li><strong>NEW: Provides Windows shell Runner Executor for Primary Use Case of .NET Framework and Other Windows Development</strong> - some .NET Framework CI builds and other Windows CI builds may require a full Windows instance due to the build tooling requirements. By having a Windows shell runner that can also auto-scale, development teams with these requirements can have a scaling GitLab runner.</li>
<li><strong>NEW: Provides Linux Shell and Windows Docker GitLab Runner Executors</strong> - while less common, these are provided as well.</li>
<li><strong>NEW: Extensive Troubleshooting Information Documented</strong> - [TESTING-TROUBLESHOOTING] (<a target="_blank" href="https://gitlab.com/guided-explorations/aws/gitlab-runner-autoscaling-aws-asg/-/blob/master/TESTING-TROUBLESHOOTING.md">https://gitlab.com/guided-explorations/aws/gitlab-runner-autoscaling-aws-asg/-/blob/master/TESTING-TROUBLESHOOTING.md</a>) - also linked from <a target="_blank" href="https://gitlab.com/guided-explorations/aws/gitlab-runner-autoscaling-aws-asg/-/blob/master/README.md">README.md</a></li>
<li><strong>Designed for Extensibility and Testability</strong> - the runner configuration scripts are (a) separate from the cloud formation and (b) downloaded dynamically at scaling time. This enables getting around some code limitations of CloudFormation, but it also enables others to easily create their own runner configurations and store them anywhere. It also enables iterative testing over just the runner script portion by scaling down to 0 and then back up - the dynamic sourcing of the scripts causes updates to be taken. For stronger version pegging or “full immutable automation code” the script can have a version number embedded in the file name and be source from S3.</li>
</ul>
<h2 id="heading-code-for-this-post">Code for This Post</h2>
<p><a target="_blank" href="https://gitlab.com/guided-explorations/aws/gitlab-runner-autoscaling-aws-asg">GitLab HA Scaling Runner Vending Machine for AWS</a></p>
<h4 id="heading-mission-impossible-code-series-inclusion">Mission Impossible Code Series Inclusion</h4>
<ul>
<li>The solution sticks to the Boring Technology selection criteria.</li>
<li>The solution is implemented in a single CloudFormation template.</li>
<li>The solution implements “Least Privileges”</li>
<li>The solution implements “Least Configuration” (don’t configure things that the user indicates they won’t use, use blank parameters as an off switch for least config).</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Lightning Fast and Easy Provisioning of Git with SSH Key Authentication on Windows]]></title><description><![CDATA[Lightning Fast and Easy Provisioning of Git with SSH Key Authentication on Windows
Maybe you have a team of Windows developers that are onboarding for your new Git server installation or maybe you’ve decided to drop http password authentication to yo...]]></description><link>https://missionimpossiblecode.io/lightning-fast-and-easy-provisioning-of-git-with-ssh-key-authentication-on-windows</link><guid isPermaLink="true">https://missionimpossiblecode.io/lightning-fast-and-easy-provisioning-of-git-with-ssh-key-authentication-on-windows</guid><category><![CDATA[Windows]]></category><category><![CDATA[Git]]></category><category><![CDATA[Mission Impossible Code]]></category><category><![CDATA[GitLab]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Thu, 17 Dec 2020 07:27:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657975696428/s0JKlAROB.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-lightning-fast-and-easy-provisioning-of-git-with-ssh-key-authentication-on-windows">Lightning Fast and Easy Provisioning of Git with SSH Key Authentication on Windows</h2>
<p>Maybe you have a team of Windows developers that are onboarding for your new Git server installation or maybe you’ve decided to drop http password authentication to your existing Git server (due to it’s many problems). Your next steps may well be into a rough and rocky rabbit hole when you were holding out hope for simplicity (you know the kind you’ve fallen into before if you’ve been in tech for more than about 45 minutes).</p>
<p>The guides on the internet for getting Windows setup for SSH authentication for Git are unnecessarily complex.</p>
<p>My inner tool smith really loathes when the very first steps into something new are fraught with rocky rabbit holes - so I took on the challenge of creating an easier way.</p>
<p>The resultant tool is a 20 line PowerShell script that deploys Git, configures SSH and leaves the public key on your clipboard so you can paste it into GitLab or any other Git collaborative webserver. There is also an optional connectivity test.</p>
<h3 id="heading-reasons-for-moving-to-ssh">Reasons For Moving to SSH</h3>
<p>There are multiple reasons you may want to move your Windows developers to SSH authentication for Git:</p>
<ol>
<li><p>You want to get away from git storing local passwords - whether in the git config or in Windows Credentials (with the windows credential helper) because it is pure pain to walk people through how to find and update this password when they change it on the Git server.</p>
</li>
<li><p>You want to avoid both http passwords and the http protocol for git.</p>
</li>
</ol>
<h3 id="heading-conventional-wisdom-on-ssh-configuration">Conventional Wisdom on SSH Configuration</h3>
<p>The conventional wisdom solution offers many steps that are roughly:</p>
<ol>
<li><p>Installing git manually.</p>
</li>
<li><p>Installing the well known Windows SSH client Putty.</p>
</li>
<li><p>Installing Putty’s key generator.</p>
</li>
<li><p>Converting the non-compatible putty generated key into an ssh compatible one.</p>
</li>
<li><p>Precisely placing the SSH key on disk.</p>
</li>
<li><p>Precisely permissioning the SSH key and it’s parent folder (ssh is purposely fussy about this in order to keep the key secure).</p>
</li>
</ol>
<blockquote>
<p>Most of this can be avoided by simply using the full SSH client that is embedded inside of the Windows git client install.</p>
</blockquote>
<h3 id="heading-the-cleanest-way-with-working-automation-code">The Cleanest Way (With Working Automation Code)</h3>
<p>Besides the above pure pain, here are the additional things solved for in this code:</p>
<ol>
<li><p>Automatically installs Git - but only if necessary (idempotent)</p>
</li>
<li><p>Automatically installs chocolatey to install Git - but only if necessary (idempotent)</p>
</li>
<li><p>Automatically generates an SSH key - but only if necessary (idempotent) (which avoids killing a key that might be in use)</p>
</li>
<li><p>Uses the Git’s built-in SSH client to create SSH keys (avoids the complexity of the above conventional wisdom)</p>
</li>
<li><p>Copies the public key to the clip board and pauses for the user to add it to the Git server (in their profile)</p>
</li>
<li><p>Optionally does a SSH login test if you provide a value for: $SSHEndPointToGitForTesting</p>
</li>
</ol>
<h3 id="heading-solution-details">Solution Details</h3>
<p>This code can be run directly from GitLab with this command:</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">Invoke-Expression</span> <span class="hljs-literal">-command</span> <span class="hljs-string">"Invoke-WebRequest -uri 'https://gitlab.com/missionimpossiblecode/MissionImpossibleCode/-/raw/master/install-gitwithssh.ps1' -UseBasicParsing -OutFile ./install-gitwithssh.ps1"</span> ; . ./<span class="hljs-built_in">install-gitwithssh</span>.ps1
</code></pre>
<p>If you want to download dynamically, but also want the test and instructions to work, then set these environment variables before calling the above:</p>
<pre><code class="lang-powershell"><span class="hljs-variable">$env:YourGitServerhttpURL</span>=<span class="hljs-string">"https://gitlab.com"</span> <span class="hljs-variable">$env:GitSSHUserAndEndPointForTesting</span>=<span class="hljs-string">"git@gitlab.com"</span> 
<span class="hljs-comment">#some Git servers might want the windows userid "git", which is specified as $env:username</span>
</code></pre>
<p>You can also simply copy the code, hardcode the two variables and distribute it in your organization.</p>
<h3 id="heading-main-code">Main Code</h3>
<pre><code class="lang-powershell"><span class="hljs-comment"># Set environment variables before calling in order to test </span>
<span class="hljs-keyword">If</span> ((<span class="hljs-built_in">Test-Path</span> env:YourGitServerhttpURL) <span class="hljs-operator">-and</span> (!(<span class="hljs-built_in">Test-Path</span> variable:YourGitServerhttpURL))) {<span class="hljs-variable">$YourGitServerhttpURL</span>=<span class="hljs-string">"<span class="hljs-variable">$env:YourGitServerhttpURL</span>"</span>} <span class="hljs-keyword">If</span> ((<span class="hljs-built_in">Test-Path</span> env:GitSSHUserAndEndPointForTesting) <span class="hljs-operator">-and</span> (!(<span class="hljs-built_in">Test-Path</span> variable:GitSSHUserAndEndPointForTesting))) {<span class="hljs-variable">$GitSSHUserAndEndPointForTesting</span>=<span class="hljs-string">"<span class="hljs-variable">$env:GitSSHUserAndEndPointForTesting</span>"</span>} 

<span class="hljs-comment"># $YourGitServerhttpURL="https://gitlab.com" </span>
<span class="hljs-comment"># $GitSSHUserAndEndPointForTesting="$env:username@gitlab.com" #Optional to trigger testing Use "git@gitlab.com" for GitLab.</span>

<span class="hljs-keyword">If</span> (!(<span class="hljs-built_in">Test-Path</span> <span class="hljs-string">'C:\Program Files\git\usr\bin\ssh-keygen.exe'</span>)) { <span class="hljs-built_in">Write-Host</span> <span class="hljs-string">'Installing latest git client using Chocolatey'</span> <span class="hljs-keyword">If</span> (!(<span class="hljs-built_in">Test-Path</span> env:chocolateyinstall)) { <span class="hljs-built_in">Write-Host</span> <span class="hljs-string">"Chocolatey is not present, installing on demand."</span> <span class="hljs-built_in">iwr</span> https://chocolatey.org/install.ps1 <span class="hljs-literal">-UseBasicParsing</span> | <span class="hljs-built_in">iex</span> } cinst <span class="hljs-literal">-y</span> git } 
<span class="hljs-keyword">If</span> (!(<span class="hljs-built_in">Test-Path</span> <span class="hljs-variable">$env:userprofile</span>.ssh\id_rsa.pub)) { <span class="hljs-built_in">Write-Host</span> <span class="hljs-string">'No default ssh key present in $env:userprofile.ssh, generating a new one.'</span> <span class="hljs-built_in">Write-Warning</span> <span class="hljs-string">'Press enter for default file name and twice for password to set it to not have a password'</span> &amp; <span class="hljs-string">'C:\Program Files\git\usr\bin\ssh-keygen.exe'</span> } <span class="hljs-built_in">get-content</span> <span class="hljs-variable">$env:userprofile</span>.ssh\id_rsa.pub | clip <span class="hljs-built_in">write-host</span> <span class="hljs-string">"Your public ssh key is now on your clipboard, ready to be pasted into your git server at <span class="hljs-variable">$YourGitServerhttpURL</span>"</span>

<span class="hljs-keyword">If</span> (<span class="hljs-built_in">Test-Path</span> variable:GitSSHUserAndEndPointForTesting) { <span class="hljs-built_in">Write-Host</span> <span class="hljs-string">'NOTE: Sometimes it takes a while for your Git server to propagate your key so it is available for authentication after first adding it!'</span> <span class="hljs-built_in">Write-Host</span> <span class="hljs-string">'After you have setup the key, to test the connection, press any key to continue...'</span>; <span class="hljs-variable">$null</span> = <span class="hljs-variable">$Host</span>.UI.RawUI.ReadKey(<span class="hljs-string">'NoEcho,IncludeKeyDown'</span>); 

<span class="hljs-comment">#Use git's open ssh: </span>
<span class="hljs-built_in">Write-Host</span> <span class="hljs-string">"...Testing ssh login as <span class="hljs-variable">$</span>{GitSSHUserAndEndPointForTesting} using key <span class="hljs-variable">$env:userprofile</span>.ssh\id_rsa on port 22"</span> <span class="hljs-variable">$env:term</span> = <span class="hljs-string">'xterm256colors'</span> <span class="hljs-built_in">push-location</span> <span class="hljs-string">'c:\program files\git\usr\bin'</span> .\ssh.exe <span class="hljs-string">"<span class="hljs-variable">$</span>{GitSSHUserAndEndPointForTesting}"</span> <span class="hljs-literal">-i</span> <span class="hljs-variable">$env:userprofile</span>.ssh\id_rsa <span class="hljs-literal">-p</span> <span class="hljs-number">22</span> <span class="hljs-built_in">pop-location</span> <span class="hljs-built_in">Write-Host</span> <span class="hljs-string">'After observing the test result above (note it may take time for your new key to propagate at the server), press any key to continue...'</span>; <span class="hljs-variable">$null</span> = <span class="hljs-variable">$Host</span>.UI.RawUI.ReadKey(<span class="hljs-string">'NoEcho,IncludeKeyDown'</span>); }
</code></pre>
<h2 id="heading-code-for-this-article">Code For This Article</h2>
<p><a target="_blank" href="https://gitlab.com/missionimpossiblecode/MissionImpossibleCode/-/blob/master/install-gitwithssh.ps1">install-gitwithssh.ps1</a></p>
<h4 id="heading-mission-impossible-code-series-inclusion">Mission Impossible Code Series Inclusion</h4>
<ul>
<li><p>The solution is very concise.</p>
</li>
<li><p>The solution is idempotent (only takes steps necessary when things are missing).</p>
</li>
<li><p>The solution solves multiple challenging issues in the simplest possible way.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Live Coding Conversion of GitHub Action Super-Linter to GitLab CI]]></title><description><![CDATA[In the most recent Mission Impossible Live Coding event, Jefferson and I convert the GitHub Action Super-Linter to run in GitLab CI. This is a summary of lessons learned and pointers to the results.
So Jefferson and I worked through this approach. Be...]]></description><link>https://missionimpossiblecode.io/live-coding-conversion-of-github-action-super-linter-to-gitlab-ci</link><guid isPermaLink="true">https://missionimpossiblecode.io/live-coding-conversion-of-github-action-super-linter-to-gitlab-ci</guid><category><![CDATA[Mission Impossible Code]]></category><category><![CDATA[Powershell]]></category><category><![CDATA[GitLab]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[GitLab-CI]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Sat, 20 Jun 2020 08:52:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657975583643/DDBCIQU0r.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the most recent Mission Impossible Live Coding event, Jefferson and I convert the GitHub Action Super-Linter to run in GitLab CI. This is a summary of lessons learned and pointers to the results.</p>
<p>So Jefferson and I worked through this approach. Before the session I played with some ideas and approaches, but ultimately we ended up with a new approach.</p>
<h3 id="heading-lessons-learned">Lessons Learned:</h3>
<ul>
<li>GitLab has linting as a part of it’s built-in Code Quality scanning:<ul>
<li>But it can be a challenge to figure out that this is where GitLab CI does linting - you have to dig into the website of the underlying software “Code Climate” to figure out that this is being done and for what languages.</li>
<li>GitLab’s built-in support is configurable, including adding your own Code Quality scanning jobs with additional products.</li>
</ul>
</li>
<li>Super-Linter has a lot of language coverage.</li>
<li>Super-Linter is implemented in a container - this means implementation on GitLab CI should be even easier - especially if the image built for it is publicly available. If the image was not publicly available, we would have built the docker file on GitLab as a prerequiste to using it in GitLab CI.</li>
<li>Super-Linter is driven by a lot of variables - very compatible with GitLab CI.</li>
<li>Super-Linter has a special parameter, RUN_LOCAL, that overrides a bunch of expected GitHub Actions input parameters - so we don’t need to emulate or map those expected parameters. Perhaps many GitHub Actions can be repurposed easily using this parameter.</li>
<li>Super-Linter only outputs results to the log - not as an artifact file. Hopefully as it matures it will be able to emit test results. If it could emit JUNIT.xml we could have loaded the results into GitLab’s test results visualization panels. If we could transform the results into GitLab Code Quality result format, results could be even more deeply integrated inline into Merge Requests.</li>
</ul>
<h3 id="heading-techniques-demonstrated">Techniques Demonstrated:</h3>
<ul>
<li>Examing the source code and documentation for clues on how to implement Super-Linter outside of GitHub.]</li>
<li>Iteration - Let GitLab CI “give it a shot” while we research other things.</li>
<li>Moving variable declarations around to ensure they are encountered early enough (a final break through by Jefferson was to move a key variable declaration from the script into GitLab’s variables: block)</li>
<li><strong>Make Into a Plug-in Extension</strong> by:<ul>
<li>Reimplementing the live code in a way that enables it to be simply included in other CI.</li>
<li>Provide an example of calling the plug-in.</li>
<li>Demonstrate how to version peg the plug-in using GitLab’s <code>include:ref:</code> to point to a specific git tag of the plugin.</li>
</ul>
</li>
</ul>
<h3 id="heading-the-code">The Code</h3>
<ul>
<li><a target="_blank" href="https://gitlab.com/guided-explorations/ci-cd-plugin-extensions/ci-cd-plugin-extension-github-action-super-linter/-/tree/63dc8990f2453312e41caa7d6358bc63d2057dd8">GitLab CI CD Plugin Extension GitHub Action Super-Linter - as completed in live coding</a></li>
<li><a target="_blank" href="https://gitlab.com/guided-explorations/ci-cd-plugin-extensions/ci-cd-plugin-extension-github-action-super-linter/">GitLab CI CD Plugin Extension GitHub Action Super-Linter - as productionized</a></li>
<li><a target="_blank" href="https://gitlab.com/guided-explorations/ci-cd-plugin-extensions/calling-extensions-examples/calling-super-linter">Example of Calling the Super-Linter GitLab Extension</a></li>
</ul>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html">GitLab Built-in Code Quality Scanning</a></li>
<li>Which uses CodeClimate, here are the <a target="_blank" href="https://docs.codeclimate.com/docs/list-of-engines">Supported Scanning Engines, Including Linting</a></li>
<li><a target="_blank" href="https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html#implementing-a-custom-tool">Implementing a Custom Code Quality Tool in GitLab</a></li>
</ul>
<h3 id="heading-mission-impossible-live-coding">Mission Impossible Live Coding</h3>
<p>The past recordings, including the above are at: <a target="_blank" href="https://www.youtube.com/channel/UCcL7kaon80MiwuGcNI7A1Dg">https://www.youtube.com/channel/UCcL7kaon80MiwuGcNI7A1Dg</a></p>
<p>We are live coding every two weeks on Friday at 2pm ET - the next one will be on July 3rd.</p>
<p>You can <a target="_blank" href="https://missionimpossiblecode.io/files/Mission_Impossible_LIVE_Coding.ics">add it to your calendar with this .ICS file</a>.</p>
<p>The live stream will is at: <a target="_blank" href="https://www.twitch.tv/missionimpossiblecode">https://www.twitch.tv/missionimpossiblecode</a> and <a target="_blank" href="https://www.youtube.com/channel/UCcL7kaon80MiwuGcNI7A1Dg">https://www.youtube.com/channel/UCcL7kaon80MiwuGcNI7A1Dg</a>.</p>
<p>We will be using GitLab issues to queue up questions and enable you to submit code snippets for consideration. Please use this GitLab project for that purpose: <a target="_blank" href="https://gitlab.com/missionimpossiblecode/live/-/issues">https://gitlab.com/missionimpossiblecode/live/-/issues</a></p>
]]></content:encoded></item><item><title><![CDATA[Mission Impossible Code - Compact, Idempotent, DevOps Oriented, Multi-Distro Package Installer Script for Linux and Mac]]></title><description><![CDATA[Everyone loves Linux for its ability to stick to fundamentals and common platform expectations. Except those of us who do a lot of deployment automation.
Why? Two main reasons: In a world of stripped back distros (think containers), even fundamental ...]]></description><link>https://missionimpossiblecode.io/mission-impossible-code-compact-idempotent-devops-oriented-multi-distro-package-installer-script-for-linux-and-mac</link><guid isPermaLink="true">https://missionimpossiblecode.io/mission-impossible-code-compact-idempotent-devops-oriented-multi-distro-package-installer-script-for-linux-and-mac</guid><category><![CDATA[Mission Impossible Code]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Working Code Example]]></category><category><![CDATA[code]]></category><category><![CDATA[Infrastructure as code]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Sat, 23 May 2020 11:24:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657975438537/ouwL4bkJd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Everyone loves Linux for its ability to stick to fundamentals and common platform expectations. Except those of us who do a lot of deployment automation.</p>
<p>Why? Two main reasons: In a world of stripped back distros (think containers), even fundamental Gnu coreutils and other basics can be missing. Which leads to the second frustration - for some reason distro families thought it would be great to not only innovate package management technology but also change up the command set and also not provide a universal command set (like an api) to hide the differences.</p>
<p>So after bashing my head on this a million times, a bit of bash code eventually emerged (yeah from my head). And as you’ll read, it was not a process of random chance and natural selection - but rather it’s opposite - hyper-engineering.</p>
<p>As per Mission Impossible Code Principles I’ve tried to make it “as simple as possible, but still have a very broad scope of reuse”</p>
<blockquote>
<p>FYI - Windows isn’t any better - while Windows PackageManagement (aka OneGet) and Chocolatey both tried to consolidate down to one command set for all package types for the platform, we now have a third one.</p>
</blockquote>
<h2 id="heading-tldr">TL;DR</h2>
<p>I used to take time to expand the section “Architecture Heuristics” and discuss the enablement created by each of the bullets in the section.</p>
<p>I won’t be doing that any longer for these reasons:</p>
<ol>
<li>I have added “Benefits” and “Coding Decisions” to each of the below to provide a synopsis of how the item benefits the solution and what coding choices it affected - which provides a better information map for faster learning.</li>
<li>By providing this learning section as mapped bullets - you can incrementally learn a few points by scanning and deep diving what interests you without feeling obligated to plow through paragraphs of prose.</li>
<li>It’s quicker and easier for me to document without having to generate formal copy - I’ll be more tempted to share when the authoring process is lighter.</li>
</ol>
<h2 id="heading-architecture-heuristics-requirements-constraints-desirements-serendipities-applicability-limitations-and-alternatives">Architecture Heuristics: Requirements, Constraints, Desirements, Serendipities, Applicability, Limitations and Alternatives</h2>
<blockquote>
<p>This section appears in many Mission Impossible Code samples because it: 1) Demonstrates there is an actual pattern to the thinking underlying this approach, 2) It allows you to learn the patterns but making the thinking plain, 3) It shows the complex engineering of creating simplicity.</p>
</blockquote>
<p>The following list demonstrates the Architectural thrust of the solution. This approach is intended to be pure to simplicity of operation and maintenance, rather than purity of a language or framework or development methodology. It is also intended to have the least possible dependencies. It’s an approach I call “Mission Impossible Coding” because it enables the code to get it’s job done no matter what.</p>
<ul>
<li><strong>Requirement: (Satisfied)</strong> Adhere to “Adoption Driven Development for Tooling”<ul>
<li><strong>Benefits:</strong> everyone will use more of your stuff, more people will truly ’love’ your stuff, driving implementation decision toward “human cognitive” priorities over machine considerations - because we all have much less time than computing resources</li>
<li><strong>Coding Decisions:</strong> Adhere to Mission Impossible Techniques (aka the entire Architecture Heuristics section)</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Don’t rely on errors to do detections (passive detection)<ul>
<li><strong>Benefits:</strong> creating automation errors when formal exception checking needs to be supported</li>
<li><strong>Coding Decisions:</strong> picking “command -v” to detect existence of a command</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Don’t require prerequisites to work<ul>
<li><strong>Benefits:</strong> avoid chicken and the egg problem, works on minimalized containers images</li>
<li><strong>Coding Decisions:</strong> picking “command -v” to detect existence of a command</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Be compact.<ul>
<li><strong>Benefits:</strong> Enables “single script” implementations as the code is not overly obnoxious at the top of a script. Many orchestration systems can send a single script to an endpoint, but sending files makes their scenario much more complex - compact code can travel inside the single script.</li>
<li><strong>Coding Decisions:</strong> IFS + read method of parsing key value pairs, some single line if statements</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Ensure parameters can be passed in from an unknown number of parent levels and unknown technologies or computing languages<ul>
<li><strong>Benefits:</strong> very broad reuse with no adaptations for many levels of enclosing orchestration.</li>
<li><strong>Coding Decisions:</strong> Work with a 1) list that is 2) constructed as a string (so that it can be passed between all types of automation technology).</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Handle at least yum and apt-get.</li>
<li><strong>Requirement: (Satisfied)</strong> Be idempotent by checking for the existence of a command before attempting installation operations.<ul>
<li><strong>Benefits:</strong> all the benefits of idempotency which are too many to list here.</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Work for scenarios where the name of the package is different than the name of the command that is needed.<ul>
<li><strong>Benefits:</strong> broader reuse due to handling this frequent use case</li>
<li><strong>Coding Decisions:</strong> use key value pairs for installing packages to get specific commands</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Auto-detect the package manager.<ul>
<li><strong>Benefits:</strong> broader reuse</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Handle the AWFUL problem of brew taps and casks (who built that? - can someone spend a day and abstract away this useless distinction right in the brew code like I’ve done here :) - yeah and disallow a package identifier to be both while you’re at it ;) )<ul>
<li><strong>Benefits:</strong> broader reuse, declarative state approach (just “make it so”)</li>
</ul>
</li>
</ul>
<h2 id="heading-the-code">The Code</h2>
<p>New versions of the code are not synced to the below article insert, please use the repository link below to get the latest.</p>
<pre><code><span class="hljs-keyword">function</span> ifcmdmissing-<span class="hljs-function"><span class="hljs-title">instpkg</span></span> () {
<span class="hljs-comment">#If you don't need brew, this can be much more compact by removing the relevant code</span>
<span class="hljs-comment">#If command is not on path, installs the package</span>
<span class="hljs-comment">#detects package managers: apt, yum and brew</span>
  <span class="hljs-keyword">if</span> [[ -n <span class="hljs-string">"<span class="hljs-subst">$(command -v brew)</span>"</span> ]] ; <span class="hljs-keyword">then</span>
    <span class="hljs-comment">#Detect package manager, brew first because some macOSes have an apt-get stub</span>
    PKGMGR=<span class="hljs-string">'brew'</span>
  <span class="hljs-keyword">elif</span> [[ -n <span class="hljs-string">"<span class="hljs-subst">$(command -v yum)</span>"</span> ]] ; <span class="hljs-keyword">then</span>
    PKGMGR=<span class="hljs-string">'yum'</span>
  <span class="hljs-keyword">elif</span> [[ -n <span class="hljs-string">"<span class="hljs-subst">$(command -v apt-get)</span>"</span> ]] ; <span class="hljs-keyword">then</span>
    PKGMGR=<span class="hljs-string">'apt-get'</span>
  <span class="hljs-keyword">fi</span>
  <span class="hljs-keyword">for</span> cmdpkg <span class="hljs-keyword">in</span> <span class="hljs-variable">$1</span> ; <span class="hljs-keyword">do</span>
      IFS=<span class="hljs-string">':'</span> <span class="hljs-built_in">read</span> -ra CMDPKGPAIR &lt;&lt;&lt;<span class="hljs-string">"<span class="hljs-variable">${cmdpkg}</span>"</span>
      CMDNAME=<span class="hljs-variable">${CMDPKGPAIR[0]}</span>
      PKGNAME=<span class="hljs-variable">${CMDPKGPAIR[1]}</span>
      <span class="hljs-built_in">echo</span> <span class="hljs-string">"If command <span class="hljs-variable">${CMDNAME}</span> is not on path, the package <span class="hljs-variable">${PKGNAME}</span> will be installed."</span>
      <span class="hljs-keyword">if</span> [[ -n <span class="hljs-string">"<span class="hljs-subst">$(command -v ${CMDNAME})</span>"</span> ]]; <span class="hljs-keyword">then</span>
          <span class="hljs-built_in">echo</span> <span class="hljs-string">"  '<span class="hljs-variable">${CMDNAME}</span>' command already present"</span>
      <span class="hljs-keyword">else</span>
          <span class="hljs-built_in">echo</span> <span class="hljs-string">"  Missing command '<span class="hljs-variable">${CMDNAME}</span>'"</span>
          <span class="hljs-built_in">echo</span> <span class="hljs-string">"  Installing package '<span class="hljs-variable">${PKGNAME}</span>' to resolve missing command."</span>
          <span class="hljs-keyword">if</span> [[ <span class="hljs-variable">$PKGMGR</span> != <span class="hljs-string">'brew'</span> ]]; <span class="hljs-keyword">then</span>
            <span class="hljs-keyword">if</span> [[ <span class="hljs-variable">$PKGMGR</span> == <span class="hljs-string">'apt-get'</span> ]]; <span class="hljs-keyword">then</span> <span class="hljs-variable">$PKGMGR</span> update; <span class="hljs-keyword">fi</span>;
            <span class="hljs-variable">$PKGMGR</span> install -y <span class="hljs-variable">${PKGNAME}</span>
          <span class="hljs-keyword">else</span>
            <span class="hljs-comment">#automatically abstract the difference between brew taps and casks</span>
            <span class="hljs-keyword">if</span> brew info <span class="hljs-variable">${PKGNAME}</span> &gt;/dev/null 2&gt;&amp;1; <span class="hljs-keyword">then</span>
              brew install <span class="hljs-variable">${PKGNAME}</span> --force
            <span class="hljs-keyword">elif</span> brew cask info <span class="hljs-variable">${PKGNAME}</span> &gt;/dev/null 2&gt;&amp;1; <span class="hljs-keyword">then</span>
              brew cask install <span class="hljs-variable">${PKGNAME}</span> --force
            <span class="hljs-keyword">else</span>
              <span class="hljs-built_in">echo</span> <span class="hljs-string">"  '<span class="hljs-variable">${PKGNAME}</span>' not found as a formulae or cask"</span>
            <span class="hljs-keyword">fi</span>
          <span class="hljs-keyword">fi</span>
      <span class="hljs-keyword">fi</span>
  <span class="hljs-keyword">done</span>
}

ifcmdmissing-instpkg <span class="hljs-string">"jq:jq wget:wget curl:curl"</span>
</code></pre><h2 id="heading-tested-on">Tested On</h2>
<ul>
<li>MacOS</li>
<li>Ubuntu container</li>
<li>Amazon Linux 2 container</li>
</ul>
<h2 id="heading-source-code-for-this-article">Source Code For This Article</h2>
<p><a target="_blank" href="https://gitlab.com/missionimpossiblecode/MissionImpossibleCode/-/blob/master/ifcmdmissing-instpkg.sh">ifcmdmissing-instpkg.sh</a></p>
<h3 id="heading-appendix-mission-impossible-pattern-philosophy">Appendix: Mission Impossible Pattern Philosophy</h3>
<p>Mission Impossible Code samples are intended to be both 1) usable directly for production use and 2) a top notch pattern to use for your own innovation. More details on this approach are in the post</p>
<h3 id="heading-appendix-constructed-simplicity-conciseness-is-the-tip-of-an-iceberg">Appendix: Constructed Simplicity (Conciseness) is The Tip of An Iceberg</h3>
<p>While epitomized by the Blaise Pascal quote “I have made this longer than usual because I have not had the time to make it shorter.”, I find that the process of creating concise, simple observations and solutions is complicated and sometimes complex.</p>
<p>Creating concise designs and solutions is a deep passion of mine. However, there is a frustration that is quick on the heals of creating something that is concise. In my line of work, at some point, you usually have to justify your final work. That is the point at which the rest of the iceberg of thinking that backs the concise tip comes to the fore. Frequently the reaction is that it can’t possibly be that involved or that you are spinning up reasons on the fly to simply bolster an idea or solution that was arrived at haphazardly.</p>
<p>Why bother addressing this sentiment? Because concise, mission impossible style, solutions can easily be criticized as lacking sophistication in their implementation. But much like a Mark Twain quote - that ruddy external appearance belies a hard wrought balance of many interrelated tradeoffs to get to a simple solution.</p>
<p>Said another way, solutions that are earnestly designed for conciseness can be rewarded by a perception of being the opposite of what they are - simplistic, backed with little or no thought.</p>
<p>This Mission Impossible Coding series exposes the submerged methodological icebergs below the waterline of the visible and concise solutions it attempts to arrive at.</p>
]]></content:encoded></item><item><title><![CDATA[UPDATED: The Ultimate AWS AutoScaling Group ASG Kickstart and Lab Kit]]></title><description><![CDATA[While noodling uses for the Ultimate AWS ASG Kickstart and Lab Kit I realized it could use a couple new features and improvements.
The first is to have instances self-tag themselves as to whether they are a spot or on-demand instance. When supporting...]]></description><link>https://missionimpossiblecode.io/updated-the-ultimate-aws-autoscaling-group-asg-kickstart-and-lab-kit</link><guid isPermaLink="true">https://missionimpossiblecode.io/updated-the-ultimate-aws-autoscaling-group-asg-kickstart-and-lab-kit</guid><category><![CDATA[cloudformation]]></category><category><![CDATA[Infrastructure as code]]></category><category><![CDATA[Mission Impossible Code]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Sat, 18 Apr 2020 08:31:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657975310374/STu482DXm.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While noodling uses for the Ultimate AWS ASG Kickstart and Lab Kit I realized it could use a couple new features and improvements.</p>
<p>The first is to have instances self-tag themselves as to whether they are a spot or on-demand instance. When supporting a mixed instances policy, the implementation requires a little more thought.</p>
<p>A second is the ever common need for a bucket that the ASG instances have access to - whether for AWS SSM results collection or inventory, for deployment artifacts to update instances, access to data or many other uses.</p>
<p>While a relatively small change, the title has also been updated to “The Ultimate AWS ASG Kickstart and Lab Kit” to indicate it is appropriate for experimentation and also as the foundation for a deployable configuration.</p>
<p>I was able unable to avoid the temptation to sneak in a couple other improvements as well.</p>
<h3 id="heading-tldr-feature-summary">TL;DR Feature Summary</h3>
<p>Skip learning new ideas and go straight to the <a target="_blank" href="https://gitlab.com/DarwinJS/ultimate-aws-asg-lab-kit/-/blob/master/CHANGELOG.md#130-2020-04-17">CHANGELOG.md</a> If you missed the original article it is here: <a target="_blank" href="https://MissionImpossibleCode.io/post/the-ultimate-aws-autoscaling-group-asg-lab-kit/">The Ultimate AWS AutoScaling Group ASG Lab Kit</a></p>
<h3 id="heading-new-features-checklist">New Features Checklist</h3>
<ul>
<li><strong>Instances tag themselves as spot or on-demand.</strong></li>
<li><strong>S3 Bucket Setup</strong></li>
<li><strong>Managed Permissions Instead of In-line</strong></li>
<li><strong>Optionally provide a keypair name</strong> for remote access.</li>
<li><strong>Use Cloudformation “Rules:”</strong> for cross parameter validation.</li>
</ul>
<h3 id="heading-features-in-detail-ideas">Features in Detail (Ideas)</h3>
<h4 id="heading-instances-tag-themselves-as-spot-or-on-demand">Instances Tag Themselves as Spot or On-Demand:</h4>
<p>One of the downstream uses I intend for this Kit is to build a GitLab CI Runner on it and I realized that ephemeral compute is good for some CI workloads which are more interruptible - like mass automated testing - but not good for others where the impact of interruption might be much more significant - like long running deployment processes. By allowing instances to self-identify and be selectable by CI engineers, they can make their own decisions about what CI workloads to run on ephemeral compute.</p>
<p>The resultant Instance tag is either COMPUTETYPE=SPOT or COMPUTETYPE=ONDEMAND. The variable $COMPUTETYPE is available throughout the userdata script including if you are using the template to pull in your own userdata code. By leveraging this variable you can surface this data to other systems you may be installing on the instance. For instance, I will be adding a GitLab CI Runner tag with this data.</p>
<p>An interesting detail here was that attempting to tag by propagating from the ASG LaunchTemplate won’t work for a mixed instances policy - when the LaunchTemplate might launch a mix of spot and on-demand, it is important that the instance self-detect and self-tag.</p>
<p>Many internet searches directed me to use the aws call “describe-spot-instance-requests”, however, in the spirit of Least Privilege and Least Config - I dug a little deeper to find that it could be done purely with “describe-instances”. Since this template already required describe instance permissions to handle ASG Lifecycle hooks, using “describe-instances” meant that I could use familiar code and not have to add permissions</p>
<h4 id="heading-s3-bucket-setup">S3 Bucket Setup</h4>
<p>As with many other resources this template can either an auto-create a bucket for you, or you can override bucket name with one that exists. In either case, Permissions are applied to the instance profile.</p>
<h4 id="heading-managed-permissions-instead-of-inline">Managed Permissions Instead of Inline</h4>
<p>Previously the template created permission using inline IAM Policies attached to the created instance role. This meant they could not easily be attached to existing roles. By creating IAM Managed Policies, they are easily attached to any existing role. They are also created even if you do not have the template auto-create the IAM Instance Profile Role.</p>
<h4 id="heading-use-cloudformation-rules-for-cross-parameter-validation">Use CloudFormation “Rules” for cross parameter validation</h4>
<p>It is a common mistake (at least for me) to choose “Windows” as the platform, but then forget to update the SSM parameter path for image lookup to point to a Windows image. Unfortunately you don’t discover this until after the machine starts. Now the template prevents execution when your parameters are in this state. It also serves as a working example of CloudFormation rules, which hard to come by - especially in YAML.</p>
<h2 id="heading-code-for-this-post">Code for This Post</h2>
<p><a target="_blank" href="https://gitlab.com/DarwinJS/ultimate-aws-asg-lab-kit/-/blob/master/CloudFormationUltimateAWSASGLabKit.cf.yml">CloudFormationUltimateAWSASGLabKit.yaml</a></p>
<p><a target="_blank" href="https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?templateURL=https://s3.amazonaws.com/missionimpossiblecode.io/files/CloudFormationUltimateAWSASGLabKit.cf.yml">Create Now in CloudFormation Console</a></p>
]]></content:encoded></item><item><title><![CDATA[NEW Oneliner to Tail the Windows Eventlog]]></title><description><![CDATA[Since switching focus to the cloud I am doing more and more pure CLI admin of Windows. One of the pains of windows admin from a console is accessing the windows eventlog. Since they are not simple text files like Linux, special PowerShell CMDLets mus...]]></description><link>https://missionimpossiblecode.io/new-oneliner-to-tail-the-windows-eventlog</link><guid isPermaLink="true">https://missionimpossiblecode.io/new-oneliner-to-tail-the-windows-eventlog</guid><category><![CDATA[Powershell]]></category><category><![CDATA[code]]></category><category><![CDATA[Deep Dive]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Mon, 30 Mar 2020 07:52:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657975190713/MH1mCAUAJ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Since switching focus to the cloud I am doing more and more pure CLI admin of Windows. One of the pains of windows admin from a console is accessing the windows eventlog. Since they are not simple text files like Linux, special PowerShell CMDLets must be used to retrieve them.</p>
<p>Due to the frequency of needing to do it, one of the biggest challenges is tailing an eventlog while waiting for results.</p>
<p>When following a text log, I simply use <code>Get-Content logfilename -wait</code> to emulate the Linux command <code>tail -f logfilename</code></p>
<p>So I went in search of what I thought would be a quick find, but all my finds were all way to long and involved - so I made a new oneliner that follows the principles of <strong>Mission Impossible Coding</strong>.</p>
<p>Managing Windows using only the PowerShell Console affects a growing list of Windows deployment scenarios:</p>
<ul>
<li>Server Core - no GUI available.</li>
<li>Windows Containers - no GUI available.</li>
<li>Using a Cloud Shell like AWS SSM Session Manager, Azure Cloud Shell or Google Cloud Shell.</li>
<li>PowerShell Remoting.</li>
<li>VS Code Remote Development.</li>
</ul>
<p>In all of these situations, tailing various Windows Eventlogs is an essential capability for development debugging and operations troubleshooting.</p>
<p>Most of the existing approaches use message indexes - which have to be retrieved by calling an API - and then tracks the last retrieved index. This means no oneliners - lots of multiline functions and full blown CMDLets.</p>
<p>I realized that for my purposes really old log lines were not of interest - even if they were the last 5 to be received - it was more about the most recent ones within a timeframe I was interested in.</p>
<p>So I noodled whether I could use time, rather than the message index of the specific log.</p>
<p>It turns out that you can use time - and rather than looking back a specific number of index entries when first loading, I look back a certain number of minutes.</p>
<p>This vastly condense the code to the point of being reasonable oneliner.</p>
<p>Why do I care about a oneliner? I had been testing the termination lifecycle hook The Ultimate AWS ASG Lab Kit - and each time I perform a test it is on an ephemeral instance that just booted and I actually issue a termination command to see the code working. So any amount of fussing installing modules or using vast tracts of code for simple functions is painful.</p>
<p>I’m going to assume that many of you working on CLI only Windows (whether through a remote or cloud shell or whether using containers) can also appreciate the power of a oneliner in these situations.</p>
<p>The code is below, but a quick walk through is:</p>
<ol>
<li>Define it as a function since it is few extra characters and I can use it again (though I have provided a listing without the function below).</li>
<li>By default, look 5 minutes into the past (set $lastdate).</li>
<li>Setup a loop that goes until it gets a CTRL-C</li>
<li>Set $newtime (can’t dynamically use Get-Time or we risk losing events during the loop)</li>
<li>List the events between the times.</li>
<li>Set $lasttime=$newtime</li>
<li>Loop again.</li>
</ol>
<p>Enjoy!</p>
<p>The below command emulate this command on Linux:</p>
<pre><code>tail <span class="hljs-operator">-</span>f <span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>log<span class="hljs-operator">/</span>messages
</code></pre><p>PowerShell Oneliner with Function (Can set which log and how many minutes to look back in initial output and computername):</p>
<pre><code>Function Tail ($logspec<span class="hljs-operator">=</span><span class="hljs-string">"Application"</span>,$pastmins<span class="hljs-operator">=</span><span class="hljs-number">5</span>,$computer<span class="hljs-operator">=</span>$env:computername) {$lastdate<span class="hljs-operator">=</span>$(Get<span class="hljs-operator">-</span>date).addminutes(<span class="hljs-operator">-</span>$pastmins);<span class="hljs-keyword">while</span> ($True) {$newdate<span class="hljs-operator">=</span>get<span class="hljs-operator">-</span>date;get<span class="hljs-operator">-</span>winevent $logspec <span class="hljs-operator">-</span>ComputerName $computer <span class="hljs-operator">-</span>ea <span class="hljs-number">0</span> <span class="hljs-operator">|</span> ? {$_.TimeCreated <span class="hljs-operator">-</span>ge $lastdate <span class="hljs-operator">-</span>AND $_.TimeCreated <span class="hljs-operator">-</span>le $newdate} <span class="hljs-operator">|</span> Sort<span class="hljs-operator">-</span>Object TimeCreated;$lastdate<span class="hljs-operator">=</span>$newdate;start<span class="hljs-operator">-</span>sleep <span class="hljs-operator">-</span>milliseconds <span class="hljs-number">330</span>}}; Tail
</code></pre><p>Smaller PowerShell Oneliner hard coded for 1) the Application log, 2) 5 minute lookback and 3) local computer only. (at 243 characters, it is 116 characters (30%) shorter than the 360 character oneliner above):</p>
<pre><code>$lastdate<span class="hljs-operator">=</span>$(Get<span class="hljs-operator">-</span>date).addminutes(<span class="hljs-number">-5</span>);<span class="hljs-keyword">while</span> ($True) {$newdate<span class="hljs-operator">=</span>get<span class="hljs-operator">-</span>date;get<span class="hljs-operator">-</span>winevent Application <span class="hljs-operator">-</span>ea <span class="hljs-number">0</span> <span class="hljs-operator">|</span> ? {$_.TimeCreated <span class="hljs-operator">-</span>ge $lastdate <span class="hljs-operator">-</span>AND $_.TimeCreated <span class="hljs-operator">-</span>le $newdate}<span class="hljs-operator">|</span> Sort<span class="hljs-operator">-</span>Object TimeCreated;$lastdate<span class="hljs-operator">=</span>$newdate;start<span class="hljs-operator">-</span>sleep <span class="hljs-operator">-</span>milliseconds <span class="hljs-number">330</span>}
</code></pre><p>P.S. This post has been included in the Mission Impossible Code series because:</p>
<ul>
<li>The solution is very concise.</li>
<li>The use of dates versus eventlog index seems to be a new approach (that enabled much shorter code than using index).</li>
<li>It is pragmatic and efficient to the need at hand.</li>
<li>As a oneliner it is easy to bring to ephemeral test machines.</li>
<li>It has enough features to be used as a complete solution for log tailing.</li>
<li>It supports remote computers.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Ultimate AWS AutoScaling Group ASG Lab Kit]]></title><description><![CDATA[A while back I wrote a blog and companion Cloud Formation templates for experimenting with the ways an ELB creation template could be linked to an ASG. That iteration was based on an ASG template designed to show how to kernel patch linux and reboot ...]]></description><link>https://missionimpossiblecode.io/the-ultimate-aws-autoscaling-group-asg-lab-kit</link><guid isPermaLink="true">https://missionimpossiblecode.io/the-ultimate-aws-autoscaling-group-asg-lab-kit</guid><category><![CDATA[cloudformation]]></category><category><![CDATA[Deep Dive]]></category><category><![CDATA[Infrastructure as code]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Windows]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Fri, 20 Mar 2020 07:19:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657975062183/l8icQjRyk.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A while back I wrote a blog and companion Cloud Formation templates for experimenting with the ways an ELB creation template could be linked to an ASG. That iteration was based on an ASG template designed to show how to kernel patch linux and reboot without termination using ASG Lifecycle hooks.</p>
<p>I had a number of improvements I wanted to make to this template set and this blog represents that work.</p>
<p>The result is really the answer to the question “What would be a minimal, but production-useful working example to learn and experiment with AWS ASGs that use spot instances and proper lifecycle hooks?”</p>
<p>Since the last team I managed had to do all of our automation work for both Windows and Linux, I wanted the solution to work for both.</p>
<h1 id="heading-tldr-feature-summary">TL;DR Feature Summary</h1>
<p>So here is the net set of functionality that the Ulitimate AWS AutoScaling Group Lab Kit includes:</p>
<h3 id="heading-previously-existing-features">Previously Existing Features</h3>
<p>The following features were made available in a previous incarnation of this template at: <a target="_blank" href="https://MissionImpossibleCode.io/post/asg-lifecycle-hook-for-linux-kernel-patching-with-a-reboot-in-aws-autoscaling-groups/">https://MissionImpossibleCode.io/post/asg-lifecycle-hook-for-linux-kernel-patching-with-a-reboot-in-aws-autoscaling-groups/</a></p>
<ul>
<li>Create a “Launching” ASG lifecycle hook so Linux kernel patching could reboot before health checks start (thereby avoiding early termination).</li>
<li>Allow an test web app to be installed to emulate a real application server.</li>
<li>Maintainability Built-in: Enable patch updating of the cluster by simply updating the CloudFormation stack.</li>
<li>Optional Troubleshooting mode that sets up SSM permissions and installs the SSM agent.</li>
<li>Support High Availability Only (no scaling): Warm HA (1 Instance - usable for applications that don’t support multiple nodes), HOT/HOT HA (2 instance ASG - if application supports it)</li>
<li>“logit” function to expose script information to the console and common logs (Linux: /var/log/messages, Windows: Application log</li>
</ul>
<p>Here is the previous article if you want to learn more about it’s features and design - which includes comparison to other ASG patching methods: <a target="_blank" href="https://MissionImpossibleCode.io/post/asg-lifecycle-hook-for-linux-kernel-patching-with-a-reboot-in-aws-autoscaling-groups/">ASG Lifecycle Hook for Linux Kernel Patching with a Reboot In AWS Autoscaling Groups</a></p>
<h3 id="heading-new-features-all-for-both-windows-and-linux">New Features (all for both Windows and Linux):</h3>
<ul>
<li>Be a great lab kit for learning, but also be a <strong>great starting point for actual production implementations</strong>.</li>
<li><strong>Windows support</strong> for all previous functionality which was only for Linux. This is especially important if your Windows spin up and initial automation might exceed the default hook time of 60 minutes (ahhh, and time to make a custom AMI for that and to never use T2 instances - just saying)</li>
<li>Maintainability Built-in: Allow CloudFormation to <strong>lookup the latest AMI</strong> (including on updates) AND enable an override to peg to a specific or custom AMI.</li>
<li><strong>Optional installation of CodeDeploy</strong> in case the ASG is wired to it for code deployment.</li>
<li><strong>Group CloudFormation parameters</strong> in a sensible way, rather than the default alphanumeric sort.</li>
<li>Support “<strong>Least Resource Creation</strong>” by only creating AWS resources when they will be used - for instance not configuring SSM IAM permissions if the troubleshooting feature was not configured.</li>
<li>Support <strong>Spot Instances</strong> and basic spot configuration parameters.</li>
<li>Support <strong>non-Spot configurations</strong> (by setting On Demand Percentage Above Base to zero) - which also supports complete on-demand ASGs that can select from multiple instance types to avoid failure when a specific instance is exhausted in an availability zone.</li>
<li>Support <strong>Configurable Autoscaling (optional)</strong> and include parameters for configuring it (step scaling policies)</li>
<li>Support <strong>TERMINATING lifecycle hooks</strong> and cleanup script for implementations that should do clean up or deregistration when the ASG scales in.</li>
<li>Support <strong>three OS Patch Scopes</strong>: all patches, only security patches and no patching (for faster testing of other things)</li>
<li><strong>State based installs</strong> - only trigger installs if the desired software is not present already.</li>
<li><strong>Built-in scaling testing</strong> by including an optional sythentic CPU driving utility and SSM parameter to control it. This allows you to dial-in the CPU utilization load you want the ASG to be under and change it after deployment to completely validate the scaling parameters and smoothness. Following the “Least Resource Creation” principle, the resources to support this are only deployed if you configure the capability.</li>
<li><strong>Allow override of the basic, built-in Instance Profile IAM Role</strong> that the template creates with one that already exists.</li>
<li>**Allow extension of userdata from an embedded script, local file or a file to download from s3://, http:// or https://</li>
</ul>
<h1 id="heading-technical-design">Technical Design</h1>
<h2 id="heading-minimal-but-completely-working-template">Minimal but Completely Working Template</h2>
<p>The CloudFormation template is purposely minimal in order to more clearly demonstrate the concepts of the solution. At the same time it includes everything needed and works. The approach adheres to <a target="_blank" href="https://MissionImpossibleCode.io/post/back-to-basics-testable-reference-pattern-manifesto-with-testable-sample-code/">The Testable Reference Pattern Manifesto</a></p>
<h2 id="heading-tested-with-both-asg-updatepolicy-settings">Tested With Both ASG Updatepolicy Settings</h2>
<p>The parameter UpdateType defaults to “RollingThroughInstances” which sets the UpdatePolicy to use AutoScalingRollingUpdate, but it can be changed to “ReplaceEntireASG” to set the UpdatePolicy to use AutoScalingReplacingUpdate. Although not tested with Lambda based updates, they would be expected to work just fine with this template.</p>
<h2 id="heading-least-privilege-iam">Least Privilege IAM</h2>
<p>The IAM Roles and least privilege permissions are included so that it is clear what permissions are needed and so that instances do not have more permissions than needed to interact with their own ASG. Two possible methods for limiting the permissions are provided. Using the ASG name in the Resource specification of the IAM is active. Using a condition on a tag is provided as a tested, but commented out alternative.</p>
<h2 id="heading-maximizing-arn-flexibility-for-template-reuse">Maximizing ARN Flexibility for Template Reuse</h2>
<p>The ASG arn in the IAM policy with the SID “ASGSelfAccessPolicy” demostrates maximizing the use of intrinsic AWS variables by using them for <strong>AWS Partition</strong> (use in Gov cloud or China without modification), <strong>AWS Account ID</strong> (use in any account) and <strong>AWS Region</strong> (use in any region without modification).</p>
<h2 id="heading-works-without-asg">Works Without ASG</h2>
<p>If the userdata code cannot retrieve it’s ASG tag it assumes that it is not in an ASG and all lifecycle hook actions are skipped. This allows the solution to be used in non-ASG scenarios.</p>
<h2 id="heading-patch-maintenance-built-in">Patch Maintenance Built-in</h2>
<p>Zero-downtime patching for the entire ASG is supported by updating the PatchRunDate in the cloudformation stack - the entire fleet will be replaced with instances that are up to date on patching. The date is purposedly used to record an environment variable within Userdata so that the ASG Updatepolicy knows it should replace all instances.</p>
<h2 id="heading-scheduled-asg-patching">Scheduled ASG Patching</h2>
<p>By simply scheduling a cloud formation update command with an updated date, the entire ASG will roll. The most AWS cloudy way to do this is a scheduled CloudWatch Event that triggers a Lambda function.</p>
<pre><code>aws cloudformation update<span class="hljs-operator">-</span>stack <span class="hljs-operator">-</span><span class="hljs-operator">-</span>stack<span class="hljs-operator">-</span>name <span class="hljs-string">"your-asg-stack"</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>parameters ParameterKey<span class="hljs-operator">=</span>1OSPatchRunDate,ParameterValue<span class="hljs-operator">=</span>$(date <span class="hljs-string">'+%Y-%m-%d'</span>),UsePreviousValue<span class="hljs-operator">=</span><span class="hljs-literal">false</span>
</code></pre><h2 id="heading-scheduling-instance-availability">Scheduling Instance Availability</h2>
<p>If you are using this template primarily for HA for an instance, you can also consider using skeddly to set the ASG Desired and Minimum counts to zero for the hours that the instance will not be in use. This assumes that the installed software has it’s state data somewhere else and that you use the termination monitoring to perform any orderly application shutdown if it is needed.</p>
<h2 id="heading-dynamic-extension-of-userdata">Dynamic Extension of Userdata</h2>
<p>Added in Version. 1.2.0 Allows additional script commands during startup. This is parameterized for testing new versions and to enable one CloudFormation template codebase to be used for many different Autoscaling groups. It also allows you to use this template without customizing it so that you can take future updates without headache. Windows 2012 and earlier also have a userdata size limit of 16Kb - this method gets around that.</p>
<ol>
<li>“Embedded” uses the code right in this template and does not use external files at all.</li>
<li>Enter a URL starting with s3://. s3 allows easy private file storage.</li>
<li>http:// or https:// to dynamically source one during instance provisioning. http/s enables usage of git raw urls (whether public or private).</li>
<li>Enter a file pathname on the local instance. The file must be present in the location by the time Userdata processes (e.g. via a custom AMI)</li>
</ol>
<p>For all external file sources, the instance must have a network route and permission to any remote locations.</p>
<p>The code you write must be idempotent so that it does the correct thing when run again after a patching reboot.</p>
<p>There is a simple example at: <a target="_blank" href="https://gitlab.com/DarwinJS/ultimate-aws-asg-lab-kit/-/raw/master/CustomInstanceConfigurationScriptSample.sh_and_ps1">https://gitlab.com/DarwinJS/ultimate-aws-asg-lab-kit/-/raw/master/CustomInstanceConfigurationScriptSample.sh_and_ps1</a></p>
<h2 id="heading-monitoring-and-metrics">Monitoring and Metrics</h2>
<p>Two monitoring and metrics values are recorded as metadata. You can control what log file the is added to (or mute the log file) by altering the function “logit”. Generally you want this to be a log file that is collected by your log aggregation service (sumologic, loggly, etc). If you already collect /var/log/cloud-init-output.log, you can mute the log file write to /var/log/messages.</p>
<h3 id="heading-lastcfpatchrun">LAST_CF_PATCH_RUN</h3>
<p>The CloudFormation parameter <code>PatchRunDate</code> is:</p>
<ul>
<li>saved on the instance as the environment variable LAST_CF_PATCH_RUN in /etc/profile.d/lastpatchingdata.sh</li>
<li>emited to /var/log/messages as “LAST_CF_PATCH_RUN: ”</li>
<li>added as a tag to both the ASG and all Ec2 instances</li>
</ul>
<p>This date simply indicates the initial setup of the ASG or the last fleetwide forced patch. It also serves to purposely change something in userdata so that the entire fleet is forced to be replaced when you run an update and change this date.</p>
<h3 id="heading-actualpatchdate">ACTUAL_PATCH_DATE</h3>
<p>The date as of spin-up is:</p>
<ul>
<li>saved on the instance as the environment variable ACTUAL_PATCH_DATE in /etc/profile.d/lastpatchingdata.sh emited to /var/log/messages as “ACTUAL_PATCH_DATE: ”</li>
</ul>
<p>Instances that spin up as a result of autoscaling will not have their patches limited to the date expressed in LAST_CF_PATCH_RUN, so ACTUAL_PATCH_DATE tracks the date they were actually patched.</p>
<p>Comparing these two dates can help you understand if you have developed a large variety of patching dates due to autoscaling and might want to roll the fleet to a standard date by updating the cloudformation with a new <code>PatchRunDate</code>.</p>
<h1 id="heading-testing-and-observing">Testing and Observing</h1>
<h2 id="heading-kicking-off-the-template">Kicking Off The Template</h2>
<p>Use the AWS CloudFormation console to launch the template - to see how subsequent updates will work, pick 4 instances and set TroubleShootingMode to true.</p>
<h2 id="heading-testing-scaling-configuration-with-synthetic-cpu-loading">Testing Scaling Configuration with Synthetic CPU Loading</h2>
<p>You can validate whether the following respond as designed:</p>
<ul>
<li>Verify designed scaling responsiveness and smoothness - up and down.</li>
<li>Verify AZ scaling configuration.</li>
<li>Verify Spot / On-demand instance parameters are responded as designed including instance types, mixed instances policy, percentage spot, etc.</li>
</ul>
<p>During deployment, be sure to enter a numeric value for the <strong>8DBGCPULoadPercentInitialValue</strong> parameter (Yeah sorry, I even like my variable names to be fully self documenting).</p>
<p>If you do not want scaling to occur immediately, set it low to something like 5.</p>
<p>If you do not provide a value at all, Synthetic CPU Loading is not even setup because this template follows a principle of “Least Configuration”.</p>
<p>After the template completes, you will find a new SSM parameter that is named as “<strong>YourASGName-SyntheticCPULoad</strong>” as the parameter name. Since the ASG name is dynamically named it will be prepended with some random characters.</p>
<p>You can now vary the synthetic CPU load using the parameter and watch the CloudWatch alarms for scale out and scale in and watch the AutoScaling Group for scaling actions.</p>
<blockquote>
<p><strong>IMPORTANT!</strong> Do not deploy the template with a value that causes scale out and then forget about it for a long period or overnight - you might bankrupt your company with AWS billing charges.</p>
</blockquote>
<h2 id="heading-observing-lifecycle-hooks-in-aws-console">Observing Lifecycle Hooks in AWS Console</h2>
<p>In the EC2 Console open the Autoscaling group, on the “Lifecycle Hook” tab observe the ‘instance-patching-reboot’ hook is configured.</p>
<p>Also, before the instances are in service you can see “Not yet in service” in the “Activity History” tab and “Pending:wait” in the “Lifecycle” column of the “Instances” tab for each instance. These will change to indicate the instances are in service as each instance completes setup procedures.</p>
<p>The same is true for observing the terminating hook.</p>
<h2 id="heading-observing-on-instance-script-actions">Observing On Instance Script Actions</h2>
<p>All the actions of this template can be observed without logging into the instance by using the AWS console to view the system log for instances (Right Click Instance =&gt; Instance Settings =&gt; Get System Log) and scanning for the text “USERDATA_SCRIPT:”</p>
<p>The first message will contain “Processing userdata script on instance:”. All the messsages include timestamps so that you can observe things like how long a reboot took and the fact that if you don’t sleep the script, it keeps processing for a while after the reboot command.</p>
<p>On Windows you would need to retreive the Application log to watch launching and terminating hook actions.</p>
<p>If you enable the debugging mode you can get a web based console prompt on both operating systems using SSM Session Manager console.</p>
<h2 id="heading-observing-logs-on-the-instance">Observing Logs on The Instance</h2>
<p>If you need or want to logon to the instance for examination or troubleshooting, set the parameter <code>TroubleShootingMode</code> to ’true’. This enables SSM IAM permissions and installs the SSM agent on the instances to allow AWS Session Manager to logon using SSH or WinRM. For linux, the log lines that you see in the AWS System Console will be in the CloudFormation log at: \var\log\cloud-init-output.log. For Windows it will be the Application log. For observing the termination hook SSM will leave the last received log on the screen - so you can actually see the termination messages after the instance is gone. On Linux you can use:</p>
<pre><code>tail <span class="hljs-operator">-</span>f <span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>log<span class="hljs-operator">/</span>messages
</code></pre><p>On Windows you can use (it is also a good generic EventLog tailing function):</p>
<pre><code>Function Tail ($logspec<span class="hljs-operator">=</span><span class="hljs-string">"Application"</span>,$pastmins<span class="hljs-operator">=</span><span class="hljs-number">5</span>,$computer<span class="hljs-operator">=</span>$env:computername) {$lastdate<span class="hljs-operator">=</span>$(Get<span class="hljs-operator">-</span>date).addminutes(<span class="hljs-operator">-</span>$pastmins);<span class="hljs-keyword">while</span> ($True) {$newdate<span class="hljs-operator">=</span>get<span class="hljs-operator">-</span>date;get<span class="hljs-operator">-</span>winevent $logspec <span class="hljs-operator">-</span>ComputerName $computer <span class="hljs-operator">-</span>ea <span class="hljs-number">0</span> <span class="hljs-operator">|</span> ? {$_.TimeCreated <span class="hljs-operator">-</span>ge $lastdate <span class="hljs-operator">-</span>AND $_.TimeCreated <span class="hljs-operator">-</span>le $newdate};$lastdate<span class="hljs-operator">=</span>$newdate;start<span class="hljs-operator">-</span>sleep <span class="hljs-operator">-</span>milliseconds <span class="hljs-number">330</span>}}; Tail
</code></pre><h2 id="heading-observing-pseudo-web-app">Observing Pseudo Web App</h2>
<p>If you set SetupPseudoWebApp to true, the following is done: 1) A port 80 ingress is added to the default VPC security group, 2) Apache is installed, 3) an apache home page is created which publishes the patching and ASG details of the</p>
<h1 id="heading-code-for-this-post">Code for This Post</h1>
<p><a target="_blank" href="https://gitlab.com/DarwinJS/ultimate-aws-asg-lab-kit/-/blob/master/CloudFormationUltimateAWSASGLabKit.cf.yml">CloudFormationUltimateAWSASGLabKit.yaml</a></p>
<p><a target="_blank" href="https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?templateURL=https://s3.amazonaws.com/missionimpossiblecode.io/files/CloudFormationUltimateAWSASGLabKit.cf.yml">Create Now in CloudFormation Console</a></p>
<h1 id="heading-nitty-gritty-appendix-architecture-heuristics-requirements-constraints-desirements-serendipities-applicability-limitations-and-alternatives">Nitty Gritty Appendix: Architecture Heuristics: Requirements, Constraints, Desirements, Serendipities, Applicability, Limitations and Alternatives</h1>
<p>This section details some of the complex journey that goes into creating a simple and highly functional pattern. It is most helpful to those who would like to architect on top of this solution.</p>
<p>I find it very helpful to enumerate architecture heuristics of a pattern as it helps with:</p>
<pre><code><span class="hljs-number">1.</span> keeping track <span class="hljs-keyword">of</span> the architecture that emerged <span class="hljs-keyword">from</span> the <span class="hljs-string">'design by building'</span> effort.
<span class="hljs-number">2.</span> my own recollection <span class="hljs-keyword">of</span> the <span class="hljs-keyword">value</span> <span class="hljs-keyword">of</span> a pattern <span class="hljs-keyword">when</span> examining past things I<span class="hljs-string">'ve done for a new solution.
3. others quickly understanding the all the points of value of an offered solution - helping guide whether they want to invest in learning how it works.
4. facilitating customization or refactoring of the code by distinguishing purpose designed elements versus incidental elements.</span>
</code></pre><p>I specifically like the model of using <strong>Constraints, Requirements, Desirements, Applicability, Limitations and Alternatives</strong> as it helps indicate the optimization of the result without stating everything as a “requirement”. This model is also more open to emergent architecture elements that come from the build effort itself.</p>
<ul>
<li><strong>Requirement: (Satisfied)</strong> Idempotent coding - does not assume anything about the installed / configured state of a given item. This includes elemental automation utilities like AWS CLI. This allows the code to work:<ul>
<li>On a broader set of distros / editions.</li>
<li>On an AMI that has been prepared from scratch without standard AWS tooling.</li>
<li>With multiple pass processing when an instance is rebooted (already performed steps are skipped or result in no changes).</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Support both Windows and Linux (yum packaging) in all functionality.</li>
<li><strong>Requirement: (Satisfied)</strong> Handle full patching or just security patching.</li>
<li><strong>Requirement: (Satisfied)</strong> Support spot instances.</li>
<li><strong>Requirement: (Satisfied)</strong> Unique internal naming tied to stack name so that it can be deployed many times for multiple, parallel deployments.</li>
<li><strong>Requirement: (Satisfied)</strong> Least Resource Creation - only create AWS resources or do instance installs if they will be used by the specific template launch. (e.g. Providing an IAM Instance Profile Role disables the built-in role creation)</li>
<li><strong>Requirement: (Satisfied)</strong> Support terminating lifecycle hooks to trigger cleanup / deregister during scale in.</li>
<li><strong>Requirement: (Satisfied)</strong> State Based Reboot - only reboot if reboot detection code shows that it is actually needed.</li>
<li><strong>Requirement: (Satisfied)</strong> Precompile .NET bytecode after patching to ensure that new and patched .NET assemblies do not slow down production operations.</li>
<li><strong>Desirement: (Satisfied)</strong> Write code in lowest common denominator so it can be wrapped in other orchestrators. Hence this is done in CloudFormation, which can be encapsulated into other automation systems.</li>
<li><strong>Desirement: (Satisfied)</strong> Built-in scaling testing through driving synthetic CPU load across all instances in the ASG with the capability to change it on demand.</li>
<li><strong>Desirement: (Satisfied)</strong> Organize CloudFormation parameters in groups to make a more sensible interactive template deployment experience.</li>
<li><strong>Desirement: (Satisfied)</strong> Self-documentation by exposing all help information as CloudFormation parameter descriptions - increasing usability for both interactive use and automated use via integration of documentation as comments.</li>
<li><strong>Desirement: (Satisfied)</strong> Build in ASG scaling testing for learning and for production configuration validation.</li>
<li><strong>Desirement: (Satisfied)</strong> Allow IAM Instance Profile Role override with external role</li>
<li><strong>Desirement: (Satisfied)</strong> Automatic latest AWS built AMI lookup with override to peg the AMI.</li>
<li><strong>Desirement: (Satisfied)</strong> Support Gov ARNs without code modification.</li>
<li><strong>Desirement: (NOT Satisfied)</strong> It would be nice if the solution could work with a fixed patch baseline to allow full DevOps environment promotion methods using a known, version pegged set of patches.</li>
<li><strong>Limitation:</strong> The patch level is dynamic and not a fixed baseline. When scaling occurs the newest instances will have patching up to date with their spin-up date. These newer patches will not have been tested with the application.<ul>
<li><strong>Countermeasure</strong>: If you integrate automated QA testing with the provisioning of a new instance, you could catch problems with patching when they happen or by running a separate nightly build of the server tier againt the latest patches.</li>
</ul>
</li>
<li><strong>Limitation:</strong> If you need to design for multiple or many reboots, you would have to do custom code to ensure userdata could pick up in the proper spot after each reboot.<ul>
<li><strong>Countermeasure</strong>: This situation is exactly what cfn-init is for, if you have not previously used it, you can read up on how to implement it <strong>within</strong> the pattern in this post.</li>
</ul>
</li>
<li><strong>Applicability:</strong> If you already release a per-ASG AMI for your own reasons (usually speed of scaling), then simply ensuring that AMI takes into account your desired patching frequency is a better solution. You could shorten your AMI release cycle to something like monthly so that satisfactory patching happens as part of the existing release process. This has the side benefit of version pegging your patching level and allowing it to be part of your development and automated QA and be ensured that production runs on a tested patch level.<ul>
<li><strong>Alternative:</strong> If you have an existing long AMI release cycle (greater than 6 months), you could combine it with the dynamic patching solution offered here to keep the cycle long (to keep the cost and logistics of managing old AMIs to a minimum if that is a high priority).</li>
</ul>
</li>
<li><strong>Alternative: Critical Vulnerability Response</strong> If you have an urgent enough patching scenario, you may wish to temporarily use this pattern to do dynamic patching when you do not normally support it.</li>
<li><strong>Limitation:</strong> This demo template relies on the default VPC security group being added to the instances and on it having default settings which allow internet access. If you have the default VPC security group nulled out (a great security practice!) or other networking configuration that limits internet access, you will need to update the template so that it has outbound internet access in your environment</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Never Hire a Butler to do a Robot's Job - Born for DevOps CI]]></title><description><![CDATA[At Infor, my team is responsible for operating a internal, scalable, highly available implementation of Gitlab It is designed for scaling to service our thousands of SaaS developers building hundreds of applications.
We have built Gitlab CI Runner de...]]></description><link>https://missionimpossiblecode.io/never-hire-a-butler-to-do-a-robots-job-born-for-devops-ci</link><guid isPermaLink="true">https://missionimpossiblecode.io/never-hire-a-butler-to-do-a-robots-job-born-for-devops-ci</guid><category><![CDATA[conference sessions]]></category><category><![CDATA[CI/CD]]></category><category><![CDATA[GitLab]]></category><category><![CDATA[Jenkins]]></category><category><![CDATA[gitlab-runner]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Fri, 16 Aug 2019 18:03:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657974778224/8z_7f2pRQ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>At Infor, my team is responsible for operating a internal, scalable, highly available implementation of Gitlab It is designed for scaling to service our thousands of SaaS developers building hundreds of applications.</p>
<p>We have built Gitlab CI Runner deployment automation to supersede our previous deployment automation for a pseudo-high availability Jenkins configuration similar to the approach of CloudBees.</p>
<p>We have put together a guide for developers to consider Gitlab CI whenever they have the opportunity to reconsider their CI.</p>
<p>These activities have given insight into the many ways which Gitlab CI differs from legacy solutions that predate DevOps and Cloud development.</p>
<p>This conference session will focus on an analysis of how and why Gitlab CI enables a stable, scalable, highly available, secure and easy to provision CI solution for our developers. It will contrast these capabilities to our old standby solution, Jenkins.</p>
<p>Session Link: <a target="_blank" href="https://gitlabcommit2019brooklyn.sched.com/event/TgXn/never-ask-a-butler-to-do-a-robots-job#">https://gitlabcommit2019brooklyn.sched.com/event/TgXn/never-ask-a-butler-to-do-a-robots-job#</a>.</p>
]]></content:encoded></item><item><title><![CDATA[GitLab Commit - The Little Conference That Could - And Did]]></title><description><![CDATA[This post contains the links to my GitLab Commit Brooklyn session “Never Hire a Butler to do a Robot’s Job” as well as an interview I had with Alan Shimel at GitLab Commit.
I’ve spoken at a lot of conferences and I’ve experienced a good number of the...]]></description><link>https://missionimpossiblecode.io/gitlab-commit-the-little-conference-that-could-and-did</link><guid isPermaLink="true">https://missionimpossiblecode.io/gitlab-commit-the-little-conference-that-could-and-did</guid><category><![CDATA[GitLab]]></category><category><![CDATA[Devops]]></category><category><![CDATA[conference]]></category><category><![CDATA[Git]]></category><category><![CDATA[GitLab-CI]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Fri, 16 Aug 2019 18:03:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657974935194/2PiH6w6Tq.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post contains the links to my GitLab Commit Brooklyn session “Never Hire a Butler to do a Robot’s Job” as well as an interview I had with Alan Shimel at GitLab Commit.</p>
<p>I’ve spoken at a lot of conferences and I’ve experienced a good number of the many ways that conferences can fail to deliver.</p>
<p>GitLab Commit had three major things working against it that made me wonder what the experience would be like. First of all it was a first time conference for GitLab. Secondarily it was only one day. In my experience, many one day conferences are thinly veiled marketing events with shallow, slapped together sessions. And finally, session slots were 30 minutes - unless it is a 5 minute lightning session, I’ve never seen a session length that short.</p>
<p>My expectations weren’t high, but I was delighted to be wrong on all counts…</p>
<p>Pssst: You can still catch GitLab Commit in <a target="_blank" href="https://about.gitlab.com/events/commit/">London on October 9th or San Franciso on January 14th.</a></p>
<p>My first hint that this was not going to be a slapped together event was what I experienced in the speaker pipeline after being accepted to speak.</p>
<p>The GitLab conference team had a clear lead-in project plan, a slide template and scheduled a couple practice run throughs with speakers. They also offered opportunities for interviews with DevOps.com and to publish articles at a variety of top DevOps news sites. While it was confidence inspiring to see that the details and marketing were being handled very well, the real proof came at the conference.</p>
<p>The evening before speaker dinner was held at Bouley Test Kitchen - it is the laboratory of Chef David Bouley. I never thought being a subject in a laboratory experiment could be so awesome. Six scrumptious small plates were served in the beautifully intimate and eclectic dining space. However, the most impactful thing was the open welcoming intimacy that the GitLab folks brought with them. From what I hear it is not hard work for them to create such social climate because it is just part of their culture.</p>
<p>Some conferences have real ethos to them - an energy or vibe that can be felt - but generally they have 3 or more days to get that vibe up to full volume. Taking time to wind up is not how GitLab does vibe - the Commit conference hit the ground running.</p>
<p>There were warm welcomes everywhere and the same positive energy I felt from the speaker treatment was available in abundance for every attendee.</p>
<p>My concern about session length was unfounded - speakers (including myself) had to be vigorous and on point with a clear, value focused message. So the length seemed to help create conciseness in what was delivered.</p>
<p>Another interesting thing was the subtle and tasteful presence of the branding everywhere. The conference was spread across three hotels, a coffee shop and a night club / bowling alley. In each venue the GitLab brand was worked into all kinds of small details and in a way that complimented the existing design features of each venue. In replacement wall posters, on mirrors, as lighting, painted on the streets, as lit up drink stir sticks and many more places. However, instead of screaming at you in neon blasts - it was blended into whatever environment you found yourself in. The conference actually blended itself into Brooklyn’s look and feel so as to feel native to the town.</p>
<p>Combined with the warm positive energy of the staff and presenters, the approach of integrating into Brooklyn’s vibe and amplifying it toward GitLabs brand, gave GitLab Commit an instant, full volume vibe right from the beginning.</p>
<p>I had the pleasure of running into the author of this approach, Emily Kyle. She summed up the uniquely engaging approach of the conference as a “Neighborhood Takeover”. And that it was - but it was not a hostile take over - rather it was “blend in and amp up the vibe”.</p>
<p>GitLab Commit really ended up being The Little Conference that Could for me - thanks for a refreshing and energetic event GitLab!</p>
<p><strong>Just In:</strong> GitLab published a summary video that does a good job at capturing some of their conference vibe: <a target="_blank" href="https://www.youtube.com/watch?v=hi2D0Se_VnA">https://www.youtube.com/watch?v=hi2D0Se_VnA</a></p>
<p>You can still catch GitLab Commit in <a target="_blank" href="https://about.gitlab.com/events/commit/">London on October 9th or San Franciso on January 14th.</a></p>
<p><strong>Never Hire a Butler To Do a Robot’s Job</strong>: <a target="_blank" href="https://www.youtube.com/watch?v=UZBjuw02gUc">https://www.youtube.com/watch?v=UZBjuw02gUc</a></p>
<p><strong>Interview with Alan Shimel of Digital Anarchist and DevOps.com (Click the play icon after the page loads)</strong>: <a target="_blank" href="https://vimeo.com/360176633#t=57m54s">https://vimeo.com/360176633#t=57m54s</a></p>
<p><strong>Original Session Abstract</strong>: <a target="_blank" href="https://gitlabcommit2019brooklyn.sched.com/event/TgXn/never-ask-a-butler-to-do-a-robots-job#">https://gitlabcommit2019brooklyn.sched.com/event/TgXn/never-ask-a-butler-to-do-a-robots-job#</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Mission Impossible Code Part 2: Extreme Multilingual IaC (via Preflight TCP Connect Testing a List of Endpoints in Both Bash and PowerShell)]]></title><description><![CDATA[It is not possible for me to count the number of times this code has saved me support calls because I never get those calls ;) A huge part of my work is to build DevOps IaC automation code as tools in a company that runs around 50% Windows and %50% L...]]></description><link>https://missionimpossiblecode.io/mission-impossible-code-part-2-extreme-multilingual-iac-via-preflight-tcp-connect-testing-a-list-of-endpoints-in-both-bash-and-powershell</link><guid isPermaLink="true">https://missionimpossiblecode.io/mission-impossible-code-part-2-extreme-multilingual-iac-via-preflight-tcp-connect-testing-a-list-of-endpoints-in-both-bash-and-powershell</guid><category><![CDATA[Mission Impossible Code]]></category><category><![CDATA[Working Code Example]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Infrastructure as code]]></category><category><![CDATA[automation]]></category><dc:creator><![CDATA[Darwin Sanoy]]></dc:creator><pubDate>Tue, 23 Jul 2019 06:22:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657974622698/8kDcBlQfb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It is not possible for me to count the number of times this code has saved me support calls because I never get those calls ;) A huge part of my work is to build DevOps IaC automation code as tools in a company that runs around 50% Windows and %50% Linux across their many SaaS software stacks.</p>
<p>One of the main types of IaC my team builds is deployment automation for DevOps agents that are designed to run on any of the 10’s of thousands of instances at the company - agents for things like vulnerability scanning, malware scanning, log aggregation and monitoring. Generally these agents are wiring up to an internal or external cloud tenant environment for reporting and/or administration.</p>
<p>Everyday at my job I learn of a new environment I’ve never heard of before that someone is trying to run my team’s code in. Frequently the environment setup is at fault when these DevOps agents error out on their tenant registration calls. After way too many escalations that resulted in the discovery that the environment is at fault - I decided we need to preflight check the tenant URLs we would need to connect to and report failures in logging so that tooling users could easily distinguish when their environment was not allowing endpoint access.</p>
<p>Another common case for endpoint verification is when code depends on public or external package management endpoints for things like yum or chocolatey packages. However, the approach is solid for endpoints of all type whether public or private, local or remote.</p>
<p>If you take a look at a lot of your automation code it may make fundamental assumptions about available endpoints and if it will run in environments that are out of your control, endpoint connectivity validation will save you boatloads of support time :)</p>
<blockquote>
<p><strong>Mission Impossible Priority</strong> - while Mission Impossible Coding applies to anything that requires ruggedness, in toolmaking the return on investment is much higher since, by definition, tools are run in a huge variety of environments they were not tested for in their development cycle.</p>
</blockquote>
<h2 id="heading-constructed-simplicity-conciseness-is-the-tip-of-an-iceberg">Constructed Simplicity (Conciseness) is The Tip of An Iceberg</h2>
<p>While epitomized by the Blaise Pascal quote “I have made this longer than usual because I have not had the time to make it shorter.”, I find that the process of creating concise, simple observations and solutions is complicated and sometimes complex.</p>
<p>Creating concise designs and solutions is a deep passion of mine. However, there is a frustration that is quick on the heals of creating something that is concise. In my line of work, at some point, you usually have to justify your final work. That is the point at which the rest of the iceberg of thinking that backs the concise tip comes to the fore. Frequently the reaction is that it can’t possibly be that involved or that you are spinning up reasons on the fly to simply bolster an idea or solution that was arrived at haphazardly.</p>
<p>Why bother addressing this sentiment? Because concise, mission impossible style, solutions can easily be criticized as lacking sophistication in their implementation. But much like a Mark Twain quote - that ruddy external appearance belies a hard wrought balance of many interrelated tradeoffs to get to a simple solution.</p>
<p>Said another way, solutions that are earnestly designed for conciseness can be rewarded by a perception of being the opposite of what they are - simplistic, backed with little or no thought.</p>
<p>This Mission Impossible Coding series exposes the submerged methodological icebergs below the waterline of the visible and concise solutions it attempts to arrive at.</p>
<h2 id="heading-architecture-heuristics-requirements-constraints-desirements-serendipities-applicability-limitations-and-alternatives">Architecture Heuristics: Requirements, Constraints, Desirements, Serendipities, Applicability, Limitations and Alternatives</h2>
<p>The following list demonstrates the Architectural thrust of the solution. This approach is intended to be pure to simplicity of operation and maintenance, rather than purity of a language or framework or development methodology. It is also intended to have the least possible dependencies. It’s an approach I call “Mission Impossible Coding” because it enables the code to get it’s job done no matter what.</p>
<ul>
<li><strong>Requirement: (Satisfied)</strong> Leverage TCP connect for end point validation, not ping, because:<ul>
<li>It should always be possible while ping and/or UDP are frequently blocked.</li>
<li>It verifies end-to-end service connectivity right to an individual port.</li>
</ul>
</li>
<li><strong>Requirement: (Satisfied)</strong> Allow input parameters to be passed as a simple string - this avoid problems when the parameter must be passed through a complex stack of languages because we aren’t trying to pass a complex data type.</li>
<li><strong>Requirement: (Satisfied)</strong> Allow multiple endpoints to be checked with one parameter.</li>
<li><strong>Requirement: (Satisfied)</strong> Keep the same data type and formatting for the parameter so that it can be passed to either Windows or Linux without transformations.</li>
<li><strong>Requirement: (Satisfied)</strong> Always <strong>informationally</strong> log what parameters are being processed <strong>before</strong> attempting to use them - this helps debugging when parameters are unwittingly being overridden or if they are malformed by a handoff somewhere above in the stack.</li>
<li><strong>Desirement: (Satisfied)</strong> Use similar code structure for Bash and PowerShell to facilitate using the scripts as a cross-language learning aid.</li>
<li><strong>Requirement: (Satisfied)</strong> Use methods that have the broadest shell version and os version support.<ul>
<li><strong>Requirement: (Satisfied)</strong> Use methods that do not require code or utilities to be installed - more important in the face of container-minimalized OS configurations.</li>
<li><strong>Desirement: (Satisfied)</strong> Use an argument format that does not require a ton of additional code to parse in Bash (hence this solution uses a space for key value pair delimiter).</li>
<li><strong>Serendipity: (Discovered)</strong> PowerShell version can run on PowerShell Core in native Linux, Bash version can run in Windows Services for Linux (WSL) on Windows.</li>
</ul>
</li>
<li><strong>Constraint: (Satisfied)</strong> Avoid using language native function definitions to allow implementers to use their own coding styles or standards for where the loop should be and whether a function should emit informational messaging and other details.</li>
</ul>
<h2 id="heading-expansion-on-architectural-heuristics">Expansion on Architectural Heuristics</h2>
<h3 id="heading-mission-impossible-pattern-philosophy">Mission Impossible Pattern Philosophy</h3>
<p>Mission Impossible Code samples are intended to be both 1) usable directly for production use and 2) a top notch pattern to use for your own innovation. More details on why are in the post <a target="_blank" href="https://MissionImpossibleCode.io/post/back-to-basics-testable-reference-pattern-manifesto-with-testable-sample-code/">Back to Basics: Testable Reference Pattern Manifesto (With Testable Sample Code)</a></p>
<h3 id="heading-no-ping-because-you-know-ping">No Ping Because You Know Ping</h3>
<p>The need to avoid reliance on ping as a connectivity verification tool is relatively obvious as it (and UDP traffic) is understandably blocked in so many circumstances. However, doing a TCP connect attempt also verifies the entire end-to-end conversation right down the specific required ports and includes verification that a return conversation is possible.</p>
<h3 id="heading-reduced-or-eliminated-runtime-requirements">Reduced or Eliminated Runtime Requirements</h3>
<p>Common approaches to the problem of verifing connectivity on Linux include using <code>nc</code> or <code>telnet</code> - both of which are not present on many minimalistic distros. On Windows PowerShell 4.x and later <code>Test-NetConnection</code> provides TCP connectivity testing - but is not available on PowerShell Core nor older versions of PowerShell.</p>
<p>All of these are avoided by the solution code in this article so that it can run in the broadest number of contexts. As an unintended positive consequent, it turns out that the PowerShell code works on Windows and Linux. The Bash version also works on Windows Services for Linux running under Windows.</p>
<blockquote>
<p><strong>Mission Impossible Priority</strong> - The unintended positive consequences of reducing or eliminating dependencies are frequent and delightful, like biting into a jelly donut for the first time ;) Dependency reduction / elimination is usually worth it.</p>
</blockquote>
<h3 id="heading-first-class-design-priority-parameters-get-passed-through-complex-multi-layer-stacks-with-value-overrides-allowed-at-many-levels">First Class Design Priority: Parameters Get Passed Through Complex, Multi-layer Stacks With Value Overrides Allowed At Many Levels</h3>
<p>It is important to consider that shell code must be called by something else to run - sometimes by a huge, multi-layered stack of “something else”. In complex automation stacks, each hand-off between layers represents risks for data types that are more complex than a string.</p>
<p>This code could have been built to take a complex data type like a hashtable or array. In my experience this generally leads to massive rabbit holes of effort to pass the data between layers of automation infrastructure. Since each language handles the syntax of complex data types differently, such data may need to be escaped or reformatted between layers due to parsing constraints. Many times specific layers don’t have complex data types or it takes special transformations to pass into the next layer.</p>
<p>To further frustrate the efforts, it can be very difficult to get debugging visibility to the hand-off between these processing environments. On top of this lack of visibility, most parsers don’t give direct errors on the problem. In fact, many times your parameter, now malformed, is not detected until you try to use it.</p>
<p>The string data type is the most elemental and universal. By using a simple, structured string - the pain of complex type passing can be avoided and parsing is handled by the shell code once the data is received into the shell environment. Not only does this allow passing through many layers of automation with ease, it allows Windows and Linux to share the exact same format. Which in my case, allows the same argument to be seamlessly passed to the Windows or Linux branch of the code at the appropriate point in the many layered automation stack.</p>
<blockquote>
<p><strong>Mission Impossible Priority</strong> - how many times have you seen yourself or others stay idealistically fixated on using a data type to the point of wasting many hours. Here is a key place where a dedication to “Mission Impossible Coding” dictates a pragmatic override of idealism to what will consistently work in the broadest number of scenarios. Idealism is a secondary to pragmatism.</p>
</blockquote>
<p>By the way, the format I have chosen here may not be friendly to your specific stack of parameter handoffs - but you can see that it uses a space to delimit key value pairs and equals sign to delimit key from value - feel free to adjust those in a way that is compatible with your stack. In fact, if anyone has devised universal delimiters that pass through most known layers without incident - I’d be interested on a comment on this article.</p>
<h3 id="heading-logging-from-a-toolmakers-perspective">Logging From A Toolmakers Perspective</h3>
<p>A healthy orientation to verbose informational logging is a toolmakers friend as it encourages development users to self-diagnose and self-resolve problems they would otherwise reach out for support on.</p>
<p>A final point to consider with parameters is to always 1) <strong>informationally</strong> log them 2) <strong>before</strong> attempting to use them. Informationally means don’t only log errors - this handles use cases where the bottom level automation is receiving working parameters, but they are not the correct.</p>
<p>The complex stacks our parameters pass through frequently allow parameter overrides at many of these levels - informationally logging them allows a super quick find of an unwitting parameter override. Once again, more critical if your automation is tooling used by others since you don’t have control over the parameter stack. The reason for logging before using them is so that any error handling that might happen does not negate your ability to report the parameters in use at the time.</p>
<blockquote>
<p><strong>Mission Impossible Priority</strong> informational logging of parameters before API calls: 1. Always communicate verbosely with your support team, 2. When you are going to die, write a note who dun it to you.</p>
</blockquote>
<h3 id="heading-built-in-list-processing-from-the-start">Built-in List Processing From The Start</h3>
<p>This code was built from day 1 with processing a list in mind. This design heuristic is stolen directly from PowerShell where CMDLet design makes it exceedingly simple to process lists of things that are pipelined in - so simple, there isn’t much reason not to do it. No matter what language you prefer, how many times have you had to refactor a solution to process a list when you originally built it for a single value. Hard to <strong>list</strong> them all isn’t it?</p>
<h3 id="heading-timeout-is-required">Timeout is Required</h3>
<p>Initially I didn’t bother with timeout functionality in the PowerShell version because the default was bearable. However, when it was run on Azure Cloud Shell - it was unbearable. A strong commitment to pre-mission testing was the only reason this came to the fore.</p>
<blockquote>
<p><strong>Mission Impossible Priority</strong> - relentless pre-mission testing of all alternatives and contingencies is standard operating behavior.</p>
</blockquote>
<h3 id="heading-mission-impossible-code-bonus-minimal-universal-logging">Mission Impossible Code Bonus: Minimal, Universal Logging</h3>
<p>Logging is fundamental to Mission Impossible Coding. I also have Bash and PowerShell code designed with the same eye to minimal, universal reuse. You can implement it with this code by retrieving it from here: <a target="_blank" href="https://github.com/DarwinJS/DevOpsAutomationCode/tree/master/MICode-MinimalUniversal-Logging">MICode-MinimalUniversal-Logging</a></p>
<h2 id="heading-the-code">The Code</h2>
<p>Here is the Bash - last url will purposely fail.</p>
<pre><code>urlportpairlist=<span class="hljs-string">"outlook.com=80 google.com=80 test.com=442"</span>
failurecount=0
<span class="hljs-keyword">for</span> urlportpair <span class="hljs-keyword">in</span> <span class="hljs-variable">$urlportpairlist</span>; <span class="hljs-keyword">do</span>
  <span class="hljs-built_in">set</span> -- $(<span class="hljs-built_in">echo</span> <span class="hljs-variable">$urlportpair</span> | tr <span class="hljs-string">'='</span> <span class="hljs-string">' '</span>) ; url=<span class="hljs-variable">$1</span> ; port=<span class="hljs-variable">$2</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"TCP Test of <span class="hljs-variable">$url</span> on <span class="hljs-variable">$port</span>"</span>
  timeout 3 bash -c <span class="hljs-string">"cat &lt; /dev/null &gt; /dev/tcp/<span class="hljs-variable">$url</span>/<span class="hljs-variable">$port</span>"</span>
  <span class="hljs-keyword">if</span> [ <span class="hljs-string">"$?"</span> -ne 0 ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"  Connection to <span class="hljs-variable">$url</span> on port <span class="hljs-variable">$port</span> failed"</span>
    ((failurecount++))
  <span class="hljs-keyword">else</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"  Connection to <span class="hljs-variable">$url</span> on port <span class="hljs-variable">$port</span> succeeded"</span>
  <span class="hljs-keyword">fi</span>
<span class="hljs-keyword">done</span>

<span class="hljs-keyword">if</span> [ <span class="hljs-variable">$failurecount</span> -gt 0 ]; <span class="hljs-keyword">then</span>
 <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$failurecount</span> tcp connect tests failed."</span>
 <span class="hljs-built_in">exit</span> <span class="hljs-variable">$failurecount</span>
<span class="hljs-keyword">fi</span>
</code></pre><p>Here is the PowerShell - last url will purposely fail.</p>
<pre><code>$UrlPortPairList=<span class="hljs-string">"outlook.com=80 google.com=80 test.com=442"</span>
$FailureCount=<span class="hljs-number">0</span> ; $ConnectTimeoutMS = <span class="hljs-string">'3000'</span>
<span class="hljs-keyword">foreach</span> ($UrlPortPair in $UrlPortPairList.<span class="hljs-keyword">split</span>(<span class="hljs-string">' '</span>))
{
  $array=$UrlPortPair.<span class="hljs-keyword">split</span>(<span class="hljs-string">'='</span>); $url=$array[<span class="hljs-number">0</span>]; $port=$array[<span class="hljs-number">1</span>]
  <span class="hljs-keyword">write</span>-host <span class="hljs-string">"TCP Test of $url on $port"</span>
  $ErrorActionPreference = <span class="hljs-string">'SilentlyContinue'</span>
  $conntest = (new-object net.sockets.tcpclient).BeginConnect($url,$port,$null,$null)
  $conntestwait = $conntest.AsyncWaitHandle.WaitOne($ConnectTimeoutMS,$False)
  <span class="hljs-keyword">if</span> (!$conntestwait)
  { <span class="hljs-keyword">write</span>-host <span class="hljs-string">"  Connection to $url on port $port failed"</span>
    $conntest.close()
    $FailureCount++
  }
  <span class="hljs-keyword">else</span>
  { <span class="hljs-keyword">write</span>-host <span class="hljs-string">"  Connection to $url on port $port succeeded"</span> }
}
If ($FailureCount -<span class="hljs-keyword">gt</span> <span class="hljs-number">0</span>)
{ <span class="hljs-keyword">write</span>-host <span class="hljs-string">"$FailureCount tcp connect tests failed."</span>
  Exit $FailureCount
}
</code></pre><h2 id="heading-tested-on">Tested On</h2>
<p>PowerShell Version (Windows and Linux)</p>
<ul>
<li>PowerShell 5.1 on Windows 10</li>
<li>PowerShell Core 7.0.0.2 on Windows 10</li>
<li>PowerShell Core 6.2.1 on Amazon Linux 2 in Docker on Windows 10</li>
<li>PowerShell Core 6.2.2 on LinuxMint 19.1 On A Laptop</li>
<li>PowerShell on Azure CloudShell</li>
</ul>
<p>Bash Version (Windows and Linux)</p>
<ul>
<li>Ubuntu 18.04.1 LTS in Windows Services for Linux (WSL) on Windows 10</li>
<li>Amazon Linux 2 in Docker on Windows 10</li>
<li>Bash on LinuxMint 19.1 On A Laptop</li>
<li>Bash on Azure CloudShell</li>
</ul>
<h2 id="heading-code-for-this-article">Code For This Article</h2>
<p><a target="_blank" href="https://github.com/DarwinJS/DevOpsAutomationCode/tree/master/MICode-MinimalUniversal-TCPConnectPreflightCheck">MICode-MinimalUniversal-TCPConnectPreflightCheck</a></p>
]]></content:encoded></item></channel></rss>