<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title></title>
    <description>Torn between the digital and the analog</description>
    <link>lucasjhall.github.io/</link>
    <atom:link href="lucasjhall.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 15 Mar 2025 20:44:50 +0000</pubDate>
    <lastBuildDate>Sat, 15 Mar 2025 20:44:50 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Event Driven Client Management</title>
        <description>&lt;p&gt;A consideration on a code based, scalable, event driven client management framework…&lt;/p&gt;

&lt;h3 id=&quot;what-are-we-trying-to-accomplish-here&quot;&gt;What are we trying to accomplish here?&lt;/h3&gt;

&lt;p&gt;I investigate the situation where we, as system administrators, find ourselves in a vastly SaaS driven world which derives many points of truth from across many different systems. How do we manage those things centrally? How do we shift from an actively managed posture, to a passive (or event driven posture), and is there a way to meet in the middle? Is there “one tool to rule them all” or are we siloed by platform? I think to pull myself back, I am just going to outline this idea, conclusion and how I arrived at it in terms of macOS and how we can embrace the inevitable, and build systems around what is currently the “only way” to manage macOS devices. And to do that we must…&lt;/p&gt;

&lt;h3 id=&quot;understand-the-macos-management-ecosystem-and-its-current-trajectory&quot;&gt;Understand the macOS management ecosystem and its current trajectory&lt;/h3&gt;

&lt;p&gt;Apple has set the direction with managing macOS devices. What I try to reiterate to folks in conversation is that the MDM spec is the MDM spec. Meaning Apple, like everywhere else in their ecosystem is defining the “rules of the game” to interact with their devices in a managed way. When setting out to “look for an mdm” I think it’s good to constantly remind yourself that it’s the same set of API calls from the biggest to the smallest vendor, Apple only gives you a defined amount of tooling, anything else is fluff. Not saying fluff isn’t helpful, but fluff is fluff and not core to device management implications.&lt;/p&gt;

&lt;p&gt;If we step back and look for an MDM vendor that 1.) works well, especially at the scale you’re operating and 2.) has the ability to be extensible to your desire. That path of discovery will look a lot different for a lot of people, and that’s okay, “live your truth”. I make the argument that being extensible is having an actively under development and functioning API. A good API is, in my opinion, the best feature a vendor can offer.&lt;/p&gt;

&lt;p&gt;Why is that the best feature? I am approaching this from a perspective where we treat not only “infrastructure as code” but client “configuration as code” as well. Something increasingly difficult to do when Apple is gating particular controls that can be managed of devices via an MDM. Having an API allows for the possibility of settings being managed in a systematic way, via some system. I realize this isn’t something everyone wants, and a small business managing a few dozen machines is completely doable in a good web interface. Where this is very beneficial is when you start approaching thousands of clients and the idea of client “configuration as code” piece really begins to have benefits.&lt;/p&gt;

&lt;p&gt;As Apple shifts this control and requirements to MDM only, this means for those items in place we are reliant on our MDM first and foremost, which is then in turn reliant on Apple and the MDM framework. The reliability and visibility into anything past what your MDM offers is minimal, to any and all vendors, and that’s just a reality of the current ecosystem in place. How do we function within those constraints while also having the ability to add robustness and logic driven workflows? In short, how do we build a flexible glue between our systems to both adhere to this new paradigm as well as have the benefits of a code driven environment? That’s what I want to know as well, and what led me on this thought experiment.&lt;/p&gt;

&lt;h3 id=&quot;what-if-client-management-wasnt-linear&quot;&gt;What if client management wasn’t linear?&lt;/h3&gt;

&lt;p&gt;I remember watching some videos on AWS’s EventBridge after it got announced and thinking “huh, that could be cool”, as most of my bad ideas start out. If you’re not familiar with AWS Event Bridge you can learn more &lt;a href=&quot;https://aws.amazon.com/eventbridge/&quot;&gt;here&lt;/a&gt;. In short it’s a central flow of json “events” (logs) that can adhere to a predefined schema, this schema you can then trigger other AWS services is a pattern match in that schema is found. The idea is that you can scale out actions horizontally, independently and quickly to other AWS services. A successor to a more linear SQS or SNS queue, arguably fulfilling an entirely different role, but I digress.&lt;/p&gt;

&lt;p&gt;This “bad idea” was in fact, “What if client management wasn’t linear?” What if we shifted from treating the management hierarchy as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config -&amp;gt; apply -&amp;gt; remediate&lt;/code&gt; and looked at clients as nodes within a greater ecosystem, going through various stages of their own life cycle, and allowing events that these nodes “emote” determine other actions that happen within the management ecosystem?&lt;/p&gt;

&lt;h4 id=&quot;so-its-just-a-big-if-this-then-that&quot;&gt;So it’s just a big if &lt;em&gt;this&lt;/em&gt; then &lt;em&gt;that&lt;/em&gt;?&lt;/h4&gt;

&lt;p&gt;Yes! No! Well sort of… isn’t all code? It would be more like a “if &lt;em&gt;this&lt;/em&gt; then that &lt;em&gt;and&lt;/em&gt; that and &lt;em&gt;that&lt;/em&gt; or &lt;em&gt;that&lt;/em&gt; and if… and if…” An oversimplification of this idea is a single stream of events propagated from any system in regards to a device, or node. From that single stream of events, we can delve into the events, in near real time, pick and choose what actions we want to take, all while archiving these node specific events for evaluating trends. This seems huge, adn overwhelming, and it was/is! The benefit of this design is you can start with the smallest process or flow and add and grow the interactions in this framework as needed, approaching it at a micro, rather than a macro, scale.&lt;/p&gt;

&lt;p&gt;I think the idea in general is not limited to AWS or EventBridge, conceptually you could extend this idea, or framework, to any system that can parse incoming data, and then perform actions based on said triggers, such as initiating webhooks via a logging tool. I will keep exploring this framework through the lens of AWS and it’s available tooling, but in no way is that the only path to implementing this idea/framework.&lt;/p&gt;

&lt;h3 id=&quot;to-begin-we-start-in-the-middle&quot;&gt;To begin, we start in the middle.&lt;/h3&gt;

&lt;p&gt;The core idea of EventBridge is that there is a defined JSON schema. AWS EventBridge Schema and &lt;a href=&quot;https://docs.aws.amazon.com/eventbridge/latest/userguide/eventbridge-schemas.html&quot;&gt;Registry&lt;/a&gt; options are quite robust, so for sake of illustration we will define our schema with some actions we wish to perform. In the life of our node we can expect lots of things to happen, but at the very core of what happens to a node, what do we want? We want to know about the event, we want to perform an action because of the event, we want to keep record of the event historically and we want to be able to look at a high level of events looking for trends.&lt;/p&gt;

&lt;h4 id=&quot;hey-listen&quot;&gt;Hey listen!&lt;/h4&gt;

&lt;p&gt;By default, any event in an Event Bridge does nothing. It effectively goes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/null&lt;/code&gt; unless a rule is triggered. So getting started the best way to have some visibility is to set a blanket rule for any, &lt;a href=&quot;https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html&quot;&gt;or a subset meeting an event pattern&lt;/a&gt;, to send the events to Cloudwatch. You can configure a &lt;a href=&quot;https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-working-with-targets.html&quot;&gt;target&lt;/a&gt; of a specific Cloudwatch log group to receive any/all of the events. A good way to ensure the events are hitting the event bus at all.&lt;/p&gt;

&lt;p&gt;By each extension, each trigger could have its own target log group, or you could have a blanket catch-all log group for any event passing through an event bus, that design choice is really up to you and the complexity of the event based triggers, and the desired level of visibility. You could also set a lambda target that transforms the event payload to GELF or some other logging specific format and then ships it to a unified logging service you may have at your utilization. Or perhaps just setting the TTL on cloudwatch to be a few weeks but also sending the logs to an s3 bucket for long term retention? All possibilities.&lt;/p&gt;

&lt;h4 id=&quot;do-the-thing-&quot;&gt;Do the thing-&lt;/h4&gt;

&lt;p&gt;What thing? All the things? AWS Event Bridge logic has an ever expanding list of available &lt;a href=&quot;https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-targets.html&quot;&gt;targets&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;API destination&lt;/li&gt;
  &lt;li&gt;API Gateway&lt;/li&gt;
  &lt;li&gt;AWS Batch job queue&lt;/li&gt;
  &lt;li&gt;CloudWatch Logs group&lt;/li&gt;
  &lt;li&gt;CodeBuild project&lt;/li&gt;
  &lt;li&gt;CodePipeline&lt;/li&gt;
  &lt;li&gt;Amazon ECS tasks&lt;/li&gt;
  &lt;li&gt;Event bus in a different account or Region&lt;/li&gt;
  &lt;li&gt;Firehose delivery stream (Kinesis Data Firehose)&lt;/li&gt;
  &lt;li&gt;Inspector assessment template (Amazon Inspector)&lt;/li&gt;
  &lt;li&gt;Kinesis stream (Kinesis Data Streams)&lt;/li&gt;
  &lt;li&gt;AWS Lambda function&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wowza, that’s a lot of services (and only some of the supported ones), and it makes my head spin with the possibilities. From a system administration perspective we can utilize our event bus as just that, a single place for passing and triggering off events we care about. Will it be something for passive analysis? No. But the ability to integrate with Kinesis opens a lot of doors in that realm.&lt;/p&gt;

&lt;h3 id=&quot;so-what-could-this-look-like&quot;&gt;So what could this look like?&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/aws-min.png&quot; alt=&quot;minimal workflow render&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Starting off will be an illustration of a basic scenario, a device enrolls. Assuming your MDM of choice has the ability to support webhooks, you could receive that payload via API Gateway, process via Lambda, and turn around and invoke change, nothing new and a fair amount of tooling and processes exist around this idea.&lt;/p&gt;

&lt;p&gt;In this thought experiment I want to have a less direct invocation, but the ability to process this event and then have multiple subsets of services fan out doing the needed actions for this event. So rather than a single Lambda function that is responsible for all the processing and logic of an event.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/aws-event-bridge.png&quot; alt=&quot;event bridge workflow render&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So something to this ☝🏻 design can illustrate the beginning of what I am imagining. A MDM endpoint will send a POST event to an API Gateway endpoint where it will send to the Lambda function to process the event specific to that data source. Those processors generate an event that contains all the needed information for the Event Bridge to process, here’s an over simplified example of an output of the MDM Processor to an event:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;device&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;enroll&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;device&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;uuid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;someSerial&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;communicate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From this event we can trigger each of these individual Lambda processors that can systematically interact with the upstream services. Looking at the hypothetical triggers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Device Enroll&lt;/code&gt; is triggered as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scope&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;action&lt;/code&gt; match
    &lt;ul&gt;
      &lt;li&gt;This process then knows to use some of the information to:
        &lt;ul&gt;
          &lt;li&gt;Update the AssetsDB with device information, such as enrollment state and time&lt;/li&gt;
          &lt;li&gt;Update the MDM with any Asset specific notes or device name from the AssetsDB from above&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User Deprovision&lt;/code&gt; is not triggered&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Comms Trigger&lt;/code&gt; is triggered as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;communicate&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;This payload can then be translated to the needed block kit syntax and sent to Slack via a Lambda function that contains all the needed information to interact with Slack via the SDK, Bolt Python etc&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Log Trigger&lt;/code&gt; is triggered as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Send the log to a specific s3 bucket as described in the trigger&lt;/li&gt;
      &lt;li&gt;Send the log to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stats processor&lt;/code&gt; that could translate the basic event, device enrolled to a time series data point then sent off to AWS Timestream for later analysis via the managed TSDB (time series database)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wait, MDM Processor is on that illustration twice? Yes. With this in theory you can have as specific services as you’d like to handle this processes. I envision each processor emoting its own events, if for nothing else then centralized logging and statistics around these duties they are performing, such as:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;communicate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;slack_post&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;info&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;channel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;someChanel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;someText&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;triggering_event&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;someEvent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we will systematically aggregate logs in a logical fashion as well as be able to create time series data specific to the usage of any of these micro services as they are consumed. cool 😎&lt;/p&gt;

&lt;h4 id=&quot;user-deactivation&quot;&gt;User Deactivation&lt;/h4&gt;

&lt;p&gt;Identity Provider processor as well! So something like:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;deactivated&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This event could trigger an MDM Processor event to lookup the user’s assigned devices and automatically deactivate or lock (depending on your use case) when a user becomes deactivated.&lt;/p&gt;

&lt;h4 id=&quot;saas-partners&quot;&gt;SaaS Partners&lt;/h4&gt;

&lt;p&gt;I included an ambiguous item on the diagram “SaaS Support” simple because Event Bridge supports &lt;a href=&quot;https://aws.amazon.com/eventbridge/integrations/&quot;&gt;SaaS integrations&lt;/a&gt;, such as (but not limited too):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SalesForce via AppFlow&lt;/li&gt;
  &lt;li&gt;Auth0&lt;/li&gt;
  &lt;li&gt;Datadog&lt;/li&gt;
  &lt;li&gt;New Relic&lt;/li&gt;
  &lt;li&gt;Zendesk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which allows for the skipping of the event ingestion and allows for these SaaS Partners to send information directly to your event bus. Not super helpful in the world of system administration, but time will lend to seeing if the adaptation of this integration continues.&lt;/p&gt;

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

&lt;p&gt;As services become dispersed, information becomes harder to aggregate, system administrators are seeing those sources of truth in various locations, a single database with all the things would be amazing, but a far cry from reality.&lt;/p&gt;

&lt;p&gt;I think an event driven client endpoint ecosystem allows for the inflexibility of these dispersed services to be aggregated by events initially, triggering downstream events and actions. An event driven architecture is something I’ve thought about in the past, triggering scripts and items from syslog servers, but it was always strenuous with a large amount of work and overhead. Not that an Event Bridge based system isn’t, it just feels more accessible as this, to some degree, is what it was designed to do… for sysadmins? probably not. But our services, infrastructure and systems need to follow suit with the direction of the industry if we want to stay flexible and scalable.&lt;/p&gt;

&lt;p&gt;In Casablanca there is a conversation between Major Strasser (an Officer of the Luftwaffe) and Captain Renault (captain of police in Casablanca) that goes like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Strasser:&lt;/strong&gt; I’m not entirely sure which side you’re on.&lt;br /&gt;
&lt;strong&gt;Renault:&lt;/strong&gt; I have no conviction, if that’s what you mean. I blow with the wind, and the prevailing wind happens to be from Vichy.&lt;br /&gt;
&lt;strong&gt;Strasser:&lt;/strong&gt; And if it should change?&lt;br /&gt;
&lt;strong&gt;Renault:&lt;/strong&gt; Well, surely the Reich doesn’t permit that possibility.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wouldn’t say I have &lt;em&gt;no&lt;/em&gt; conviction, but I resonate with Renault’s sentiment here, in my little town, I only have the control over the systems I am permitted, and I have to do what I can to better that environment within the bounds of the “prevailing wind”, be it trends in SaaS services, changes to the MDM Specification or shifting sands of internal tooling and processes.&lt;/p&gt;

&lt;p&gt;Thus concludes this ramble under the guise of a thought experiment.&lt;/p&gt;

&lt;h4 id=&quot;some-references&quot;&gt;Some References&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;https://support.apple.com/guide/mdm/welcome/web&quot;&gt;Apple MDM&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://aws.amazon.com/eventbridge/&quot;&gt;AWS EventBridge&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://aws.amazon.com/lambda/&quot;&gt;AWS Lambda&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;AWS CloudWatch&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;AWS S3&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://json-schema.org&quot;&gt;JSON Schema&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 18 May 2021 19:11:11 +0000</pubDate>
        <link>lucasjhall.github.io/2021/05/18/event-driven-client-management.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/2021/05/18/event-driven-client-management.html</guid>
        
        
      </item>
    
      <item>
        <title>pyThings</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/lucasjhall/pyThings&quot;&gt;pyThings&lt;/a&gt; is a Python library used to create and call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-callback-urls&lt;/code&gt; with the assistance of &lt;a href=&quot;https://github.com/martinfinke/xcall&quot;&gt;xcall&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example of creating some Projects (Epics) and Tasks (Stories) via another bit of logic (dry details of the items withheld):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/gifs/things.gif&quot; alt=&quot;Things auto populating projects and tasks&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was created in haste to include in some other unnecessary automation of daily tasks and time tracking. This library supports most of the URL Schema published by Cultured Code on their documentation page &lt;a href=&quot;https://culturedcode.com/things/support/articles/2803573/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add/Update a Task:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pyThings.tasks&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AddTask&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AddTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;A New Task&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pyThings.tasks&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateTask&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_token&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;ID_STRING&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Add/Update a Project:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pyThings.projects&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AddProject&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AddProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;My New Project&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pyThings.projects&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateProject&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_token&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;ID_STRING&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As I mentioned in the README, while Things &lt;a href=&quot;https://culturedcode.com/things/support/articles/2803573/&quot;&gt;Callback URL Scheme&lt;/a&gt; is robust, it is very write oriented. It is not currently possible to get anything from Things as far as I could find, aside from an associated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-things-id&lt;/code&gt; upon creation, which is needed to update any Task or Project.&lt;/p&gt;

&lt;p&gt;A few things I am thinking about for future release are moving to something like &lt;a href=&quot;https://github.com/robwalton/python-xcall&quot;&gt;robwalton/python-xcall&lt;/a&gt; which is a client for bi-directional communication with x-callback-url. As well as removing the execution of the callback url upon class creation, this was dne just to simplify the library in my context, though I realize that may not be a wide use case. The idea of creating Projects and Issues as classes is retaining the information in a diffable format, that can then be cached locally, outside of Things, to be used again (when using this library in syncing from say a project management suite via another tool).&lt;/p&gt;

&lt;p&gt;I went as far as digging through the sqlite db Things keeps on disk, but…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://media.giphy.com/media/I5TF0P9E9bmI8/giphy.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 31 Mar 2021 20:00:00 +0000</pubDate>
        <link>lucasjhall.github.io/python/2021/03/31/pythings.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/python/2021/03/31/pythings.html</guid>
        
        
        <category>python</category>
        
      </item>
    
      <item>
        <title>TimingPy</title>
        <description>&lt;p&gt;TimingPy is a Python library to wrap the web API for
&lt;a href=&quot;https://timingapp.com/&quot;&gt;Timing.app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Timing.app is a &lt;a href=&quot;https://timingapp.com/pricing&quot;&gt;paid&lt;/a&gt;
macOS app for time tracking. The goals of this quick project were two fold,&lt;/p&gt;

&lt;p&gt;1) to continue learning Python and
2) creating some tooling to bridge all of the various
tools/service I use throughout the day, more on this someday (maybe).&lt;/p&gt;

&lt;p&gt;The repository is available on Github here: &lt;a href=&quot;https://github.com/lucasjhall/TimingPy&quot;&gt;lucasjhall/TimingPy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TimingPy supports all of the calls available via
&lt;a href=&quot;https://web.timingapp.com/docs/&quot;&gt;Web API Documentation&lt;/a&gt;
as of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2021-02-27&lt;/code&gt; but currently has no data validation.
Here is a quick illustration to create a task:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimingPy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_new_task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;New Task&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2021-03-16T16:00:00+0000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;end_date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2021-03-16T17:00:00+0000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Returning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;task&lt;/code&gt; a requst obj that shold read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Response [201]&amp;gt;&lt;/code&gt; via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print()&lt;/code&gt;. Supporing the usual request attributes. Reading details about the task created:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\/time-entries\/$ID_OF_EVENT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;start_date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2021-03-16T16:00:00.000000+00:00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;end_date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2021-03-16T17:00:00.000000+00:00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;duration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;project&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;New Task&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;notes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;is_running&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;creator_name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$YOUR_ACCOUNT_EMAIL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start_date&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;end_date&lt;/code&gt; need to
be formated in &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO_8601&quot;&gt;ISO8601&lt;/a&gt;
which this tool doesn’t account for, as of yet.&lt;/p&gt;

&lt;p&gt;You can read more on Timing &lt;a href=&quot;https://timingapp.com/&quot;&gt;here&lt;/a&gt;, the
&lt;a href=&quot;https://web.timingapp.com/docs/&quot;&gt;Web API Documentation&lt;/a&gt; and
&lt;a href=&quot;https://timingapp.com/help/teams_overview&quot;&gt;Timing for Teams&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Fri, 19 Mar 2021 08:00:00 +0000</pubDate>
        <link>lucasjhall.github.io/python/2021/03/19/timingpy.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/python/2021/03/19/timingpy.html</guid>
        
        
        <category>python</category>
        
      </item>
    
      <item>
        <title>Into the Stream Deck</title>
        <description>&lt;p&gt;The Elgato Stream Deck is a nifty device aimed for customizable interface for streamers and broadcasters, and I didn’t use it for any of that… one word: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;macros&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;tldr&quot;&gt;tl;dr&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Elgato’s &lt;a href=&quot;https://www.elgato.com/en/gaming/stream-deck&quot;&gt;Stream Deck&lt;/a&gt; and it’s software capabilities&lt;/li&gt;
  &lt;li&gt;100% not using it for streaming, but mostly just macros and daily automation&lt;/li&gt;
  &lt;li&gt;Basic app/script launch&lt;/li&gt;
  &lt;li&gt;Intro to Stream Deck’s profiles &lt;em&gt;and&lt;/em&gt; application aware profiles&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;a-little-on-the-hardware&quot;&gt;A little on the hardware&lt;/h4&gt;

&lt;p&gt;If you’d like you can read more about it on Elgato’s page &lt;a href=&quot;https://www.elgato.com/en/gaming/stream-deck&quot;&gt;here&lt;/a&gt;. It comes in three sizes, Mini (6 keys), Standard (15 keys) and XL (32 keys), it even offers a &lt;a href=&quot;https://www.elgato.com/en/gaming/stream-deck-mobile&quot;&gt;mobile version&lt;/a&gt; that runs on iOS and Android.&lt;/p&gt;

&lt;p&gt;Though this devices is designed for one use, my immediate thought was &lt;em&gt;macros&lt;/em&gt;. I have been using &lt;a href=&quot;https://www.keyboardmaestro.com/main/&quot;&gt;Keyboard Maestro&lt;/a&gt; from sometime and often use a key combo to initiate a function, but a deck with pictures? Maybe even gifs to trigger macros, sign me up! I wont get into my &lt;a href=&quot;https://www.keyboardmaestro.com/main/&quot;&gt;Keyboard Maestro&lt;/a&gt; setup as that could, and may be, a blog post all of its own.&lt;/p&gt;

&lt;p&gt;(&lt;em&gt;A lot of the following examples are synthesized from something I am currently doing, or have done, and not specific to a singular device. All that to say I don’t every recommend accessing or using personally associated apps on a work device.&lt;/em&gt;)&lt;/p&gt;

&lt;h4 id=&quot;getting-set-up&quot;&gt;Getting Set Up&lt;/h4&gt;

&lt;p&gt;Get a Stream Deck, whatever size suits your style/desk. There is effectively a possible infinite folder structure with later Stream Deck firmware/os updates so I went with the “Standard” model as 15 keys seem a happy medium.&lt;/p&gt;

&lt;p&gt;Snag the software for your OS and get going.&lt;/p&gt;

&lt;p&gt;Making a plan… some basic items for illustration:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Launch Slack, possibly even users or specific channels&lt;/li&gt;
  &lt;li&gt;Launch user specific Chrome browsers (work/personal)&lt;/li&gt;
  &lt;li&gt;Some basic tooling around apps I use on the daily&lt;/li&gt;
  &lt;li&gt;Zoom (Call controls)
    &lt;ul&gt;
      &lt;li&gt;Code (Documentation, text manipulation, basic git wrappers)&lt;/li&gt;
      &lt;li&gt;VMware Fusion (VM Controls)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;down-the-rabbit-hole&quot;&gt;Down the rabbit hole&lt;/h4&gt;

&lt;p&gt;Lets take a look at some examples within the apps I mentioned above…&lt;/p&gt;

&lt;p&gt;A note on my shorthand, when I reference something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System &amp;gt; Website&lt;/code&gt;, I am saying within Stream Deck’s configuration (sidebar) under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System&lt;/code&gt; header I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Website&lt;/code&gt; function, so on and so forth.&lt;/p&gt;

&lt;h5 id=&quot;slack&quot;&gt;Slack&lt;/h5&gt;

&lt;p&gt;Like a lot a folks I live in Slack during the work day, and the quick search/channel jumping is fine, but I wanted to see if there was a way to make some shortcuts to channels/DMs I spend the most time… and it was actually pretty easy on macOS. I used an application url callback for Slack, and that looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slack://channel?team=$TEAM_ID&amp;amp;id=$CHANNEL_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The quickest way to find these values out is load the slack team/channel you’d like to access in the browser:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://app.slack.com/client/T04QVKUQG/C0533K6HC&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T04QVKUQG&lt;/code&gt; is the team id, the second is the channel or user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C0533K6HC&lt;/code&gt; so swap those out with the callback URL above:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slack://channel?team=T04QVKUQG&amp;amp;id=C0533K6HC&lt;/code&gt; and voila&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/stream-deck/slack-url.png&quot; alt=&quot;Stream Deck Example URL&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Add that to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System &amp;gt; Website&lt;/code&gt; as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;url&lt;/code&gt;, on clicking you should launch directly to your desired channel or user. A team id won’t change per “Slack” instance (team) so you can note this and then while in slack, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;right click &amp;gt; Copy Link&lt;/code&gt; on a channel name and you’ll get a url such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://$team.slack.com/archives/C0533K6HC&lt;/code&gt; where the last part as we see, is the same channel identifier.&lt;/p&gt;

&lt;h5 id=&quot;chrome-profiles&quot;&gt;Chrome Profiles&lt;/h5&gt;

&lt;p&gt;This is another simple one, that just needs a slight bit of digging. If you use multiple Chrome profiles, you just need to determine what those profiles are and how you can reference them. In Chrome (while in the profile you seek to identify) open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chrome://version/&lt;/code&gt; and look for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Profile Path&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Profile Path: /Users/$user/Library/Application Support/Google/Chrome/Profile 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is the name of the Chrome profile (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Profile 1&lt;/code&gt;) we care about, you can test this by running in the terminal:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/Applications/Google&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Chrome.app/Contents/MacOS/Google&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Chrome &lt;span class=&quot;nt&quot;&gt;--args&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--profile-directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Profile 1&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and if its the first profile created, its most likely:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/Applications/Google&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Chrome.app/Contents/MacOS/Google&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Chrome &lt;span class=&quot;nt&quot;&gt;--args&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--profile-directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Default&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A word of warning, running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--profile-directory=&apos;NonexistentProfile&apos;&lt;/code&gt; Chrome will just generate a profile if one does not exist, easily remediated via the “Other People” UI box launched via your user icon.&lt;/p&gt;

&lt;p&gt;I pipe these commands through to &lt;a href=&quot;https://www.keyboardmaestro.com/main/&quot;&gt;Keyboard Maestro&lt;/a&gt;, which is for sure over kill, but you could easily call them in various ways such as the configuration &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System &amp;gt; Open &amp;gt; App / File&lt;/code&gt; where this is the script you wish to run.&lt;/p&gt;

&lt;h3 id=&quot;application-awareness&quot;&gt;Application Awareness&lt;/h3&gt;

&lt;p&gt;So far we’ve talked about triggering some basic functionality with the Stream Deck, launching apps, or specific parts of apps/urls. I have a “base” profile that the stream deck goes to by default and some sub “folders” that look something like this (slimmed down for illustration):&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;default_profile
├── fusion
├── personal_chrome
├── slack_personal
│   ├── channel_a
│   ├── channel_b
│   ├── channel_c
│   ├── user_1
│   └── user_2
├── slack_work
│   ├── channel_a
│   ├── channel_b
│   ├── channel_c
│   ├── user_1
│   └── user_2
├── spotify
├── system_controls
│   ├── lock
│   ├── screen_brightness_preset_a
│   ├── screen_brightness_preset_b
│   ├── screen_saver
│   └── toggle_audio_out
├── work_chrome
└── zoom

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

&lt;p&gt;Any apps such as Zoom, VMware Fusion, or Spotify I didn’t create a static sub folder of items for these apps, instead I used Stream Deck’s ability to switch to a specific &lt;em&gt;profile&lt;/em&gt; when an app is launched, “Smart Profiles”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/stream-deck/smart-profile.png&quot; alt=&quot;Smart Profile Example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I see two big benefits here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;any time an app is opened/in the foreground Stream Deck displays that profile&lt;/li&gt;
  &lt;li&gt;your not constantly navigating and in and our of sub folders trying to access different actions&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;zoom--vmware-fusion&quot;&gt;Zoom &amp;amp; VMware Fusion&lt;/h4&gt;

&lt;p&gt;These profiles are application aware, my base profile simply launches these apps respectively, the app coming to the fore ground causes stream deck to load the profile for each. These applications I created “Hotkeys” for common keyboard commands and shortcuts for these app I find myself in a lot. In Stream Deck, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System &amp;gt; Hotkey&lt;/code&gt;, you can define, name and give an icon to a hotkey trigger.&lt;/p&gt;

&lt;p&gt;Zoom Examples&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Toggle Mute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘ ⇧ A&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Controls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^\&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Start/Stop Share &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘ ⇧ S&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Chat Panel &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘ ⇧ H&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Toggle View &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘ ⇧ W&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VMware Fusion Examples&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Take Snapshot &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘ ⇧ S&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Show Snapshots &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘ S&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Settings &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘ E&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Get Info &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘ I&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Show Machine Library &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⇧ ⌘ L&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;vscode&quot;&gt;VSCode&lt;/h5&gt;

&lt;p&gt;For VSCode I have a few shortcuts, and some script invocations, but I also want to illustrate the ability to go to a specific profile via a Stream Deck configuration. While when VSCode is in the foreground, its profile will be on the Stream Deck, but you can override this by adding a link to another profile, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream Deck &amp;gt; Switch Profile&lt;/code&gt;. So I have created a git specific profile for a lot of wrapping around &lt;a href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt; and &lt;a href=&quot;https://cli.github.com/&quot;&gt;gh&lt;/a&gt;. So while in my VSCode profile I can quickly switch to Git items, and then switch back (via the same configuration, just VSCode profile) on the fly as needed. This is handy as using VSCode’s internal terminal wouldn’t trigger Stream Deck in the same way as say iTerm or Terminal.app. To visualize it as above, it would look something like this:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;default_profile
├── fusion(launch app)
├── vscode(launch app)
└── zoom(launch app)

vscode
├── snapshot
├── library
└── git(switch to profile)

git
├── branch_create
├── sync_main
├── pr
├── stage
└── vscode(switch to profile)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There is also the ability to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream Deck &amp;gt; Switch Profile&lt;/code&gt; and set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next Profile&lt;/code&gt; and this button will take you to the next profile as configured in your Stream Deck software.&lt;/p&gt;

&lt;p&gt;There is also a VSCode “Action” pack available via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;More Actions...&lt;/code&gt; that includes some functionality like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create Terminal&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open Folder&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Insert Snippet&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;what-a-wondrous-boat-ride&quot;&gt;“What a wondrous boat ride…”&lt;/h4&gt;

&lt;p&gt;This is just the surface of what Stream Deck’s software itself can do, I haven’t even touched on all of the &lt;a href=&quot;https://help.elgato.com/hc/en-us/articles/360028241291-Elgato-Stream-Deck-Quick-Start-Guide&quot;&gt;built in features&lt;/a&gt;, the additional “Actions” available, or the community created plugins (&lt;a href=&quot;https://streamdeck-plugins.com/&quot;&gt;Unofficial Stream Deck Plugins&lt;/a&gt;) implemented via the &lt;a href=&quot;https://developer.elgato.com/documentation/stream-deck/sdk/overview/&quot;&gt;SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As for me, while it has some basic macro functionality, it mostly acts as a trigger for far more complex automation in underlying Python scripts or Keyboard Maestro workflows. Who knows, maybe I will dive into that in another post… no promises there.&lt;/p&gt;

&lt;p&gt;And &lt;em&gt;any&lt;/em&gt; icon you want to use, can even be a gif. &lt;em&gt;chef kiss&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;references-and-additional-links&quot;&gt;References and Additional Links&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.elgato.com/en/gaming/stream-deck&quot;&gt;Stream Deck&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://streamdeck-plugins.com/&quot;&gt;Unofficial Stream Deck Plugins&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://github.com/Corcules/KMlink&quot;&gt;Corcules/KMlink&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.keyboardmaestro.com/main/&quot;&gt;Keyboard Maestro&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.zoom.us/hc/en-us/articles/205683899-hot-keys-and-keyboard-for-zoom&quot;&gt;Zoom Shortcuts&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cli.github.com/&quot;&gt;Github CLI&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://developer.elgato.com/documentation/stream-deck/sdk/overview/&quot;&gt;Stream Deck SDK&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://help.elgato.com/hc/en-us/articles/360028241291-Elgato-Stream-Deck-Quick-Start-Guide&quot;&gt;Stream Deck Quick Start&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 27 Feb 2021 11:11:11 +0000</pubDate>
        <link>lucasjhall.github.io/automation/macos/fun/2021/02/27/intro-to-stream-deck.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/automation/macos/fun/2021/02/27/intro-to-stream-deck.html</guid>
        
        
        <category>automation</category>
        
        <category>macOS</category>
        
        <category>fun</category>
        
      </item>
    
      <item>
        <title>Welcome to Github!</title>
        <description>&lt;p&gt;No more scroll back? I am working on determining what, if anything, will be brought to this site from my original blog on &lt;a href=&quot;https://lucasjacobhall.wordpress.com/&quot;&gt;wordpress.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The future is static, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;markdown&lt;/code&gt;.&lt;/p&gt;
</description>
        <pubDate>Mon, 25 Jan 2021 10:10:00 +0000</pubDate>
        <link>lucasjhall.github.io/intro/2021/01/25/welcome.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/intro/2021/01/25/welcome.html</guid>
        
        
        <category>Intro</category>
        
      </item>
    
      <item>
        <title>WiPi - A RaspberryPi Hotspot To Go</title>
        <description>&lt;p&gt;tl;dr: A Raspberry Pi with a cellular HAT to act as a private internet access
point and portable local network.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2020/08/img_3440.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Taking paternity leave for the month of July to introduce our newborn to
extended family introduced a lot of hurdles. With all going on in the
world not many could be solved with some DIY tech. Being on the road for
a month I thought it’d be rad to have a WiFi hotspot to connect to with
a bit more configuration than just an iPhone.&lt;/p&gt;

&lt;p&gt;In short the goals for the project were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Portable WiFi Hotspot&lt;/li&gt;
  &lt;li&gt;Standalone internet connectivity&lt;/li&gt;
  &lt;li&gt;Bring our WiFi devices on the road and not have to setup or count on
other folks / hotel / public WiFi access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Here&apos;s the gist...&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-build&quot;&gt;The Build&lt;/h2&gt;

&lt;h3 id=&quot;hardware&quot;&gt;Hardware&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.raspberrypi.org/blog/raspberry-pi-4-on-sale-now-from-35/&quot;&gt;Raspberry Pi 4B
2019&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;I chose a Raspberry Pi 4B for the base board for no other reason
that I wanted to get an 4GB model for tinkering previously and
had it to repurpose for this build.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sixfab.com/product/raspberry-pi-4g-lte-modem-kit/&quot;&gt;Raspberry Pi 4G/LTE HAT
Kit&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Bundled from SixFab
        &lt;ul&gt;
          &lt;li&gt;Raspberry Pi 3G/4G &amp;amp; LTE Base HAT1&lt;/li&gt;
          &lt;li&gt;Quectel: EC25 Mini PCle 4G/LTE Module&lt;/li&gt;
          &lt;li&gt;1LTE Full Band PCB Antenna – u.FL Plug – 100mm&lt;/li&gt;
          &lt;li&gt;1LTE - GNSS Dual u.FL Antenna - 100mm&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B0002INQT2/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&amp;amp;psc=1&quot;&gt;Pelican 1200
w/foam&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B07D25RSWX/ref=ppx_yo_dt_b_asin_title_o09_s00?ie=UTF8&amp;amp;psc=1&quot;&gt;Anker PowerPortCube USB
Cube&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B07F6HVFH4/ref=ppx_yo_dt_b_asin_title_o09_s00?ie=UTF8&amp;amp;psc=1&quot;&gt;Journeyman Pro AC Pass
through&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B01N5RCZQH/ref=ppx_yo_dt_b_asin_title_o06_s00?ie=UTF8&amp;amp;psc=1&quot;&gt;TP Link
AC750&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B076H3SRXG/ref=ppx_yo_dt_b_asin_title_o00_s01?ie=UTF8&amp;amp;psc=1&quot;&gt;Wyze Cam 1080p HD
Indoor&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://support.wyzecam.com/hc/en-us/articles/360026245231-Wyze-Cam-RTSP&quot;&gt;Custom Firmware Supporting
RTSP&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-design&quot;&gt;The Design&lt;/h3&gt;

&lt;p&gt;Over all pretty simple idea, a local wifi / lan with its own independent
internet connection.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2020/08/untitled-diagram.png?w=681&quot; alt=&quot;&quot; /&gt;{.wp-image-2333}&lt;/p&gt;

&lt;p&gt;Few items of note here&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I have never done anything with cellular providers / HATS etc and a
Pi platform&lt;/li&gt;
  &lt;li&gt;I wrongly assumed most cameras have rtsp support out of box&lt;/li&gt;
  &lt;li&gt;I wrongly assumed modern applications are designed to be data
conscious&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;setting-up-the-pi&quot;&gt;Setting Up the Pi&lt;/h2&gt;

&lt;h3 id=&quot;os&quot;&gt;OS&lt;/h3&gt;

&lt;p&gt;Flashed and installed &lt;a href=&quot;https://www.raspberrypi.org/downloads/&quot;&gt;Raspberry Pi
OS&lt;/a&gt; Latest (formerly known as
Raspbian)&lt;/p&gt;

&lt;h3 id=&quot;cellular-hat&quot;&gt;Cellular HAT&lt;/h3&gt;

&lt;p&gt;SixFabHas Links to &quot;&lt;a href=&quot;https://docs.sixfab.com/docs/raspberry-pi-3g-4g-lte-base-hat-introduction&quot;&gt;Complete
Documentation&lt;/a&gt;&quot;
on the &lt;a href=&quot;https://sixfab.com/product/raspberry-pi-4g-lte-modem-kit/&quot;&gt;Cellular Modem
Kit&lt;/a&gt;&apos;s Page.
I will say I was impressed that at the documentation SixFab Provides.
Depending on your IoT cellular provider, Sixfab ships with a SIM from
Twilio, you can follow the
&lt;a href=&quot;https://docs.sixfab.com/docs/ppp-installer-for-base-shield&quot;&gt;PPP&lt;/a&gt;/&lt;a href=&quot;https://docs.sixfab.com/docs/qmi-interface-with-base-shield&quot;&gt;QMI&lt;/a&gt;
tutorials respectively.&lt;/p&gt;

&lt;p&gt;I was kind of like, &quot;Wait thats it?&quot; the Twilio SIM being QMI with
some inherent trust didn&apos;t need anything past the QMI tutorials Sixfab
provided.&lt;/p&gt;

&lt;h3 id=&quot;wifi&quot;&gt;WiFi&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://raspap.com/&quot;&gt;RaspAP&lt;/a&gt;,
&lt;a href=&quot;https://github.com/billz/raspap-webgui&quot;&gt;billz/raspap-webgui&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;RaspAP lets you quickly get a WiFi access point up and running to
share the connectivity of many popular &lt;a href=&quot;https://raspap.com/#supported-operating-systems&quot;&gt;Debian-based
devices&lt;/a&gt;, including
the Raspberry Pi. Our popular &lt;a href=&quot;https://raspap.com/#quick-installer&quot;&gt;Quick
installer&lt;/a&gt; creates a known-good
default configuration that “just works” on all current Raspberry Pis
with onboard wireless. A responsive interface gives you control over
the relevant services and networking options. Advanced DHCP settings,
OpenVPN client support, SSL, security audits, themes and multilingual
options are included.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://raspap.com/&quot;&gt;https://raspap.com/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Again, not a lot to say here, another great tool that &quot;just works&quot;.&lt;/p&gt;

&lt;h3 id=&quot;nebula&quot;&gt;Nebula&lt;/h3&gt;

&lt;p&gt;The Pi hosting the WiFi / Cellular connections is also enrolled in my
&lt;a href=&quot;https://github.com/slackhq/nebula&quot;&gt;nebula&lt;/a&gt; network, I talk a bit more
about nebula in a previous
&lt;a href=&quot;https://lucasjhall.com/2020/02/16/nebula-getting-started/&quot;&gt;post&lt;/a&gt;. This
allows for this pi to reach other nodes that are also connected to the
network, it does &lt;em&gt;not&lt;/em&gt; treat the local lan as an extension of the nebula
network. Once enrolled I was able to ping/ssh into the Pi via its
cellular network, as well as reach any other resources as allowed on the
nebula network. &lt;em&gt;Very slick.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;extending-wifi-via-ac750&quot;&gt;Extending WiFi via AC750&lt;/h3&gt;

&lt;p&gt;The TP-Link AC750 (TL-WR902AC) has built in &lt;a href=&quot;https://www.tp-link.com/us/user-guides/tl-wr902ac_v3/&quot;&gt;functionality for various
Wifi modes.&lt;/a&gt;
Setting up to extend an existing SSID, then moving it away from the
Pi&apos;s location would allow for just that, an extended WiFi signal.&lt;/p&gt;

&lt;h2 id=&quot;assembly&quot;&gt;Assembly&lt;/h2&gt;

&lt;p&gt;::: {.wp-block-jetpack-slideshow .aligncenter effect=”slide”}
::: {.wp-block-jetpack-slideshow_container .swiper-container}&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2020/08/img_3221.jpg?w=739&quot; alt=&quot;Cut and wired the Anker Cube into the Journeyman pass
through.&quot; /&gt;{.wp-block-jetpack-slideshow_image
.wp-image-2356}&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2020/08/img_3446-2.jpg?w=739&quot; alt=&quot;Hid the antenna&apos;s behind the
foam.&quot; /&gt;{.wp-block-jetpack-slideshow_image
.wp-image-2352}&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2020/08/img_3861-1.jpg?w=739&quot; alt=&quot;Passthrough on rear of
case.&quot; /&gt;{.wp-block-jetpack-slideshow_image
.wp-image-2349}&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2020/08/img_3859-1.jpg?w=739&quot; alt=&quot;Camera storage, cube access with foam
inserted.&quot; /&gt;{.wp-block-jetpack-slideshow_image
.wp-image-2350}&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2020/08/img_3857-2-1.jpg?w=739&quot; alt=&quot;The Pi is mounted to the case with plastic standoffs passed
through the
foam.&quot; /&gt;{.wp-block-jetpack-slideshow_image
.wp-image-2351}&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2020/08/img_3444-2-1.jpg?w=739&quot; alt=&quot;Out standing (in a) field
shot.&quot; /&gt;{.wp-block-jetpack-slideshow_image
.wp-image-2353}&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;[]{.wp-block-jetpack-slideshow_button-prev .swiper-button-prev
.swiper-button-white}[]{.wp-block-jetpack-slideshow_button-next
.swiper-button-next
.swiper-button-white}[]{.wp-block-jetpack-slideshow_button-pause}&lt;/p&gt;

&lt;p&gt;::: {.wp-block-jetpack-slideshow_pagination .swiper-pagination .swiper-pagination-white}
:::
:::
:::&lt;/p&gt;

&lt;h2 id=&quot;thoughts&quot;&gt;Thoughts&lt;/h2&gt;

&lt;p&gt;All in all this was a fun project, not much use on the day to to day,
but as a travel companion its a rad little setup. It was a fun project
that will more than likely get dismantled into other projects!&lt;/p&gt;
</description>
        <pubDate>Sun, 09 Aug 2020 00:20:10 +0000</pubDate>
        <link>lucasjhall.github.io/2020/08/09/wipi-a-raspberrypi-hotspot-to-go.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/2020/08/09/wipi-a-raspberrypi-hotspot-to-go.html</guid>
        
        
      </item>
    
      <item>
        <title>Nebula - Getting Started </title>
        <description>&lt;p&gt;Open source mesh networking- cool!&lt;/p&gt;

&lt;h2 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h2&gt;

&lt;p&gt;I had no part in the development or implementation of this tool. This
blog post explores the tool from the perspective of the f/oss tool as it
appears on Github for a home lab scenario, and what I&apos;ve learned from
getting it up and running in a said scenario.&lt;/p&gt;

&lt;p&gt;This guide will use Ubuntu, UFW, so if those aren&apos;t your favorites,
sorry but you&apos;ll still get the gist.&lt;/p&gt;

&lt;h2 id=&quot;what-is-nebula&quot;&gt;What &lt;em&gt;Is&lt;/em&gt; Nebula&lt;/h2&gt;

&lt;p&gt;Most likely you know, or you wouldn&apos;t found your way here. But in  the
developers words:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Nebula is a scalable overlay networking tool with a focus on
performance, simplicity and security. It lets you seamlessly connect
computers anywhere in the world. Nebula is portable, and runs on
Linux, OSX, and Windows. (Also: keep this quiet, but we have an early
prototype running on iOS). It can be used to connect a small number of
computers, but is also able to connect tens of thousands of computers.&lt;/p&gt;

  &lt;p&gt;Nebula incorporates a number of existing concepts like encryption,
security groups, certificates, and tunneling, and each of those
individual pieces existed before Nebula in various forms. What makes
Nebula different to existing offerings is that it brings all of these
ideas together, resulting in a sum that is greater than its individual
parts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The rest of this post will explore getting a few nodes enrolled and
talking to one another, based off the public documentation &quot;&lt;a href=&quot;https://github.com/slackhq/nebula#getting-started-quickly&quot;&gt;Getting
Started
(quickly)&lt;/a&gt;&quot;.&lt;/p&gt;

&lt;h2 id=&quot;getting-started-less-quickly-with-some-commentary-and-opinions&quot;&gt;Getting Started, &lt;em&gt;less&lt;/em&gt; quickly, with some commentary and opinions&lt;/h2&gt;

&lt;h3 id=&quot;planning&quot;&gt;Planning&lt;/h3&gt;

&lt;h4 id=&quot;lighthouse&quot;&gt;Lighthouse&lt;/h4&gt;

&lt;p&gt;You need a Lighthouse node, something that is reachable via the public
internet and will point nodes to one another.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[Nebula lighthouses allow nodes to find each other, anywhere in the
world. A lighthouse is the only node in a Nebula network whose IP
should not
change.]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}[[ ]{.Apple-converted-space}]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;[[The
]{.Apple-converted-space}]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}recommendation[[
on the Github page
is:]{.Apple-converted-space}]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[Nebula lighthouses allow nodes to find each other, anywhere in the
world. A lighthouse is the only node in a Nebula network whose IP
should not
change.]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}[[ ]{.Apple-converted-space}]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which is[ sound, and very beneficial if you will have multiple nodes
]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}across[
locations or cloud
]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}providers[.
]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}&lt;/p&gt;

&lt;p&gt;[This node will
need:]{style=”color:#24292e;font-family:-apple-system, BlinkMacSystemFont,;”}&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Reachable via the internet&lt;/li&gt;
  &lt;li&gt;Have the nebula port exposed, default udp/4242&lt;/li&gt;
  &lt;li&gt;Console/ssh access for configuration etc&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;nodes&quot;&gt;Nodes&lt;/h4&gt;

&lt;p&gt;Any device that will connect to the Nebula network will be a node,
including the Lighthouse, however the lighthouse&apos;s config differs
slightly and will be touch on separately.&lt;/p&gt;

&lt;p&gt;These nodes can be any supported OS, the Nebula binaries are available
via the github to &lt;a href=&quot;https://github.com/slackhq/nebula/releases&quot;&gt;download&lt;/a&gt;
or compile.&lt;/p&gt;

&lt;p&gt;For sake illustration one node and lighthouse is required, though I&apos;d
recommend two nodes to better demonstrate the peer to peer capabilities.&lt;/p&gt;

&lt;h4 id=&quot;documentation--planning&quot;&gt;Documentation &amp;amp; Planning&lt;/h4&gt;

&lt;p&gt;Node Organization&lt;/p&gt;

&lt;p&gt;A good ole spreadsheet of the the IP range you choose will be handy for
this- the default network in the example config is 192.168.100.1, which
could work for you but it could be anything. I made a spreadsheet with
192.168.100.10.24 in column A, hostname in column B and C for noting any
group associations. For sake of a home setup this was a solid base
document to help me keep track of what device is assigned what IP etc.
Knowing these values and associations are handy as you look to reference
your infrastructure in the future, especially if its decentralized and
all not directly accessible on your LAN.&lt;/p&gt;

&lt;p&gt;Groups can be used to limit in and outbound traffic for nodes. Outbound
traffic defaults to &lt;strong&gt;any,&lt;/strong&gt; and inbound to specific ports and
protocols. In our examples we will be looking at a few different
services, SSH, Netdata and Filebeat in a later post. Each of these have
a particular port / protocol associated with them, and we could make
specific rules around each. For illustrations on this post we will be
sticking-with icmp. An example of making an unbound rule allowing icmp
from any nebula host would look something like:&lt;/p&gt;

&lt;p&gt;[gist
https://gist.github.com/lucasjhall/0e5baf2abfc0c106430677c407127a89 /]&lt;/p&gt;

&lt;p&gt;You can look at the &lt;a href=&quot;https://github.com/slackhq/nebula/blob/1592da921de1e6b363fcc70d887ffa5cdc0aab74/examples/config.yml#L155-L172&quot;&gt;example
config&lt;/a&gt;
as well for some other ideas.&lt;/p&gt;

&lt;h2 id=&quot;enough-chat-lets-go&quot;&gt;Enough Chat, Let&apos;s Go&lt;/h2&gt;

&lt;p&gt;Okay, so we will end up with the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Lighthouse1&lt;/strong&gt; (Ubuntu, cloud based node, routable ip)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Node1&lt;/strong&gt; (Ubuntu, hardware based node, internal LAN, in my case a
Pi 3B +)&lt;/li&gt;
  &lt;li&gt;CA (its you, not specifically a device, as this is a mutual trust
environment)&lt;/li&gt;
  &lt;li&gt;working area, specific to &lt;strong&gt;mgmt1&lt;/strong&gt; and not (at this point) enrolled
in nebula&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will be taking all of our actions from &lt;strong&gt;mgmt1&lt;/strong&gt; but will not be
installing Nebula on this device for this example.&lt;/p&gt;

&lt;p&gt;So that looks like:&lt;/p&gt;

&lt;p&gt;[gist
https://gist.github.com/lucasjhall/af232532d7609e1e76d23a8a3c7e911a ]&lt;/p&gt;

&lt;h3 id=&quot;working-area&quot;&gt;Working Area&lt;/h3&gt;

&lt;p&gt;This will all be on mgmt1 a place to create and manage all the items for
the nodes you&apos;re enrolling, also where all the sensitive stuff will
live, until you deploy and remove it, ephemeralness of this stuff is up
to you-&lt;/p&gt;

&lt;p&gt;I made a directory structure that was similar to:&lt;/p&gt;

&lt;p&gt;[gist
https://gist.github.com/lucasjhall/77e4c3dbfa9a958a3c5de85d6b81459c /]&lt;/p&gt;

&lt;h4 id=&quot;lets-setup-our-ca&quot;&gt;Let&apos;s Setup Our CA&lt;/h4&gt;

&lt;p&gt;I got the nebula-cert binary that was needed for the specific OS of my
&lt;strong&gt;mgmt1&lt;/strong&gt; and placed it in nebula/binaries/, if all the devices are
Ubuntu with the same architecture then this is all pretty easy.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./nebula-cert ca -name &quot;Myorganization, Inc&quot;&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This will create files
named[ ]{.Apple-converted-space}&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.key&lt;/code&gt;[ ]{.Apple-converted-space}and[ ]{.Apple-converted-space}&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.cert&lt;/code&gt;[ ]{.Apple-converted-space}in
the current directory.
The[ ]{.Apple-converted-space}&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.key&lt;/code&gt;[ ]{.Apple-converted-space}file
is the most sensitive file you&apos;ll create, because it is the key used
to sign the certificates for individual nebula nodes/hosts. Please
store this file somewhere safe, preferably with strong encryption.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The ca.key is the signing key for the CA, so use this when you&apos;re
creating nodes, but otherwise store it somewhere else, a vault or a
password manager that supports encrypting attached files.&lt;/p&gt;

&lt;p&gt;Once the ca.key is created you can proceed with generating nodes needed
credentials. During this command you&apos;ll reference your planned naming
schema afore mentioned in the spreadsheet.&lt;/p&gt;

&lt;h4 id=&quot;lighthouse-on-mgmt1&quot;&gt;Lighthouse (on mgmt1)&lt;/h4&gt;

&lt;p&gt;The docs have this example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./nebula-cert sign -name &quot;lighthouse1&quot; -ip &quot;192.168.100.1/24&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is telling nebula-cert to create an identity and sign it where the
name is &quot;lighthouse1&quot; and the up will be &quot;192.168.100.1&quot; and it
will, by default be in &lt;strong&gt;no groups&lt;/strong&gt;. As lighthouses can be used to for
simply pointing nodes to one another this can be beneficial.&lt;/p&gt;

&lt;p&gt;Or you can treat a cloud based lighthouse as a regular node and run
additional service and tag it with any groups that may be applicable.&lt;/p&gt;

&lt;p&gt;I then placed all the needed pieces into the lighthouse1 directory:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;cp the &lt;strong&gt;ca.cert&lt;/strong&gt; . (generated in the CA step)&lt;/li&gt;
  &lt;li&gt;lighthouse1.crt&lt;/li&gt;
  &lt;li&gt;lighthouse1.key&lt;/li&gt;
  &lt;li&gt;cp example
&lt;a href=&quot;https://github.com/slackhq/nebula/blob/master/examples/config.yml&quot;&gt;config.yaml&lt;/a&gt;
. (from the public or cloned repo)&lt;/li&gt;
  &lt;li&gt;nebula binary (&lt;a href=&quot;https://github.com/slackhq/nebula/releases&quot;&gt;from
github&lt;/a&gt; / compiled)&lt;/li&gt;
  &lt;li&gt;nebula.service (&lt;a href=&quot;https://github.com/slackhq/nebula/tree/master/examples/service_scripts&quot;&gt;from
github&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;As an aside I was initially naming config.yml to {host}.yml, this was
short sighted as you start automate the starting of the process via
launchctl or systemd this makes the template of the config.yml more
cumbersome, changing the nebula.service for every host, NOPE. (Unless
you&apos;re using a config management tool, Chef, Puppet, Salt et al, but I
digress).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Configure the Lighthouse1 Nebula config.yaml per &lt;a href=&quot;https://github.com/slackhq/nebula#5-configuration-files-for-each-host%20https://github.com/slackhq/nebula%235-configuration-files-for-each-host&quot;&gt;host config
notes&lt;/a&gt;,
with special attention to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;On the lighthouse node, you&apos;ll need to
    &lt;blockquote&gt;
      &lt;p&gt;ensure[ ]{.Apple-converted-space}&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;am_lighthouse: true&lt;/code&gt;[ ]{.Apple-converted-space}is
set.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;####&lt;/p&gt;

&lt;h4 id=&quot;lighthouse-host-configuration-on-lighthouse1&quot;&gt;Lighthouse Host Configuration (on lighthouse1)&lt;/h4&gt;

&lt;p&gt;With your choice of service provider get a machine up and patched. I
chose Ubuntu 18.04 Server because it was what I was most familiar with-
you do you.&lt;/p&gt;

&lt;p&gt;I accessed my machine via the console, patched, etc. (You may want SSH
or some means to transport files to the device)&lt;/p&gt;

&lt;p&gt;I then setup my UFW to basically deny all inbound, then add a rule to
except for 4242, our nebula port. Via a ufw allow 4242/udp. If you&apos;re
not familiar with UFW you can read more
&lt;a href=&quot;https://help.ubuntu.com/community/UFW&quot;&gt;here&lt;/a&gt;. Or choose the firewall of
your choice. Heck have at iptables yourself, it&apos;s your life.&lt;/p&gt;

&lt;p&gt;You can now move the entirety of that nebula/lighthouse1/ to your
lighthouse1 machine, an scp -r to ~/ or whatever your preference.&lt;/p&gt;

&lt;p&gt;From here you could try and start nebula per the docs, but remember the
config is pointing to /etc/nebula/ for pki items and the like. So lets
just put everything where it needs to be- And this is where we want all
the pieces to end up here:&lt;/p&gt;

&lt;p&gt;[gist
https://gist.github.com/lucasjhall/b75399d35e4951e10a2d75e73e273ab7 /]&lt;/p&gt;

&lt;p&gt;Run the binary pointed to the proper config and if no errors are
reported the service has stated. So at this point our lighthouse is up
and running, we can now configure our client.&lt;/p&gt;

&lt;h4 id=&quot;node1-on-mgmt1&quot;&gt;Node1 (on mgmt1)&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./nebula-cert sign -name &quot;node1&quot; -ip &quot;192.168.100.2/24&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Configure the node Nebula config.yaml per &lt;a href=&quot;https://github.com/slackhq/nebula/blob/master/examples/config.yml&quot;&gt;host config
notes&lt;/a&gt;,
with special attention to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;[am_lighthouse]{.pl-ent}&lt;/strong&gt;: [false]{.pl-c1}&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;[static_host_map]{.pl-ent}&lt;/strong&gt;:, where IP is the nebula interface ip
followed by real, and reachable IP of the lighthouse,
[[&quot;]{.pl-pds}[192.168.100.1]{.pl-ent}[&quot;]{.pl-pds}]{.pl-s}:
[[&quot;100.64.22.11:4242&quot;]]{.pl-s}&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;[hosts]{.pl-ent}&lt;/strong&gt;: where we list ip of the lighthouse on the
nebula network, 192.168.100.1&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;reference the cloned config for in line notes on the above&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As before this is telling nebula-cert to create an identity and sign it
where the name is &quot;node1&quot; and the up will be &quot;192.168.100.2&quot; and it
will, by default be in &lt;strong&gt;no groups&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I then placed all the needed pieces into the node1 directory:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;cp the &lt;strong&gt;ca.cert&lt;/strong&gt; . (generated in the CA step)&lt;/li&gt;
  &lt;li&gt;node1.crt&lt;/li&gt;
  &lt;li&gt;node1.key&lt;/li&gt;
  &lt;li&gt;cp example
&lt;a href=&quot;https://github.com/slackhq/nebula/blob/master/examples/config.yml&quot;&gt;config.yaml&lt;/a&gt;
. (from the public or cloned repo)&lt;/li&gt;
  &lt;li&gt;nebula binary (&lt;a href=&quot;https://github.com/slackhq/nebula/releases&quot;&gt;from
github&lt;/a&gt; / compiled)
(ensure its for your architecture)&lt;/li&gt;
  &lt;li&gt;nebula.service (&lt;a href=&quot;https://github.com/slackhq/nebula/tree/master/examples/service_scripts&quot;&gt;from
github&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;node1-configuration-on-node1&quot;&gt;Node1 Configuration (on node1)&lt;/h4&gt;

&lt;p&gt;Rinse and repeat with the steps in the lighthouse1 section.&lt;/p&gt;

&lt;p&gt;I accessed my machine via the console, patched, etc. (You may want SSH
or some means to transport files to the device.&lt;/p&gt;

&lt;p&gt;You can now move the entirety of that nebula/node1/ to your lighthouse1
machine, an scp -r to ~/ or whatever your preference.&lt;/p&gt;

&lt;p&gt;From here you could try and start nebula per the docs, but remember the
config is pointing to /etc/nebula/ for pki items and the like. So lets
just put everything where it needs to be- And this is where we want all
the pieces to end up here:&lt;/p&gt;

&lt;p&gt;Run the binary pointed to the proper config and if no errors are
reported the service has stated. So at this point our node1 is up and
running, and we should see some handshakes showing we have now connected
our node1/lighthouse.&lt;/p&gt;

&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;

&lt;p&gt;At this point if you have no errors you can ping one host via the nebula
interface from another, this demonstrates the connectivity over the neb0
or whatever configure interface.&lt;/p&gt;

&lt;h2 id=&quot;future-posts&quot;&gt;Future Posts&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Explore groups and Example Services&lt;/li&gt;
  &lt;li&gt;Nebula&apos;s built in sshd&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;questions&quot;&gt;Questions&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/slackhq/nebula#getting-started-quickly&quot;&gt;Github
QuickStart&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://join.slack.com/t/nebulaoss/shared_invite/enQtOTA5MDI4NDg3MTg4LTkwY2EwNTI4NzQyMzc0M2ZlODBjNWI3NTY1MzhiOThiMmZlZjVkMTI0NGY4YTMyNjUwMWEyNzNkZTJmYzQxOGU&quot;&gt;Reach out on the Nebula f/oss
Slack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/slackhq/nebula/issues&quot;&gt;File an issue on Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 16 Feb 2020 23:10:00 +0000</pubDate>
        <link>lucasjhall.github.io/talks/2020/02/16/nebula-getting-started.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/talks/2020/02/16/nebula-getting-started.html</guid>
        
        
        <category>talks</category>
        
      </item>
    
      <item>
        <title>Re: Changes</title>
        <description>&lt;p&gt;Last year I picked up and left Bellingham to take a CPE role at
&lt;a href=&quot;https://slack.com/&quot;&gt;Slack&lt;/a&gt;. I worked out of the Denver office and bit-
and now I am back in Bellingham. Slack allowed me to go full time remote
(rad) and my wife and I moved back to be close to friends during our
next phase of life.&lt;/p&gt;

&lt;p&gt;Hopefully I will have more desire to post soon. Some cool stuff I&apos;d
love to share, perhaps for the impending conference season... ✌🏻&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Dec 2019 20:21:47 +0000</pubDate>
        <link>lucasjhall.github.io/life/2019/12/20/re-changes.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/life/2019/12/20/re-changes.html</guid>
        
        
        <category>life</category>
        
      </item>
    
      <item>
        <title>Ch... ch... changes</title>
        <description>&lt;p&gt;I am excited to announce that I have accepted a Client Platform Engineer
position at &lt;a href=&quot;https://slack.com/&quot;&gt;Slack&lt;/a&gt;{.profileLink} . My family will
be relocating to Denver CO at the end of January. We are deeply saddened
to leave the Pacific Northwest, Bellingham, and the incredible community
here, the #Cascadia Macadmins community, and the &lt;a href=&quot;https://www.meetup.com/Seattle-Apple-Admins/&quot;&gt;Apple Admins of
Seattle and the Great
Northwest.&lt;/a&gt;{.groupHomeHeader-groupNameLink} On
the flip side we are so excited to see what awaits us in Denver and
Colorful Colorado holds...&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cob.org/&quot;&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2019/01/Bellingham_City_Flag_cropped.png&quot; alt=&quot;Bellingham_City_Flag_(cropped)&quot; /&gt;{.alignnone
.wp-image-2276 width=”304” height=”182”}&lt;/a&gt;         
       
&lt;a href=&quot;https://www.denver.org/&quot;&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2019/01/Flag_of_Denver_Colorado.svg_.png&quot; alt=&quot;Flag_of_Denver,_Colorado.svg&quot; /&gt;{.alignnone
.wp-image-2277 width=”300” height=”180”}&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 01 Jan 2019 19:10:23 +0000</pubDate>
        <link>lucasjhall.github.io/life/2019/01/01/ch-ch-changes.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/life/2019/01/01/ch-ch-changes.html</guid>
        
        
        <category>life</category>
        
      </item>
    
      <item>
        <title>&quot;Touchless&quot; macOS deployment utilizing JAMF Connect and InstallApplications</title>
        <description>&lt;p&gt;This post will take a high level view of using and MDM
(&lt;a href=&quot;https://simplemdm.com/&quot;&gt;SimpleMDM)&lt;/a&gt;, &lt;a href=&quot;https://www.jamf.com/products/jamf-connect/&quot;&gt;JAMF
Connect&lt;/a&gt; and
&lt;a href=&quot;https://github.com/erikng/installapplications&quot;&gt;InstallApplications&lt;/a&gt; for
&quot;touchless&quot; deployment of macOS.  This deployment method uses Munki as
the primary tool for package management, and has been in place since the
&quot;NoMAD Days.&quot;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Big assumptions&lt;/strong&gt;. You are familiar with MDM and the process
surrounding macOS, and have some knowledge
of &lt;a href=&quot;https://github.com/erikng/installapplications&quot;&gt;InstallApplications&lt;/a&gt;.
You are using Okta as an IdP, have the needed services and are
paying/testing &lt;a href=&quot;https://www.jamf.com/products/jamf-connect/&quot;&gt;JAMF
Connect&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-workflow&quot;&gt;The workflow:&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2018/11/untitled2.png&quot; alt=&quot;untitled(2)&quot; /&gt;{.alignnone
.size-full .wp-image-2258 width=”980” height=”3065”}&lt;/p&gt;

&lt;h2 id=&quot;whats-happening-here&quot;&gt;Whats happening here...&lt;/h2&gt;

&lt;p&gt;This was born out of the following scope:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&quot;Touchless&quot; Deployment of macOS&lt;/li&gt;
  &lt;li&gt;Manage user accounts and provisioning&lt;/li&gt;
  &lt;li&gt;Desire for 2FA for login/SSO&lt;/li&gt;
  &lt;li&gt;Users are &quot;managed&quot;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;user-gets-a-machine&quot;&gt;User gets a machine&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;They power on and... &lt;/strong&gt; Assuming your MDM workflow is setup correctly
and you&apos;ve configured Install Applications for your needs...&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Munki (and Middleware)&lt;/li&gt;
  &lt;li&gt;Jamf Connect (Login+ Okta, LaunchAgent and Sync)&lt;/li&gt;
  &lt;li&gt;Images/Assets for branding&lt;/li&gt;
  &lt;li&gt;DEPNotify&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the initial setup process, the machine will load those
aforementioned tools and assets prior to the login screen, giving your
user a branded JAMF Connect Login screen.  I&apos;ve completely disabled the
ability to create an account on the machine via the MDM.&lt;/p&gt;

&lt;h2 id=&quot;user-account-creation&quot;&gt;User Account Creation&lt;/h2&gt;

&lt;h3 id=&quot;account-setup-in-okta&quot;&gt;Account setup in Okta&lt;/h3&gt;

&lt;p&gt;This is controlled through JAMF Connects OIDC groups as outlined in the
&lt;a href=&quot;https://files.nomad.menu/NoMADLoginPlusAdminGuide.pdf&quot;&gt;NoMAD Login+ Admin
Guide&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div&gt;

Using OpenID Connect Applications to Manage Authentication NoMAD
Login+Okta can use native applications created in the Okta admin
dashboard to shape the login process. This allows for live management
of who can authenticate at the loginwindow from the Okta admin
dashboard without having to update configurations on the local
machine. Also this makes it easy to work NoMAD Login+ into your
existing Okta account creation workflow.

&lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;Woot! So even if a user account exists, it can only login if it is in
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OIDCAccessClientID&lt;/code&gt; group.  This is also extended by
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OIDCAdminClientID&lt;/code&gt; group which allows the user to be admin when they
are logged in, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OIDCSecondaryLoginClientID&lt;/code&gt; which is used to
determine who can create local accounts after an initial user has been
created.&lt;/p&gt;

&lt;p&gt;JAMF Connect Also allows for the managed setting of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DenyLocal&lt;/code&gt; which
will force a login through Okta (there are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LocalFallback&lt;/code&gt; options which
won&apos;t be explored here).&lt;/p&gt;

&lt;p&gt;As well... the Login Window mechanism knows if a user is flagged for a
change of password and will redirect to an Okta captive portal.&lt;/p&gt;

&lt;p&gt;This forces a user to &lt;strong&gt;change their password&lt;/strong&gt; and &lt;strong&gt;setup their
multifactor&lt;/strong&gt; because the login window redirects to predefined Okta url
where the Okta account settings are enforced.&lt;/p&gt;

&lt;h3 id=&quot;machine-login---local-account-creation&quot;&gt;Machine Login - Local Account Creation&lt;/h3&gt;

&lt;p&gt;Once the user has setup their MFA and reset their password via Okta, the
user can then login to their machine, whichis forced to auth through
Okta and use a MFA, for example it could look like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2018/11/screen-shot-2018-11-21-at-9-25-16-am.png&quot; alt=&quot;Screen Shot 2018-11-21 at 9.25.16
AM&quot; /&gt;{.alignnone
.wp-image-2268 width=”260” height=”185”}&lt;/p&gt;

&lt;p&gt;After authenticating with their username/password then verifying their
mfa JAMF Connect creates the local user as prescribed by the OIDC groups
as assigned to the user in Okta when the user logins to the machine for
the first time.&lt;/p&gt;

&lt;h2 id=&quot;machine-configuration&quot;&gt;Machine Configuration&lt;/h2&gt;

&lt;p&gt;At this point with InstallApplications already having run before login
and running any additional items in the user space as needed, I have
Munki run in the background to start downloading packages, but I held
the actual notifications via DEPNotify until the user logged in with the
JAMF Connect Sync item via its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SignInCommand&lt;/code&gt; configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2018/11/screen-shot-2018-11-21-at-11-07-14-am.png&quot; alt=&quot;Screen Shot 2018-11-21 at 11.07.14
AM&quot; /&gt;{.alignnone
.size-full .wp-image-2273 width=”285” height=”117”}&lt;/p&gt;

&lt;p&gt;Which then steps the user through a customized welcome via DEPNotify...&lt;/p&gt;

&lt;h2 id=&quot;items-of-note&quot;&gt;Items of Note&lt;/h2&gt;

&lt;h3 id=&quot;yubikey-provisioning&quot;&gt;Yubikey Provisioning&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Yubikey&apos;s must be provisioned and added into Okta before the user
logs into Okta for the first time. &lt;a href=&quot;https://support.okta.com/help/s/article/Using-YubiKey-Authentication-in-Okta&quot;&gt;Using YubiKey Authentication in
Okta&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;This restriction/limitation doesn&apos;t come into place with the Okta
Push or a TOTP code &lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;temp-password&quot;&gt;Temp password&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;User is provided with a temp password in on boarding materials with
an assigned hardware token&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;depnotify-slack-alerts&quot;&gt;DEPNotify Slack Alerts&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;May be over kill, but you can integrate this process into a webhooks
into you DEPNotify to notify when this process is started/finished
on clients&lt;/li&gt;
  &lt;li&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2018/11/screen_shot_2018-11-21_at_8_46_44_am.png&quot; alt=&quot;Screen_Shot_2018-11-21_at_8_46_44_AM&quot; /&gt;{.alignnone
.size-full .wp-image-2261 width=”546” height=”72”}&lt;/li&gt;
  &lt;li&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2018/11/screen_shot_2018-11-21_at_8_46_52_am.png&quot; alt=&quot;Screen_Shot_2018-11-21_at_8_46_52_AM&quot; /&gt;{.alignnone
.size-full .wp-image-2260 width=”554” height=”78”}&lt;/li&gt;
  &lt;li&gt;This will not scale well.. but if you want a little more insight
into a smaller shop this is handy.&lt;/li&gt;
  &lt;li&gt;As well as if you have an optional install into the JAMF Connect
provisioning...&lt;/li&gt;
  &lt;li&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2018/11/screen_shot_2018-11-21_at_8_49_48_am.png&quot; alt=&quot;Screen_Shot_2018-11-21_at_8_49_48_AM&quot; /&gt;{.alignnone
.size-full .wp-image-2262 width=”550” height=”75”}&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;mdm-slack-alerts&quot;&gt;MDM Slack Alerts&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Again, probably doesn&apos;t scale well... and depending on your
MDM...&lt;/li&gt;
  &lt;li&gt;SimpleMDM allows for webhooks, then you can use something like
&lt;a href=&quot;https://github.com/fimtitzgerald/simplemdm-slack-webhook&quot;&gt;this&lt;/a&gt; to
receive notifications on device enrollments/unenrollmcnts as well&lt;/li&gt;
  &lt;li&gt;&lt;img src=&quot;%7B%7B%20site.baseurl%20%7D%7D/assets/2018/11/screen_shot_2018-11-21_at_8_52_53_am-2.png&quot; alt=&quot;Screen_Shot_2018-11-21_at_8_52_53_AM-2&quot; /&gt;{.alignnone
.wp-image-2264 width=”444” height=”55”}&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;other-items-not-explored&quot;&gt;Other items not explored&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Filevault
    &lt;ul&gt;
      &lt;li&gt;Escrowing Keys&lt;/li&gt;
      &lt;li&gt;Securetoken Limitations&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;JAMF Connect &quot;Demobilization&quot; &amp;amp; AD
    &lt;ul&gt;
      &lt;li&gt;This assumes you are using Okta as your IdP and machines are not
bound to AD&lt;/li&gt;
      &lt;li&gt;If you have Okta syncing from or mastered by your directory
service, its not a stretch to see the potential for management
here.…&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;InstallApplicatoins Package Delivery
    &lt;ul&gt;
      &lt;li&gt;You&apos;ll need to host these items publicly for the initial
enrollment to work&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;resources-mentioned&quot;&gt;Resources Mentioned&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://simplemdm.com/&quot;&gt;SimpleMDM&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jamf.com/products/jamf-connect/&quot;&gt;JAMF Connect&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/erikng/installapplications&quot;&gt;InstallApplications&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.okta.com/help/s/article/Using-YubiKey-Authentication-in-Okta&quot;&gt;Using YubiKey Authentication in
Okta&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/fimtitzgerald/simplemdm-slack-webhook&quot;&gt;SimpleMDM Slack
Webhook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 21 Nov 2018 17:30:11 +0000</pubDate>
        <link>lucasjhall.github.io/2018/11/21/touchless-macos-deployment-utilizing-jamf-connect-and-installapplications.html</link>
        <guid isPermaLink="true">lucasjhall.github.io/2018/11/21/touchless-macos-deployment-utilizing-jamf-connect-and-installapplications.html</guid>
        
        
      </item>
    
  </channel>
</rss>
