<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:blog="https://www.russ.cloud/rss/ns"><channel><title>Russ McKendrick</title><description>Russ.Cloud - The personal blog of Russ McKendrick</description><link>https://www.russ.cloud/</link><language>en-gb</language><copyright>Copyright 2026 Russ McKendrick</copyright><managingEditor>web.site@mckendrick.email (Russ McKendrick)</managingEditor><webMaster>web.site@mckendrick.email (Russ McKendrick)</webMaster><item><title>Token Use: Coach Mode and a Week of Releases</title><link>https://www.russ.cloud/2026/07/19/token-use-coach-mode-and-a-week-of-releases/</link><guid isPermaLink="true">https://www.russ.cloud//2026/07/19/token-use-coach-mode-and-a-week-of-releases/</guid><description>A week after Token Use hit 1.0.0 by cutting features, it&apos;s on 1.2.2. Coach mode, a Rust port of Microsoft&apos;s AI-Engineering-Coach rules, full-text scrollback, an MCP server, and a 3D graph.</description><pubDate>Sun, 19 Jul 2026 09:14:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week I &lt;a href=&quot;/2026/07/12/a-catch-up-terminal-svg-and-token-use-v1/&quot;&gt;wrote about Token Use reaching 1.0.0&lt;/a&gt;, and the point of that post was mostly about what I’d taken out. The Insights area was gone, the &lt;a href=&quot;/glossary/agent/&quot;&gt;Agent&lt;/a&gt; Setup page was gone, and the tool had settled into a single sentence: it reads your local usage records, normalises them, prices them, and shows you the result.&lt;/p&gt;
&lt;p&gt;That lasted about four days. Since then there have been five releases and Token Use is on 1.2.2. In my defence, the thing I added back is nothing like the thing I removed.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/tokenuse&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/tokenuse: A Rust TUI and Desktop app for exploring AI coding tool token and cost usage&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-f93e562e3b40.jpg&quot; alt=&quot;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/001.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/001.png&quot; alt=&quot;The Token Use desktop Overview page showing seven days of usage: £1538 across 8,308 calls and 61 sessions, plan utilisation bars for Claude Code, Codex, and Copilot, and the Activity Pulse spend and call charts&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2560&quot; height=&quot;1720&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That’s the last seven days. £1538, 8,308 calls, 61 sessions. Graph, Coach, and Scrollback in the sidebar are all new since the 1.0.0 post.&lt;/p&gt;
&lt;h2&gt;Coach mode, and the extension that started it&lt;/h2&gt;
&lt;p&gt;A colleague at work pointed me at &lt;span&gt;&lt;a href=&quot;https://github.com/microsoft/AI-Engineering-Coach&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AI-Engineering-Coach&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, a VS Code extension from Microsoft that watches how you work with AI coding tools and grades you on it. Not on your code, on your &lt;em&gt;practice&lt;/em&gt;: are your prompts specific enough, are your sessions running long past the point where context has gone stale, are you reviewing what the model writes before you accept it. It’s MIT licensed, and the rules are readable.&lt;/p&gt;
&lt;p&gt;The reason it stuck with me is that it’s answering a question Token Use was already sitting on the data for and never asking. I had every prompt, every response, every edited file, every timestamp, sat in a local archive being summed into a cost figure. Cost tells you what you spent. It tells you nothing about whether you spent it well.&lt;/p&gt;
&lt;p&gt;So Coach is a report card for how you work with your AI tools. It landed in &lt;strong&gt;1.1.0 “Report Card”&lt;/strong&gt; as a desktop sidebar page, and the TUI got the same grade, findings, and Setup panel a couple of releases later under &lt;code&gt;k&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The detection is a Rust-native port of the extension’s rule catalogue, vibe-coded in Rust using &lt;a href=&quot;/glossary/claude-code/&quot;&gt;Claude Code&lt;/a&gt; like the rest of Token Use. Worth being clear about the split, given what the rest of this post is about: Claude Code wrote the port, and the calls about what to carry over, what to leave out, and where to deviate were mine.&lt;/p&gt;
&lt;p&gt;It’s all evaluated locally. No network calls, no model in the loop, which is the same line I drew when I cut Insights: I’m happy for the tool to count things, I’m not happy for it to start another model to tell you what the count means. There are 28 rules now across four groups (prompt quality, session hygiene, code review, tool mastery), each with a severity that costs you points when it fires, and the composite grade maps onto A+ through F. Attribution is in &lt;code&gt;NOTICE&lt;/code&gt; at the repo root and in &lt;code&gt;docs/development/coach.md&lt;/code&gt;. The port is rules-as-code rather than the reference’s user-editable &lt;a href=&quot;/glossary/markdown/&quot;&gt;markdown&lt;/a&gt; DSL, and thresholds are Microsoft’s defaults unless there’s a documented deviation.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/002.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/002.png&quot; alt=&quot;The desktop Coach Report tab showing an overall grade of C at 77 out of 100, 19 of 28 rules clean, and score cards for Prompt Quality 73, Session Hygiene 66, Code Review 80, and Tool Mastery 100&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2560&quot; height=&quot;1720&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Below the grade there’s a Findings tab that turns each triggered rule into a filterable evidence list, an AI Output tab counting what the models actually produced, and an Activity tab with work hours, a calendar strip, and per-project breakdowns. The Flow and Pace panels sit at the bottom of the Report tab, and mine puts my average longest unbroken block at 222 minutes.&lt;/p&gt;
&lt;h3&gt;What I deliberately skipped&lt;/h3&gt;
&lt;p&gt;Sixteen of the reference rules don’t work here, and the reason is worth saying out loud because it’s the whole shape of the port. Rules like yolo-mode, no-plan-mode, no-slash-commands and auto-approve-terminal are all detectable from VS Code’s own chat logs. Token Use reads the usage files that Claude Code, &lt;a href=&quot;/glossary/codex/&quot;&gt;Codex&lt;/a&gt;, Cursor, Copilot, and Gemini write to disk, and those signals simply aren’t in there. Rather than approximate them and hand you a grade built on guesses, they’re skipped.&lt;/p&gt;
&lt;p&gt;The reference also has a &lt;code&gt;profanity&lt;/code&gt; rule. That one I skipped on a judgement call: scanning someone’s prompts for swearing reads as surveillance in a usage dashboard, whatever the intent. And the extension’s &lt;a href=&quot;/glossary/large-language-model/&quot;&gt;LLM&lt;/a&gt;-backed features (Skill Finder, Learning Center, the Context Health review) are permanently out of scope under the no-network rule.&lt;/p&gt;
&lt;p&gt;Going the other way, there’s one rule that isn’t in the reference set at all. &lt;strong&gt;Retry Loops&lt;/strong&gt; borrows codeburn’s one-shot/retry idea and points it at the archive: it flags periods where a high share of edited files needed a re-edit after a shell run, which is the pattern where a change didn’t survive its own verification. It only fires for tools that actually report edited files, so Claude Code, Codex, and Cursor.&lt;/p&gt;
&lt;p&gt;The other thing I care about here is that rows with missing history stay &lt;code&gt;NULL&lt;/code&gt; and are excluded from rule denominators. If half your archive predates the enrichment columns, that half doesn’t get to skew your grade. A grading tool that quietly marks you down for data it never had is worse than useless.&lt;/p&gt;
&lt;h3&gt;The Setup panel&lt;/h3&gt;
&lt;p&gt;A couple of releases later, in 1.2.0, Coach picked up an advisory panel alongside the grade that’s kept separate from it, deliberately. It looks at your local Claude Code configuration and flags configured &lt;a href=&quot;/glossary/model-context-protocol/&quot;&gt;MCP&lt;/a&gt; servers you never call, &lt;code&gt;CLAUDE.md&lt;/code&gt; files over 200 lines once &lt;code&gt;@import&lt;/code&gt;s are expanded, files being re-read repeatedly inside a single session, and reads under &lt;code&gt;node_modules&lt;/code&gt; and friends. Each one comes with a rough token-savings estimate.&lt;/p&gt;
&lt;p&gt;None of it affects your practice grade, because none of it is really about practice. It’s the stuff that’s quietly costing you input tokens on every single turn.&lt;/p&gt;
&lt;h2&gt;Everything else that shipped&lt;/h2&gt;
&lt;p&gt;Coach was the headline, but four more releases went out in the same week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.1.1 “Ground Truth”&lt;/strong&gt; was a correction. Cursor changed where it stores things, and Token Use had been reading a shape of the data that no longer reflected what Cursor actually does. Ingestion now goes through the current Agent &lt;code&gt;store.db&lt;/code&gt; alongside the older state bubbles, rebuilt around a single canonical reconstruction of each user turn. Cursor’s first-party model pricing went in too, and model lookups became tool-aware so Cursor models get Cursor rates instead of falling through to a global table. Codex Desktop was also silently dropping usage events where the credit balance arrived as a string rather than a number, which is fixed and backfilled. The archive now carries provenance labels for interaction mode, token quality, and timestamp quality, and Coach’s timing rules only trust exact timestamps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.2.0 “Total Recall”&lt;/strong&gt; is the big one after Coach. &lt;strong&gt;Scrollback&lt;/strong&gt; is full-text search across every archived transcript, backed by an SQLite FTS5 index, available as a TUI tab on &lt;code&gt;/&lt;/code&gt; and a desktop page. There’s a new MCP server (&lt;code&gt;tokenuse mcp&lt;/code&gt;) exposing read-only &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;overview&lt;/code&gt;, &lt;code&gt;projects&lt;/code&gt;, and &lt;code&gt;scrollback&lt;/code&gt; tools over stdio, and it will also serve over a loopback HTTP endpoint so it can run alongside the desktop app rather than fighting it for the archive. &lt;strong&gt;By Activity&lt;/strong&gt; classifies every call into one of thirteen task categories using deterministic rules rather than a model. And there’s &lt;code&gt;tokenuse doctor&lt;/code&gt; for per-tool diagnostics, plus &lt;code&gt;tokenuse status&lt;/code&gt; and &lt;code&gt;tokenuse overview&lt;/code&gt; for scriptable summaries with &lt;code&gt;--json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Buried in that release is a parser fix I’m quietly pleased with. Claude Code writes streamed JSONL where a single assistant turn can span multiple lines, and the old parser took the first line and dropped the rest, which silently lost tool calls. Incremental sync also went byte-level, so on a ~50MB project a one-file append now resyncs in about 0.04s rather than about 0.4s.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;doctor&lt;/code&gt; is the one I keep going back to. It walks every probed location for every tool and tells you what it found, what’s missing, and how many calls it managed to parse, which turns “why is Cursor showing nothing” from a guess into a list.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/003.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/003.png&quot; alt=&quot;The desktop Config Data Sources panel showing per-tool diagnostics for Claude Code, Cursor, Codex, and Copilot with FOUND and MISSING paths and parsed call counts&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2560&quot; height=&quot;1720&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The Projects page also became a two-level thing in this release: an index, then a page per project. This is the Token Use repo itself over the same seven days. £1098, and 45,749 lines of Rust that Claude Code wrote rather than me, which is the honest accounting behind a post otherwise full of “I added”.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/004.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/004.png&quot; alt=&quot;The tokens project page showing £1098 across 5,596 calls and 25 sessions, 45,749 AI-written lines of Rust, an estimated 22.8 active hours, and an AI code output breakdown by language&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2560&quot; height=&quot;1720&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;1.2.1 “Model Citizen”&lt;/strong&gt; made the Models catalogue clickable. Selecting a row opens a page for that model with its own KPIs, session list, token-composition split, per-tool donut, and an effective-pricing panel. That last one says so explicitly when a model is being billed through the pricing book’s fallback row, rather than presenting a guess as a price. The TUI got &lt;code&gt;m&lt;/code&gt; for a typeable model picker, mirroring the existing &lt;code&gt;p&lt;/code&gt; project picker.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/005.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/005.png&quot; alt=&quot;The desktop Models catalogue grouped by provider, showing Anthropic and OpenAI models with cost and call counts across five time periods and a cache hit rate column&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2560&quot; height=&quot;1720&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It’s also where the shared model registry from 1.0.0 earns its keep. Every dated pin, vendor prefix, and router alias in the raw logs folds down into one row per model, which is why that table reads as a list rather than the same model three times under slightly different names.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.2.2 “Constellation”&lt;/strong&gt; shipped yesterday and adds a 3D Graph page: projects wired to the tools and models they use, or the same thing from the other end. Node size and edge strength follow whichever measure you pick (calls, spend, or tokens), and selected nodes drill straight into their detail pages. Large graphs get capped, and when that happens the view tells you how many entities it left out instead of pretending it’s showing you everything.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/006.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-07-19-token-use-coach-mode-and-a-week-of-releases/006.png&quot; alt=&quot;The desktop Graph page showing projects as green nodes connected to Claude Code and Codex tool nodes and model nodes, with a header stating 16 of 62 entities and 33 relationships are visible&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2560&quot; height=&quot;1720&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Note the line above the canvas: 16 of 62 entities, 33 relationships. Drawing the top slice and letting you assume that’s everything would have been easier, and I’ve used enough dashboards that do exactly that.&lt;/p&gt;
&lt;h2&gt;Installing&lt;/h2&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Token Use&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# The terminal UI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/tokenuse&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tokenuse&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# The macOS desktop app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/tokenuse-desktop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Token Use&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Schema migrations&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;1.1.0 moves the call archive to schema v4 and 1.2.0 takes it to v7, so the first launch after upgrading will be slower while it backfills from the history already on disk. Older binaries will refuse to open a newer archive, so upgrade both the TUI and the desktop app together.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Linux, Windows, and manual desktop downloads are on &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/tokenuse/releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub Releases&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and there’s more at &lt;span&gt;&lt;a href=&quot;https://www.tokenuse.app/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;tokenuse.app&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Adding a report card, a search index, an MCP server, and a 3D graph a week after a post about cutting features is not a great look. But Coach still passes the test I set for 1.0.0: it counts what’s already in your data and shows you the count, rather than starting a model to tell you what the count means.&lt;/p&gt;
&lt;p&gt;My grade is a C. 77 out of 100, 19 of the 28 rules clean. Session hygiene came out at 66, which is a D, mostly down to 51 runaway agent loops and an average unbroken block of nearly four hours.&lt;/p&gt;
&lt;p&gt;Coach has also flagged me for &lt;a href=&quot;/glossary/vibe-coding/&quot;&gt;Vibe Coding&lt;/a&gt; eight times, with the advice to write a small spec first and review the output line by line. A tool I vibe-coded, telling me off for vibe-coding. I did briefly wonder about retuning that threshold.&lt;/p&gt;
&lt;p&gt;Thanks to the team at Microsoft who wrote those rules and put them out under MIT, and to my colleague for pointing the extension out. Coach exists because someone else had already done the thinking about what good looks like.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/tokenuse&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/tokenuse: A Rust TUI and Desktop app for exploring AI coding tool token and cost usage&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-f93e562e3b40.jpg&quot; alt=&quot;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/microsoft/AI-Engineering-Coach&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - microsoft/AI-Engineering-Coach: better agentic engineering&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;better agentic engineering. Contribute to microsoft/AI-Engineering-Coach development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-b94107d6f41a.jpg&quot; alt=&quot;better agentic engineering. Contribute to microsoft/AI-Engineering-Coach development by creating an account on GitHub.&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;If you give it a go and the grading feels wrong for how you work, that’s worth an issue. The rules are code, and thresholds tuned against VS Code chat logs won’t be right for everyone. Pull requests and issues are welcome.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-07-19-token-use-coach-mode-and-a-week-of-releases.BJtXdmtz.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/07/19/token-use-coach-mode-and-a-week-of-releases-og.png</blog:ogImage><blog:readingTime>11</blog:readingTime><category>tools</category><category>ai</category><category>code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>A Catch-Up: terminal-svg and Token Use v1</title><link>https://www.russ.cloud/2026/07/12/a-catch-up-terminal-svg-and-token-use-v1/</link><guid isPermaLink="true">https://www.russ.cloud//2026/07/12/a-catch-up-terminal-svg-and-token-use-v1/</guid><description>Two projects worth writing about: terminal-svg, a new Rust tool for pixel-perfect terminal screenshots, and Token Use finally reaching v1</description><pubDate>Sun, 12 Jul 2026 15:27:44 GMT</pubDate><content:encoded>&lt;p&gt;It’s been a busy few months of evenings and weekends, and two of my projects have reached a point where they’re worth writing about. One is brand new: &lt;strong&gt;terminal-svg&lt;/strong&gt;, a tool for turning terminal output into SVG screenshots that actually look right everywhere. The other is &lt;strong&gt;Token Use&lt;/strong&gt;, which has been through more releases than I can count and has finally landed on 1.0.0.&lt;/p&gt;
&lt;p&gt;Rather than spin these into two posts, I’ll take them one after the other. Both were vibe-coded in Rust using &lt;a href=&quot;/glossary/claude-code/&quot;&gt;Claude Code&lt;/a&gt;, so if you’ve read the &lt;a href=&quot;/2026/04/11/introducing-ai-commit/&quot;&gt;AI Commit&lt;/a&gt;, &lt;a href=&quot;/2026/02/01/introducing-ssl-toolkit/&quot;&gt;ssl-toolkit&lt;/a&gt; or &lt;a href=&quot;/2026/04/05/introducing-hostsbutler/&quot;&gt;HostsButler&lt;/a&gt; posts, the shape of the work will be familiar.&lt;/p&gt;
&lt;h2&gt;terminal-svg&lt;/h2&gt;
&lt;p&gt;If you’ve ever tried to put a terminal screenshot in a README or a blog post, you’ll know the two ways it usually goes wrong. Either the tool regex-strips the ANSI codes, which breaks the moment your output has a progress bar or a spinner or anything that redraws a line, or it renders text against a system font, which means the box-drawing characters and powerline segments line up perfectly on your machine and nowhere else. I’d been hitting both often enough — most of the screenshots on this blog are of terminal output — that I decided to build something that did neither.&lt;/p&gt;
&lt;p&gt;The result is &lt;strong&gt;terminal-svg&lt;/strong&gt;. Point it at a command, a file, or a pipe and it gives you back an SVG with window chrome, your choice of colour scheme, and the JetBrains Mono Nerd Font it used subsetted and embedded in the file itself. That last part is the bit I care about most: the SVG renders identically on &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt;, in a browser on a machine with no fonts installed, anywhere an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag works.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/terminal-svg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/terminal-svg: Pixel-perfect SVG screenshots of terminal output, from a single self-contained binary.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Pixel-perfect SVG screenshots of terminal output, from a single self-contained binary. - russmckendrick/terminal-svg&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-3e32200b12b2.jpg&quot; alt=&quot;Pixel-perfect SVG screenshots of terminal output, from a single self-contained binary. - russmckendrick/terminal-svg&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h3&gt;How it avoids the usual problems&lt;/h3&gt;
&lt;p&gt;The output goes through a proper VT state machine (&lt;span&gt;&lt;a href=&quot;https://github.com/asciinema/avt&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;avt&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, the engine behind asciinema) rather than a regex. Carriage-return progress bars, &lt;code&gt;ESC[K&lt;/code&gt; clears, and cursor-up repaints all resolve to exactly the final screen a real terminal would show. For each screenshot it collects only the glyphs actually used and embeds those as WOFF2, which usually adds a few KB rather than a whole font file. Box-drawing aligns, powerline segments work, and wide CJK characters take up exactly two cells. Colour emoji can’t live in a monochrome font subset, so those get emitted as their own positioned text runs and render through the viewer’s native emoji font without knocking the columns after them out of line.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Screenshots&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Run a command in a PTY (it sees a real TTY, so colours are on)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;terminal-svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lsd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-la&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Pipe ANSI output through it&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ls&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--color=always&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminal-svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ls.svg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Render a captured ANSI dump with a different theme&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;terminal-svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dump.ansi&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nord&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dump.svg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/assets/2026-07-12-terminal-svg-and-token-use-v1-0-0/ls.svg&quot;&gt;&lt;img src=&quot;/assets/2026-07-12-terminal-svg-and-token-use-v1-0-0/ls.svg&quot; alt=&quot;A terminal screenshot showing the output of the &apos;ls&apos; command&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;740&quot; height=&quot;491&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Recording animated SVGs&lt;/h3&gt;
&lt;p&gt;The part I didn’t expect to enjoy as much as I did is &lt;code&gt;terminal-svg rec&lt;/code&gt;. It drops you into your shell, records everything you do, and when you exit it renders an animated SVG replaying the session. It’s a GIF-quality demo at a fraction of the size, the text stays real and selectable, and like everything else it plays anywhere an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag does — GitHub READMEs included, no JavaScript.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Recording a session&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Record an interactive session; exit the shell to finish&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;terminal-svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;demo.svg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# rec keeps an asciicast next to the SVG, so you can retheme&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# or re-time the replay later without re-recording&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;terminal-svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;demo.cast&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;github-dark&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--speed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--no-loop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;demo-fast.svg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Recordings are standard &lt;span&gt;&lt;a href=&quot;https://docs.asciinema.org/manual/asciicast/v2/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;asciicast v2&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; files, so &lt;code&gt;asciinema play demo.cast&lt;/code&gt; works, and existing asciinema recordings render straight through terminal-svg.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/assets/2026-07-12-terminal-svg-and-token-use-v1-0-0/terminal.svg&quot;&gt;&lt;img src=&quot;/assets/2026-07-12-terminal-svg-and-token-use-v1-0-0/terminal.svg&quot; alt=&quot;A terminal screenshot showing the output of the &apos;ls&apos; command&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1504&quot; height=&quot;899&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;The SVG is its own source&lt;/h3&gt;
&lt;p&gt;Every SVG carries its own recording, compressed inside a &lt;code&gt;&amp;lt;metadata&amp;gt;&lt;/code&gt; block that browsers ignore, along with the options it was rendered with. Lose the &lt;code&gt;.cast&lt;/code&gt; and nothing is lost — the SVG re-renders and the recording extracts byte-for-byte. It means you can re-theme a screenshot you saved months ago without keeping the original around.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Round-tripping&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Re-render an existing SVG with different options&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;terminal-svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;demo.svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nord&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--speed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;demo-nord.svg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Recover the recording from the SVG&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;terminal-svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extract&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;demo.svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;demo.cast&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Because the embedded source contains everything that was captured, including anything a program echoed, there’s a &lt;code&gt;terminal-svg edit&lt;/code&gt; for cleaning a take up first (masking a leaked token, say, or taming a run of long pauses), and a &lt;code&gt;--no-embed-source&lt;/code&gt; flag if you’d rather it carried nothing at all. There’s also a browser-based visual editor (&lt;code&gt;terminal-svg editor&lt;/code&gt;) with a live preview and a timeline panel for fixing typos and retiming events after the fact, but that’s a rabbit hole for another post.&lt;/p&gt;
&lt;h3&gt;Installing&lt;/h3&gt;
&lt;p&gt;It’s a single self-contained binary with no runtime dependencies.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - macOS and Linux&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/terminal-svg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Linux (direct binary)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;s/x86_64/amd64/;s/aarch64/arm64/&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-LO&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/terminal-svg/releases/latest/download/terminal-svg-linux-&lt;/span&gt;&lt;span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminal-svg-linux-&lt;/span&gt;&lt;span&gt;${ARCH}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminal-svg-linux-&lt;/span&gt;&lt;span&gt;${ARCH} &lt;/span&gt;&lt;span&gt;/usr/local/bin/terminal-svg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Windows&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Invoke-WebRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Uri &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/terminal-svg/releases/latest/download/terminal-svg-windows-amd64.exe&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;OutFile &lt;/span&gt;&lt;span&gt;&quot;terminal-svg.exe&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;On Windows, drop the &lt;code&gt;.exe&lt;/code&gt; somewhere on your &lt;code&gt;PATH&lt;/code&gt;. The &lt;code&gt;rec&lt;/code&gt; subcommand is macOS and Linux only for now; rendering &lt;code&gt;.cast&lt;/code&gt; files works everywhere. All builds are on the &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/terminal-svg/releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub releases page&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, each with a &lt;code&gt;.sha256&lt;/code&gt; next to it, and there’s a theme gallery at &lt;span&gt;&lt;a href=&quot;https://terminal-svg.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;terminal-svg.dev&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Token Use hits v1.0.0&lt;/h2&gt;
&lt;p&gt;If you’ve read the &lt;a href=&quot;/2026/tokenuse/&quot;&gt;Token Use post&lt;/a&gt; before, you’ll know it started as a small TUI for reading the token and cost data that Claude Code, &lt;a href=&quot;/glossary/codex/&quot;&gt;Codex&lt;/a&gt;, Cursor, and Copilot already write to disk. It’s had a long run of small releases since then, and 1.0.0, named Clear Focus, is the biggest change since I started it.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/tokenuse&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/tokenuse: A Rust TUI and Desktop app for exploring AI coding tool token and cost usage&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-f93e562e3b40.jpg&quot; alt=&quot;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Two things happened in this release. The first is that the desktop app got rebuilt properly. It used to be more or less a copy of the terminal layout wrapped in a window; now it’s a sidebar application with its own screens — Overview, Analytics, Tools, Models, Projects, and Config — plus direct pages for each supported tool. It still shares the same Rust data layer as the TUI, so the numbers can’t drift between the two, but it stopped pretending to be a terminal. The Projects screen now does a proper local drill-down: pick a project to see its sessions, pick a session to see its calls, open a call for its full token breakdown. The TUI got the same attention from the other direction and is now tightened down to three views: Overview, Deep Dive, and Usage.&lt;/p&gt;
&lt;p&gt;The second thing is that I took some features out. Earlier versions had an Insights area that generated recommendations by shelling out to a local &lt;code&gt;codex&lt;/code&gt;, &lt;code&gt;claude&lt;/code&gt;, or &lt;code&gt;gemini&lt;/code&gt; and asking it what your usage meant. There was an &lt;a href=&quot;/glossary/agent/&quot;&gt;Agent&lt;/a&gt; Setup page doing something similar for config auditing. Both sat outside the job the tool is actually good at, added a second layer of generated interpretation on top of your data, and threw confusing errors when one of those CLIs wasn’t installed. So they’re gone, end to end — the advice engine, the prompts, the snapshots, all of it.&lt;/p&gt;
&lt;p&gt;I’ll be honest, cutting features on the way to a 1.0.0 felt slightly backwards while I was doing it. But it left the tool much easier to describe: Token Use reads your local usage records, normalises them, prices them, and shows you the result. It doesn’t start another model to tell you what the numbers mean. Everything still stays local — there’s no platform &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; key, no proxy, no telemetry, and the only network calls are the confirmed pricing and currency downloads and the opt-in quota sync, exactly as before.&lt;/p&gt;
&lt;p&gt;The other change worth mentioning is a shared model registry. Real tool logs are full of awkward model ids: dated Claude pins, vendor-prefixed Gemini paths, Cursor’s &lt;code&gt;auto&lt;/code&gt; and &lt;code&gt;default&lt;/code&gt;, Copilot routers that hide which provider actually served the request. All of that now folds into one set of canonical rows through &lt;code&gt;src/models/registry.json&lt;/code&gt;, shared by the TUI, the desktop app, and the visual reports, so a model shows up the same way everywhere instead of three slightly different ways.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Token Use&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# The terminal UI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/tokenuse&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tokenuse&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# The macOS desktop app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/tokenuse-desktop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Token Use&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Linux, Windows, and manual desktop downloads are on &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/tokenuse/releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub Releases&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and there’s more at &lt;span&gt;&lt;a href=&quot;https://www.tokenuse.app/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;tokenuse.app&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Two fairly different projects, then. terminal-svg scratches a very specific itch I’ve had for years: screenshots that look the same for everyone who loads the page. It’s already rendering most of the terminal output on this blog, including its own README. Token Use reaching 1.0.0 is less about new features and more about deciding what the tool is for and cutting the rest, which turned out to be the more useful exercise of the two.&lt;/p&gt;
&lt;p&gt;Both are on GitHub, both are open source, and both have rough edges I’m still working through. If you give either a go and something breaks or you have an idea, pull requests and issues are welcome.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/terminal-svg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/terminal-svg: Pixel-perfect SVG screenshots of terminal output, from a single self-contained binary.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Pixel-perfect SVG screenshots of terminal output, from a single self-contained binary. - russmckendrick/terminal-svg&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-3e32200b12b2.jpg&quot; alt=&quot;Pixel-perfect SVG screenshots of terminal output, from a single self-contained binary. - russmckendrick/terminal-svg&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/tokenuse&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/tokenuse: A Rust TUI and Desktop app for exploring AI coding tool token and cost usage&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-f93e562e3b40.jpg&quot; alt=&quot;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-07-12-a-catch-up-terminal-svg-and-token-use-v1.DwRtd3An.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/07/12/a-catch-up-terminal-svg-and-token-use-v1-og.png</blog:ogImage><blog:readingTime>8</blog:readingTime><category>tools</category><category>ai</category><category>code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Playing with Headroom: Compressing the Context Going to My AI Coding Agents</title><link>https://www.russ.cloud/2026/06/13/playing-with-headroom-compressing-the-context-going-to-my-ai-coding-agents/</link><guid isPermaLink="true">https://www.russ.cloud//2026/06/13/playing-with-headroom-compressing-the-context-going-to-my-ai-coding-agents/</guid><description>I spent a few days running Headroom, a local proxy that promises to cut the tokens your AI coding agent sends to the provider. The savings are real, but they&apos;re not where the dashboard first makes you think they are.</description><pubDate>Sat, 13 Jun 2026 10:10:02 GMT</pubDate><content:encoded>&lt;p&gt;I spend real money on AI coding tools. Enough that I built &lt;span&gt;&lt;a href=&quot;https://tokenuse.app/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Token Use&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to keep an eye on where it all goes across &lt;a href=&quot;/glossary/claude-code/&quot;&gt;Claude Code&lt;/a&gt;, &lt;a href=&quot;/glossary/codex/&quot;&gt;Codex&lt;/a&gt;, and the rest. So when a project turns up promising “60-95% fewer tokens, same answers,” it gets my attention, even if the part of me that has read a few too many README files starts squinting straightaway.&lt;/p&gt;
&lt;p&gt;The project is &lt;span&gt;&lt;a href=&quot;https://headroom-docs.vercel.app/docs&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Headroom&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and I’ve been running it for a few days against my normal Claude Code workflow to see what those numbers actually look like once they hit my own usage rather than a benchmark. The short version: there are savings, they’re worth having, but almost none of them come from the bit the headline is selling. Let me walk through what I found.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/chopratejas/headroom&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - headroomlabs-ai/headroom: Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server. - headrooml...&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-fc660d3bee25.jpg&quot; alt=&quot;Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server. - headrooml...&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;What Headroom actually is&lt;/h2&gt;
&lt;p&gt;Headroom is a context-compression layer for AI agents. In practice that means a local proxy: it sits between your coding &lt;a href=&quot;/glossary/agent/&quot;&gt;agent&lt;/a&gt; and the model provider, intercepts everything the agent reads on its way to the &lt;a href=&quot;/glossary/large-language-model/&quot;&gt;LLM&lt;/a&gt;, and tries to shrink it before forwarding. Tool outputs, build logs, &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; blobs, file reads, RAG chunks, and the growing conversation history all get inspected and, where it can, compressed.&lt;/p&gt;
&lt;p&gt;It’s the work of Tejas Chopra, it’s Apache-2.0 licensed, and it’s mostly &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; with a Rust core. It can run as a Python library, an &lt;a href=&quot;/glossary/model-context-protocol/&quot;&gt;MCP&lt;/a&gt; server, or the proxy I’ve been using. I went with the proxy because it needs no changes to how I work: point Claude Code at it and carry on.&lt;/p&gt;
&lt;p&gt;The compression itself is the clever-sounding part. A content router sniffs each payload, works out what kind of data it is, and hands it to a matching compressor. Arrays of near-identical JSON get crushed down to a representative sample. Logs get de-duplicated. The original is kept locally so nothing is genuinely lost, and the model gets a &lt;code&gt;headroom_retrieve&lt;/code&gt; tool it can call to pull the full version back if it actually needs it. That last detail is what separates this from just truncating output and hoping, and it’s the bit I liked most on paper.&lt;/p&gt;
&lt;h2&gt;Getting it running&lt;/h2&gt;
&lt;p&gt;Installation is a pip install and a handful of subcommands. The full extras pull in the ML models for the heavier text compression, which means a chunky first-run download, so be on a decent connection for the first go.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Headroom&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;headroom-ai[all]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;headroom&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;headroom&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;headroom init&lt;/code&gt; wires it into the agents it can find on your machine, registers its MCP retrieve tool with Claude Code and Codex, and tells you to restart them so the hooks take effect. From there, the way you run an agent through the proxy is &lt;code&gt;headroom wrap&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Running Claude Code through Headroom&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;headroom&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wrap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;claude&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That starts the proxy, points Claude Code’s &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; base URL at it, and launches Claude Code as normal. As far as the day-to-day goes, that’s it. You work exactly as before, and the proxy quietly does its thing in the background on port 8787.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-06-13-playing-with-headroom/001.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-06-13-playing-with-headroom/001.png&quot; alt=&quot;Terminal showing headroom init configuring Claude Code and Codex, registering the MCP retrieve tool, and the headroom wrap claude command starting the proxy&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2330&quot; height=&quot;1378&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Telemetry is on by default&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Headroom sends anonymous aggregate stats (token counts and compression ratios, no prompt content) unless you opt out. If you’d rather it didn’t, set &lt;code&gt;HEADROOM_TELEMETRY=off&lt;/code&gt; or run the proxy with &lt;code&gt;--no-telemetry&lt;/code&gt;. Worth knowing before you start.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;The dashboard&lt;/h2&gt;
&lt;p&gt;The proxy serves a live dashboard at &lt;code&gt;http://127.0.0.1:8787/dashboard&lt;/code&gt;, and it’s a nicely put-together thing. Proxy dollars saved, token-savings percentage, request overhead, a prefix-cache panel, and an Anthropic subscription-window tracker that watches your 5-hour and 7-day usage windows and the overage on top.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-06-13-playing-with-headroom/002.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-06-13-playing-with-headroom/002.png&quot; alt=&quot;Headroom dashboard showing $9.23 proxy compression savings, $47.36 prefix cache discount, 20.7% token savings on 1.9M tokens, 304ms overhead, and an Anthropic subscription window panel&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2878&quot; height=&quot;2562&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It’s the sort of dashboard that makes you feel like you’re getting away with something. After a few longer sessions mine was reporting 20.7% token savings on roughly 1.9 million tokens, $9.23 saved on compression, and $47.36 on top from the prefix cache. Fifty-odd dollars for the price of a &lt;code&gt;pip install&lt;/code&gt;. Lovely.&lt;/p&gt;
&lt;p&gt;Except that’s the number you stop reading at, and the interesting part is what happens when you don’t.&lt;/p&gt;
&lt;h2&gt;Reading the numbers properly&lt;/h2&gt;
&lt;p&gt;Two things are worth pulling apart here, because the headline figure quietly blends them.&lt;/p&gt;
&lt;p&gt;The first is that the big dollar number isn’t really Headroom’s. That $47.36 is the prefix-cache discount, which is Anthropic’s own prompt caching doing its job: cached input tokens are billed at about a tenth of the normal rate, and over 10.5 million cache reads at an 88% hit rate that adds up fast. Headroom’s contribution here is to keep the prefix of each request byte-stable so the cache actually hits, which is a real and useful thing to do. But Claude Code already caches aggressively on its own, so most of that discount is money I’d have saved anyway. Crediting it to the proxy is generous accounting.&lt;/p&gt;
&lt;p&gt;The second is more interesting, and you only see it if you run &lt;code&gt;headroom perf&lt;/code&gt;, which breaks down where the savings came from.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;headroom perf (trimmed)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Requests:     152&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Tokens:       8,956,380 -&amp;gt; 7,105,767 (20.7% reduction)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Total saved:  1,850,613 tokens&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Per-Model Breakdown&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;----------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;claude-haiku-4-5: 34 reqs, 6,688 tokens saved (2%)    ~$0.01 at list price&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;claude-opus-4-8:  116 reqs, 1,843,925 tokens saved (22%)  ~$9.22 at list price&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Actual bill savings depend on provider caching behavior&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Optimization Overhead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;----------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Average:  304ms&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Max:      24722ms&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;500ms:   8 requests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Transform Effectiveness&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;----------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content_router: 0.8% avg reduction, 152 uses, 97,239 saved&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Content Router Routing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;----------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Compressed:  92 (2%)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Excluded:    1356 (28%) — Read/Glob outputs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Skipped:     3047 (63%) — &amp;lt;50 words&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Unchanged:   311 (6%) — ratio too high&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;So the proxy reports 1.85 million tokens saved, and the actual content compressor (the clever JSON-and-logs router that’s the whole pitch) accounts for 97,239 of them. That’s about five percent of the saving. The rest is the proxy trimming stale conversation history on long sessions, which is a perfectly reasonable thing to do, but it’s not the headline feature. The dashboard files all of it under “compression,” and the perf breakdown is the more honest reading.&lt;/p&gt;
&lt;p&gt;Look at the routing lines and it’s clear why. Sixty-three percent of what passed through was too short to bother with. Twenty-eight percent was &lt;code&gt;Read&lt;/code&gt; and &lt;code&gt;Glob&lt;/code&gt; output, which Headroom deliberately excludes. Only two percent actually got compressed. My sessions are mostly conversational back-and-forth and file reads, and that’s precisely the shape of work where the content compressor has the least to chew on. The tool even tells me so in its own recommendations, suggesting I let it compress stale &lt;code&gt;Read&lt;/code&gt; outputs for a bigger win.&lt;/p&gt;
&lt;p&gt;None of this is Headroom being broken. It’s Headroom being honest about a workload it isn’t ideally suited to. If I were running tool-heavy agents that hammer JSON APIs or wade through enormous log files, those compression numbers would look very different. For someone doing the kind of mixed coding-and-chatting I do, the compressor is a rounding error and the real saving is the history trimming plus a cache discount I mostly already had.&lt;/p&gt;
&lt;p&gt;One more thing the dashboard glosses over: it isn’t free. The proxy adds about 300ms of overhead per request on average, with the occasional outlier into the tens of seconds while the ML text model warms up. Against the multi-second latency of an actual model response that’s mostly noise, but it’s not nothing, and it’s the kind of cost that doesn’t show up next to the dollar signs.&lt;/p&gt;
&lt;h2&gt;The bit I didn’t expect to like&lt;/h2&gt;
&lt;p&gt;The feature that earned its keep wasn’t the compression at all. It was &lt;code&gt;headroom learn&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It reads back through your past agent sessions, the logs Claude Code and Codex leave behind, finds the places where the agent went wrong and correlates them with whatever eventually worked, and proposes corrections to write into your &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt;. It runs as a dry run by default and shows you exactly what it would add before touching anything.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;headroom learn&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;headroom&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;learn&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;headroom learn (trimmed)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Detected agents: Claude Code, OpenAI Codex CLI, Google Gemini CLI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;============================================================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[codex] codex&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Path: /Users/russ.mckendrick&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;============================================================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Analyzing with claude-cli...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Sessions: 181  |  Calls: 26599  |  Failures: 956 (3.6%)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Recommendations: 5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[WOULD WRITE] /Users/russ.mckendrick/.codex/AGENTS.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;──────────────────────────────────────────────────&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;## Headroom Learned Patterns&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*Auto-generated by `headroom learn` on 2026-06-13 — do not edit manually*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;### dvr file map&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*~2,500 tokens/session saved*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Large files — read targeted ranges, never cat whole: `src/recorder.rs`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(~2400 lines) and `frontend/App.jsx` (~2300 lines). Recordings live at&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`/Volumes/vids/_dvr/` locally and `/mnt/_dvr/` on the server; they are&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;NOT under the repo.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;### Projects &amp;amp; Paths&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*~1,500 tokens/session saved*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Active work is in two Rust projects, not the session root (`~`):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`~/Code/dvr` and `~/Code/aicommit`. Both have an AGENTS.md. cwd often&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;starts at `~` — cd into the project or use absolute paths.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;### Build &amp;amp; Validation (Rust)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*~1,200 tokens/session saved*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Standard gate in both projects:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`cargo fmt &amp;amp;&amp;amp; cargo check &amp;amp;&amp;amp; cargo clippy --all-targets --all-features&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-- -D warnings &amp;amp;&amp;amp; cargo test` (clippy is run `-D warnings`, so warnings&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fail). dvr&apos;s frontend builds only with `pnpm run build`.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;### aicommit facts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*~1,000 tokens/session saved*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Installed binary is `aic` (the `aicommit` bin was removed). Config env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vars use the `AIC_` prefix; AI engine code in `src/ai/`, prompts in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`prompts/commit-system.md`; tests in `tests/cli.rs` and `tests/provider.rs`.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;### Environment (macOS)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*~700 tokens/session saved*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;macOS host: `timeout`/`gtimeout` and `go` are NOT installed — don&apos;t use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`timeout` to bound commands and don&apos;t attempt `go build`/`go test`.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Running the dvr server with `cargo run` binds a port and is blocked by&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;the sandbox; rely on unit/integration tests rather than launching the server.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;──────────────────────────────────────────────────&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dry run — use --apply to write.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;On my machine it correctly spotted my two active Rust projects, worked out which files are too large to read whole, learned the &lt;code&gt;cargo fmt &amp;amp;&amp;amp; cargo check &amp;amp;&amp;amp; cargo clippy &amp;amp;&amp;amp; cargo test&lt;/code&gt; gate I run before every commit, and noted that this Mac has no &lt;code&gt;timeout&lt;/code&gt; or &lt;code&gt;go&lt;/code&gt; installed so the agents should stop trying to use them. It also slotted in a block for &lt;span&gt;&lt;a href=&quot;https://github.com/chopratejas/headroom&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;codebase-memory-mcp&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, the knowledge-graph indexer, telling the agents to prefer graph queries over blind file searching.&lt;/p&gt;
&lt;p&gt;That’s the sort of context I’d normally write into an &lt;code&gt;AGENTS.md&lt;/code&gt; by hand over a few weeks of being mildly annoyed. Having a tool mine it out of my own session history and hand it back is properly useful, and it’s the part of Headroom I’ll keep using regardless of what the compression numbers do.&lt;/p&gt;
&lt;h2&gt;What I made of it&lt;/h2&gt;
&lt;p&gt;Headroom is at v0.25.0 and the author is upfront that it’s early and not battle-tested against every edge case, which matches what I saw. The pieces all work, the dashboard is good, and nothing fell over.&lt;/p&gt;
&lt;p&gt;Whether it’s worth running comes down to what your agents actually do. If you’re running tool-heavy, JSON-and-log-heavy agent loops, the content compression has real material to work on and the savings should be the genuine article. If you’re doing mixed interactive coding like me, the compressor barely registers, the history trimming does a bit, and the headline dollar figure is mostly the provider’s own cache discount wearing Headroom’s badge. The two things are complementary rather than competing, so it’s not an argument against running it, just an argument for reading the perf breakdown instead of the front page.&lt;/p&gt;
&lt;p&gt;The lesson, if there is one, is the same one that applies to any dashboard with a big green number on it: the number is the start of the question, not the answer. Headroom’s perf command tells you the truth if you ask it, which is more than a lot of tools manage. And the &lt;code&gt;learn&lt;/code&gt; feature alone makes the install worth it.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/chopratejas/headroom&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - headroomlabs-ai/headroom: Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server. - headrooml...&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-fc660d3bee25.jpg&quot; alt=&quot;Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server. - headrooml...&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;If you spend enough on tokens to care, give it a go, run &lt;code&gt;headroom perf&lt;/code&gt; after a few real sessions, and decide based on what it shows you rather than the headline. Your mileage will depend entirely on what your agents spend their day reading.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-06-13-playing-with-headroom-compressing-the-context-going-to-my-ai-coding-agents.CbTZXrhI.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/06/13/playing-with-headroom-compressing-the-context-going-to-my-ai-coding-agents-og.png</blog:ogImage><blog:readingTime>11</blog:readingTime><category>ai</category><category>tools</category><category>macos</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>codebase-memory-mcp: Giving Claude Code (and Codex) a Map</title><link>https://www.russ.cloud/2026/05/10/codebase-memory-mcp-giving-claude-code-and-codex-a-map/</link><guid isPermaLink="true">https://www.russ.cloud//2026/05/10/codebase-memory-mcp-giving-claude-code-and-codex-a-map/</guid><description>A short look at codebase-memory-mcp, an MCP server that indexes your codebase into a queryable knowledge graph so Claude Code or Codex can answer structural questions without grepping every file.</description><pubDate>Sun, 10 May 2026 16:13:23 GMT</pubDate><content:encoded>&lt;p&gt;Anyone who has spent a few hours with &lt;a href=&quot;/glossary/claude-code/&quot;&gt;Claude Code&lt;/a&gt; or &lt;a href=&quot;/glossary/codex/&quot;&gt;Codex&lt;/a&gt; on a growing project will have watched the &lt;a href=&quot;/glossary/agent/&quot;&gt;agent&lt;/a&gt; grep its way around the same files over and over. Ask it “what calls this function?” and you can almost see it picking through the codebase a directory at a time, burning tokens as it goes. It works, but it’s not exactly elegant.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/DeusData/codebase-memory-mcp&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;codebase-memory-mcp&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; takes a different approach. It’s an &lt;a href=&quot;/glossary/model-context-protocol/&quot;&gt;MCP&lt;/a&gt; server that parses your source with tree-sitter and indexes everything (functions, classes, call chains, even cross-service HTTP links) into a SQLite-backed knowledge graph. Your agent then queries the graph instead of grepping the filesystem.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/DeusData/codebase-memory-mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - DeusData/codebase-memory-mcp: High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static bin...&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-bcb2a34cb3ad.jpg&quot; alt=&quot;High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static bin...&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;What it does&lt;/h2&gt;
&lt;p&gt;The README claims a 99.2% reduction in tokens for structural queries on a multi-service test project (~3,400 tokens via the graph versus ~412,000 via file-by-file exploration), and there’s an &lt;span&gt;&lt;a href=&quot;https://arxiv.org/abs/2603.27277&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;arXiv paper&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; backing the design with benchmarks across 31 real-world repositories: 83% answer quality, 10× fewer tokens, and 2.1× fewer tool calls than file-by-file exploration. I was sceptical, but after a couple of sessions on one of my own projects, I’m prepared to believe it.&lt;/p&gt;
&lt;p&gt;It supports 66 languages - the usual suspects (&lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt;, Go, TypeScript, Rust, Java, C/C++, C#, PHP, Ruby) plus a long tail that includes Zig, Elixir, OCaml, Haskell, and infrastructure formats like Dockerfile, &lt;a href=&quot;/glossary/kubernetes/&quot;&gt;Kubernetes&lt;/a&gt; manifests, and HCL. The Linux kernel (28M LOC, 75K files) gets indexed in three minutes on an M3 Pro, and most queries return in under a millisecond. 14 MCP tools cover indexing, call-path tracing, dead code detection, &lt;a href=&quot;/glossary/git/&quot;&gt;git&lt;/a&gt;-diff impact analysis, ADR management, and Cypher-like graph queries against the graph. REST routes from FastAPI, Gin, Express and friends are first-class graph nodes too, so you can match HTTP call sites to their handlers across services.&lt;/p&gt;
&lt;p&gt;It’s a single statically-linked binary written in C with no runtime dependencies, backed by SQLite in WAL mode. The cache lives at &lt;code&gt;~/.cache/codebase-memory-mcp/&lt;/code&gt; and persists across restarts.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;The repo provides a one-liner installer that grabs the right pre-built binary for your platform, sorts macOS code signing, and registers the MCP server with whichever coding agents it can find. I went with the &lt;code&gt;--ui&lt;/code&gt; variant, which bundles a graph visualisation frontend on top of the MCP server itself:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install on macOS / Linux with the UI variant&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-fsSL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--ui&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Windows users get a PowerShell equivalent (&lt;code&gt;install.ps1&lt;/code&gt; from the same repo) - the &lt;span&gt;&lt;a href=&quot;https://github.com/DeusData/codebase-memory-mcp#quick-start&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;README’s Quick Start&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; has the three-line download-inspect-run sequence.&lt;/p&gt;
&lt;p&gt;The script drops the binary into &lt;code&gt;~/.local/bin&lt;/code&gt; and then runs &lt;code&gt;codebase-memory-mcp install&lt;/code&gt; to wire it into any agents it detects. It auto-configures eleven of them out of the box (Claude Code, Codex &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt;, Gemini CLI, Zed, OpenCode, Antigravity, Aider, KiloCode, VS Code, OpenClaw, and Kiro), adding MCP server entries, instruction files, skills, and pre-tool hooks where each agent supports them. In my case it picked up both Claude Code and Codex without me having to point at anything, and it’ll warn you if &lt;code&gt;~/.local/bin&lt;/code&gt; isn’t on your PATH yet.&lt;/p&gt;
&lt;p&gt;After it finishes, restart your coding agent. In Claude Code, &lt;code&gt;/mcp&lt;/code&gt; will show &lt;code&gt;codebase-memory-mcp&lt;/code&gt; in the list of available servers; in Codex, the new tools turn up on the next session.&lt;/p&gt;
&lt;h2&gt;Indexing a project&lt;/h2&gt;
&lt;p&gt;Once it’s connected, point your agent at a project:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Index a project&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Index this project&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The agent calls &lt;code&gt;index_repository&lt;/code&gt; against your current directory. The first run does a full parse; after that, it’s incremental, and only files whose content hashes have changed get re-parsed.&lt;/p&gt;
&lt;h2&gt;Asking questions&lt;/h2&gt;
&lt;p&gt;Once the graph is built, the agent has something better than grep to reach for. The kind of questions that used to take a flurry of greps now resolve to a single tool call:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“What calls &lt;code&gt;process_order&lt;/code&gt;?”&lt;/li&gt;
&lt;li&gt;“Find all functions with no callers”&lt;/li&gt;
&lt;li&gt;“Show me the cross-service HTTP calls between the &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; and the worker”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Under the hood, those become &lt;code&gt;trace_call_path&lt;/code&gt;, &lt;code&gt;search_graph&lt;/code&gt;, and &lt;code&gt;query_graph&lt;/code&gt; calls. Both Claude Code and Codex translate the natural language into the right tool without any extra prompting on my part.&lt;/p&gt;
&lt;p&gt;Here it is answering a real one against this blog’s repo:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-10-codebase-memory-mcp-giving-claude-code-and-codex-a-map/001.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-10-codebase-memory-mcp-giving-claude-code-and-codex-a-map/001.png&quot; alt=&quot;The agent answering a question about Cloudflare images by calling search_graph and get_code_snippet against the indexed blog repo&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2204&quot; height=&quot;2788&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Behind that single prompt the agent ran one &lt;code&gt;search_graph&lt;/code&gt; call (16 results, top hit &lt;code&gt;getCFImageUrl&lt;/code&gt;), pulled the implementation with &lt;code&gt;get_code_snippet&lt;/code&gt;, and skim-read a few of the related docs files. Total work for a question that would otherwise have been a parade of greps and reads: “Explored 5 files, 1 search”. The summary it produced (this repo uses Cloudflare Image Transformations and the key helper is &lt;code&gt;src/utils/cloudflare-images.ts&lt;/code&gt; at line 32) is the kind of concise, file-referenced answer I want from an agent, and the kind that’s hard to get when it’s forced to crawl the filesystem one read at a time.&lt;/p&gt;
&lt;p&gt;For the curious, here’s what that whole Codex session actually cost - pulled out of &lt;span&gt;&lt;a href=&quot;https://tokenuse.app&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Token Use&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, the dashboard I &lt;a href=&quot;/2026/05/01/counting-the-cost-of-vibe-coding/&quot;&gt;wrote about last week&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-10-codebase-memory-mcp-giving-claude-code-and-codex-a-map/003.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-10-codebase-memory-mcp-giving-claude-code-and-codex-a-map/003.png&quot; alt=&quot;The Token Use desktop app showing seven Codex calls for the blog project totalling £0.299 over 238.9K input tokens, 3.5K output tokens, and 198.3K cache-read tokens&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2916&quot; height=&quot;1440&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The whole session, including the one-off &lt;code&gt;index_repository&lt;/code&gt; call upfront (the most expensive of the seven by some margin), came to £0.299 - call it 30p. The question-answering portion alone, once the index was in place, was about 22p across six smaller calls. The standout number is the cache reads: 198.3K of the 238.9K input tokens were served from cache, with the actual output a tiny 3.5K. Codex is doing very little talking and a lot of querying, which is precisely the shape you want.&lt;/p&gt;
&lt;h2&gt;The graph UI&lt;/h2&gt;
&lt;p&gt;If you went with the &lt;code&gt;--ui&lt;/code&gt; variant, the binary will also start a small web app on demand that lets you browse the indexed graph visually. Run it with the &lt;code&gt;--ui=true&lt;/code&gt; flag and a port of your choosing:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Start the graph UI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;codebase-memory-mcp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--ui=true&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--port=9749&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then point your browser at &lt;code&gt;http://localhost:9749&lt;/code&gt;. Here’s what it looks like for this blog:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-10-codebase-memory-mcp-giving-claude-code-and-codex-a-map/002.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-10-codebase-memory-mcp-giving-claude-code-and-codex-a-map/002.jpg&quot; alt=&quot;The codebase-memory-mcp graph UI showing 10,929 nodes and 10,916 edges for the Astro blog repo&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4112&quot; height=&quot;2502&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That’s 10,929 nodes and 10,916 edges, which sounds like a lot until you remember Astro turns every MDX file into a tree of Section nodes (7,885 of them in my case, alongside 542 Files, 542 Modules, 94 Functions, and a handful of Routes). The sidebar filters by node label and edge type, you can search and click into individual nodes, and the whole thing lays itself out as a big force-directed graph that’s quietly fun to poke around in.&lt;/p&gt;
&lt;p&gt;I haven’t reached for the UI much during day-to-day work - the value flows through the agent - but it’s useful as a sanity check on what the indexer has actually picked up, and it makes for a decent screensaver if someone is looking over your shoulder.&lt;/p&gt;
&lt;h2&gt;Teaching your agent to actually use it&lt;/h2&gt;
&lt;p&gt;One thing the README is upfront about: without a hint, agents will sometimes default to grep and Read for code questions even with the MCP server connected. The good news is that &lt;code&gt;install&lt;/code&gt; handles this for you - it adds skills, instruction blocks, and pre-tool hooks for whichever agents it detects, and those nudge the agent toward graph queries for structural questions rather than letting it reach for grep out of habit.&lt;/p&gt;
&lt;p&gt;If you want to know what kind of guidance gets added - or you’re using an agent that isn’t on the auto-config list and want to roll your own - here’s a sensible starting block. Drop it into &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt; for Claude Code, or &lt;code&gt;~/.codex/AGENTS.md&lt;/code&gt; for Codex:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;CLAUDE.md / AGENTS.md&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Codebase Memory (codebase-memory-mcp)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;When this MCP server is available, &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;prefer graph tools over grep/Explore for structural code questions&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Graph queries return precise results in a single tool call (~500 tokens) vs file-by-file exploration (~80K tokens).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Before exploration/planning&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: Run &lt;/span&gt;&lt;span&gt;`index_repository`&lt;/span&gt;&lt;span&gt; to ensure the graph is current&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;&quot;Who calls X?&quot;&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`trace_call_path(function_name=&quot;X&quot;, direction=&quot;inbound&quot;)`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;&quot;What does X call?&quot;&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`trace_call_path(function_name=&quot;X&quot;, direction=&quot;outbound&quot;)`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Find functions by pattern&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`search_graph(label=&quot;Function&quot;, name_pattern=&quot;.*Pattern.*&quot;)`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Dead code&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`search_graph(label=&quot;Function&quot;, relationship=&quot;CALLS&quot;, direction=&quot;inbound&quot;, max_degree=0, exclude_entry_points=true)`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Cross-service calls&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`search_graph(relationship=&quot;HTTP_CALLS&quot;)`&lt;/span&gt;&lt;span&gt; or &lt;/span&gt;&lt;span&gt;`query_graph`&lt;/span&gt;&lt;span&gt; with Cypher&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;REST routes&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`search_graph(label=&quot;Route&quot;)`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Understand structure first&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`get_graph_schema`&lt;/span&gt;&lt;span&gt; before writing complex queries&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Read source&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`get_code_snippet(qualified_name=&quot;...&quot;)`&lt;/span&gt;&lt;span&gt; after finding functions via search&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;Complex patterns&lt;/span&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`query_graph`&lt;/span&gt;&lt;span&gt; with Cypher for multi-hop graph traversals&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Use grep/Glob for text search (string literals, error messages, config values) - the graph doesn&apos;t index text content.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I dropped this into my global &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;~/.codex/AGENTS.md&lt;/code&gt;, and the difference is noticeable in both. The agent leans on &lt;code&gt;trace_call_path&lt;/code&gt; and &lt;code&gt;search_graph&lt;/code&gt; for anything structural, and falls back to grep only for text content the graph doesn’t index, like string literals or error messages.&lt;/p&gt;
&lt;h2&gt;A few caveats&lt;/h2&gt;
&lt;p&gt;The graph indexes structure, not text content, so don’t expect it to find that one config string you misspelt three months ago - that’s still grep territory. The recommended block above is explicit about this, which is why the agent will still happily reach for grep when it’s the right tool for the job.&lt;/p&gt;
&lt;p&gt;That said, for the “where is this used?” and “what’s actually dead?” questions that come up constantly when refactoring, it’s quietly become part of my default setup in both Claude Code and Codex.&lt;/p&gt;
&lt;h2&gt;Worth a try?&lt;/h2&gt;
&lt;p&gt;If you’re using Claude Code or Codex on anything bigger than a single-file script and you’ve felt the friction of the agent re-reading the same files for the same questions, this is worth ten minutes of your time. The install is painless thanks to the script doing the agent configuration for you, and once a project is indexed the token savings on structural queries are substantial enough to notice on the bill at the end of the month.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/DeusData/codebase-memory-mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - DeusData/codebase-memory-mcp: High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static bin...&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-bcb2a34cb3ad.jpg&quot; alt=&quot;High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static bin...&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-05-10-codebase-memory-mcp-giving-claude-code-and-codex-a-map.n2uenlXW.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/05/10/codebase-memory-mcp-giving-claude-code-and-codex-a-map-og.png</blog:ogImage><blog:readingTime>9</blog:readingTime><category>ai</category><category>code</category><category>tools</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Counting the Cost of Vibe Coding</title><link>https://www.russ.cloud/2026/05/01/counting-the-cost-of-vibe-coding/</link><guid isPermaLink="true">https://www.russ.cloud//2026/05/01/counting-the-cost-of-vibe-coding/</guid><description>A local-first dashboard for tracking AI coding tool spend across Claude Code, Codex, Cursor, Copilot, and Gemini CLI - with a TUI, a desktop app, and zero API keys required.</description><pubDate>Fri, 01 May 2026 07:53:04 GMT</pubDate><content:encoded>&lt;p&gt;Over the last year or so, every AI coding provider seems to have settled on the same pricing direction: token-based billing. &lt;a href=&quot;/glossary/claude-code/&quot;&gt;Claude Code&lt;/a&gt;, &lt;a href=&quot;/glossary/codex/&quot;&gt;Codex&lt;/a&gt;, Cursor, Copilot, and Gemini all now meter you on input, output, cache reads, cache writes, and (in some cases) reasoning tokens, and the bill at the end of the month reflects how chatty you’ve been with the model rather than a flat subscription.&lt;/p&gt;
&lt;p&gt;Which is fine, until you start wondering where the money actually went.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/glossary/vibe-coding/&quot;&gt;Vibe coding&lt;/a&gt; makes that worse, not better. When you’re cheerfully throwing prompts at the model and pasting in screenshots, the per-turn cost is the last thing on your mind, but it does add up.&lt;/p&gt;
&lt;p&gt;I’d been using a couple of existing tools to keep an eye on this, and they did the job for the basics (totals per day, totals per project) but I kept hitting limitations. I wanted to see costs in pounds rather than always having to do the conversion in my head. I wanted to drill into a specific session and see the prompt that triggered a particularly expensive turn. I wanted to compare Claude Code and Codex side by side on the same project, because I tend to use both depending on the task. None of the existing tools quite did all of that, so I built &lt;strong&gt;Token Use&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.tokenuse.app&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Token Use - AI Token Usage &amp;amp; Cost Tracker for Coding Tools&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Track AI coding token usage, costs, models, projects, and quotas across Claude Code, Codex, Cursor, GitHub Copilot, and Gemini with a local TUI and desktop app.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;tokenuse.app&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/tokenuse.app-605b56a76170.jpg&quot; alt=&quot;Token Use social preview showing a broadcast studio for AI coding token and cost usage tracking.&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Updated for v0.0.8&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;This post originally went out alongside the first public release. Token Use has moved on a fair bit since then, so I’ve updated the screenshots and prose to match what’s currently shipping. The biggest changes are Gemini &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; support, the Activity Pulse graphs, the tray popover and background alerts on the desktop app, and the Reports workflow that replaced the older snapshot exports.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;What it does&lt;/h2&gt;
&lt;p&gt;Token Use reads the session files that AI coding tools already write to your local disk, normalises the data into a single archive, and shows you what you’re spending. There’s no &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; key, no proxy, no telemetry endpoint, no daemon, and no live file watcher. It just parses the JSONL (and one SQLite database, in Cursor’s case) that your tools have already written, then renders the results.&lt;/p&gt;
&lt;p&gt;It currently supports five sources:&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Tool&lt;/th&gt;&lt;th&gt;Source format&lt;/th&gt;&lt;th&gt;Token quality&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;JSONL session files under &lt;code&gt;~/.claude/projects/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Exact, including cache reads and writes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Codex&lt;/td&gt;&lt;td&gt;JSONL rollouts under &lt;code&gt;~/.codex/sessions/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Exact per-turn token deltas, plus rate-limit snapshots&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cursor&lt;/td&gt;&lt;td&gt;SQLite &lt;code&gt;state.vscdb&lt;/code&gt; and &lt;a href=&quot;/glossary/agent/&quot;&gt;Agent&lt;/a&gt; transcripts under &lt;code&gt;~/.cursor/projects/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Exact when present, estimated otherwise&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; Copilot&lt;/td&gt;&lt;td&gt;Legacy CLI events and VS Code Copilot Chat transcripts&lt;/td&gt;&lt;td&gt;Exact for legacy, estimated for transcripts&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Gemini CLI&lt;/td&gt;&lt;td&gt;Chat files under &lt;code&gt;~/.gemini/tmp/&amp;lt;project_hash&amp;gt;/chats/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Exact, including cached input reads and thought tokens&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;It comes in two flavours: a Rust-based TUI for the terminal, and a Tauri desktop app for when you’d rather have a window with a sidebar and clickable rows. Both share the same local archive, so whichever you launch shows the same data, and they share the same embedded keymap so the shortcuts work the same in either place.&lt;/p&gt;
&lt;h2&gt;The meta moment&lt;/h2&gt;
&lt;p&gt;Before we get into installing and using it, I want to show you the obvious thing: I built Token Use using Claude Code and Codex, which means Token Use has a very detailed record of what Token Use cost to build.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/001.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/001.png&quot; alt=&quot;The Token Use desktop app filtered to the tokens project, showing the cost of building Token Use itself&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2588&quot; height=&quot;2476&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The dashboard above shows the project filtered to &lt;code&gt;tokens&lt;/code&gt; (which is the name of the folder on my local machine), with the breakdown split across the tools I used while building it. That 96.3% cache hit rate is doing a lot of the heavy lifting on the Claude Code side. Anthropic prices cache reads at roughly 10% of the input rate, so without that ratio the Claude column would be considerably more eye-watering.&lt;/p&gt;
&lt;p&gt;The orange and cyan chart in the middle is the &lt;strong&gt;Activity Pulse&lt;/strong&gt;: the orange bars are spend per bucket, the cyan area is call volume over the same window. Hovering a bucket shows the period label, cost, and call count. Short windows like 24 Hours and 7 Days use hourly buckets; 30 Days and All Time use daily buckets. This Month is a hybrid that stays hourly for the first two weeks and switches to daily from the 15th onward, which avoids the start of a month collapsing into one or two oversized bars.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/002.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/002.png&quot; alt=&quot;Session drill-down showing per-call costs for a Token Use development session&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2588&quot; height=&quot;2476&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Drilling into a single session shows every call: when it happened, which model handled it, how many tokens went in and out, what tools were used, and the start of the prompt. The Codex session above cost £55.37 across 616 calls, with 86.9M of the 91.4M input tokens served from cache.&lt;/p&gt;
&lt;p&gt;I’ll leave the absolute number for the GitHub README (the cover image above isn’t entirely satirical), but seeing it broken down by day, by tool, and by session was the thing that finally convinced me the tool was actually useful rather than just an interesting weekend project.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;h3&gt;macOS&lt;/h3&gt;
&lt;p&gt;Both the TUI and the desktop app are available through my &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/homebrew-tap&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebrew tap&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the TUI - macOS&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/tokenuse&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the desktop app - macOS&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/tokenuse-desktop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Token Use&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The desktop DMG is Apple Silicon only, signed with a Developer ID Application certificate, and notarised through App Store Connect, so it should open without any Gatekeeper drama.&lt;/p&gt;
&lt;h3&gt;Linux&lt;/h3&gt;
&lt;p&gt;The TUI works on AMD64 and ARM64. The script below detects your architecture and grabs the right binary:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the TUI - Linux&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;s/x86_64/amd64/;s/aarch64/arm64/&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/tokenuse/releases/latest/download/tokenuse-linux-&lt;/span&gt;&lt;span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tokenuse&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tokenuse&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tokenuse&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/local/bin/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The desktop app for Linux is published as unsigned AppImage, deb, and rpm assets for both architectures. Verify the matching &lt;code&gt;.sha256&lt;/code&gt; file before running. For the AppImage:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the desktop app - Linux&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-O&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/russmckendrick/tokenuse/releases/latest/download/tokenuse-desktop-linux-amd64.AppImage&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-O&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/russmckendrick/tokenuse/releases/latest/download/tokenuse-desktop-linux-amd64.AppImage.sha256&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sha256sum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tokenuse-desktop-linux-amd64.AppImage.sha256&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tokenuse-desktop-linux-amd64.AppImage&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;./tokenuse-desktop-linux-amd64.AppImage&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;AppImage installs can check for newer desktop releases from the Config tab. If you used the &lt;code&gt;.deb&lt;/code&gt; or &lt;code&gt;.rpm&lt;/code&gt; package, update manually with the matching asset from GitHub Releases.&lt;/p&gt;
&lt;h3&gt;Windows&lt;/h3&gt;
&lt;p&gt;The Windows TUI is a single executable:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the TUI - Windows&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Invoke-WebRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Uri &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/tokenuse/releases/latest/download/tokenuse-windows-amd64.exe&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;OutFile &lt;/span&gt;&lt;span&gt;&quot;tokenuse.exe&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The desktop app ships as both an NSIS installer and an MSI for environments that prefer Windows Installer. Both are unsigned for now, so verify the &lt;code&gt;.sha256&lt;/code&gt; before installing:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the desktop app - Windows&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Invoke-WebRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Uri &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/tokenuse/releases/latest/download/tokenuse-desktop-windows-amd64-setup.exe&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;OutFile &lt;/span&gt;&lt;span&gt;&quot;tokenuse-setup.exe&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Get-FileHash&lt;/span&gt;&lt;span&gt; .\&lt;/span&gt;&lt;span&gt;tokenuse-setup.exe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Algorithm SHA256&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Start-Process&lt;/span&gt;&lt;span&gt; .\&lt;/span&gt;&lt;span&gt;tokenuse-setup.exe&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Like the AppImage build, the installed Windows desktop app can check for later NSIS installer releases from the Config tab. All releases are on the &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/tokenuse/releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub releases page&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;The TUI&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;tokenuse&lt;/code&gt; and it’ll scan your local session files, build the archive, and drop you into the Overview page:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/003.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/003.png&quot; alt=&quot;The Token Use TUI Overview page showing summary tiles, the Activity Pulse graph, and ranked spend tables&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3710&quot; height=&quot;2048&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The summary tiles up top cover cost, calls, sessions, cache hit rate, and input/output token totals. Below that, the Activity Pulse is chronological and ignores whatever sort mode you’ve picked, so the line keeps showing usage over time even when the ranked tables underneath are sorted by spend, latest date, or token use. The ranked tables themselves cover project and project/tool spend, model spend, shell commands, and &lt;a href=&quot;/glossary/model-context-protocol/&quot;&gt;MCP&lt;/a&gt; servers.&lt;/p&gt;
&lt;p&gt;The filters work the way you’d expect:&lt;/p&gt;

















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Key&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;1&lt;/code&gt; – &lt;code&gt;5&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Period: rolling 24 hours, 7 days, 30 days, this month, all time&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;t&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cycle the tool filter&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Open the project picker&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;s&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Open the session picker&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;g&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cycle sort mode: spend, latest date, token use&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Tab&lt;/code&gt; / &lt;code&gt;Shift-Tab&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cycle between Overview, Deep Dive, and Usage&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Shift-D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Toggle between live and bundled sample data&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Resync the archive&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;e&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Generate a report&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;q&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Quit&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;1&lt;/code&gt; period is rolling from the current time rather than a calendar-day midnight cutoff, which I find more useful when you’ve just finished a session and want to see what it actually cost.&lt;/p&gt;
&lt;p&gt;The Deep Dive page swaps the compact Activity Pulse for a larger Activity Trend graph, and adds two panels that aren’t on Overview: Top Sessions, ranking the most expensive sessions in the active filter, and Core Tools, which counts every AI tool call (Bash, Edit, Read, update_plan, and so on).&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/004.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/004.png&quot; alt=&quot;The Deep Dive page showing the Activity Trend graph and dense ranking tables for sessions, models, and tool usage&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3710&quot; height=&quot;2048&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Sessions and call detail&lt;/h2&gt;
&lt;p&gt;The session page is the bit I find myself in most often. Press &lt;code&gt;s&lt;/code&gt; to open a searchable picker of every session in the active filter; start typing to narrow it down by date, tool, or project name:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/005.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/005.png&quot; alt=&quot;Session picker overlay showing every session in the filter, with live search at the top&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3710&quot; height=&quot;2048&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Pick one and the drill-down opens, showing every turn in the session along with its timestamp, model, cost, tokens, tools, and the start of the prompt:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/006.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/006.png&quot; alt=&quot;TUI session drill-down showing the per-call breakdown for a Codex session&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3710&quot; height=&quot;2048&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Pressing &lt;code&gt;Enter&lt;/code&gt; on any row opens a detail modal with the full stored prompt and metadata for that turn. It’s useful for tracking down which question produced the expensive answer.&lt;/p&gt;
&lt;h2&gt;Usage and rate limits&lt;/h2&gt;
&lt;p&gt;The Usage page is a rolling 24-hour view across every supported tool. Opening the page automatically selects the 24 Hours period so the visible filter matches what each per-tool console is showing, and the page ignores the project filter so each tool gets its own section regardless of where you’ve been working.&lt;/p&gt;
&lt;p&gt;Each tool console shows a 24-hour pulse graph, totals for calls, tokens, cost, and last seen, plus the top three models for that tool’s slice. If you’re using Codex, the Usage page also shows the rate-limit snapshots Codex writes to its session files, so you can see how close you are to the 5-hour and weekly windows without having to do the maths yourself:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/007.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/007.png&quot; alt=&quot;The Usage page showing rolling 24-hour activity per tool plus Codex rate-limit windows&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3710&quot; height=&quot;2048&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Token Use doesn’t make any live API calls to fetch limits, it just surfaces the values your tools have already written locally. That’s a deliberate trade-off: the figures lag slightly behind whatever the tool is showing in real time, but there’s no extra surface area to worry about.&lt;/p&gt;
&lt;h2&gt;Currency and pricing&lt;/h2&gt;
&lt;p&gt;Costs are calculated and stored as USD at import time, then converted for display. The Config page lets you pick a different display currency:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/008.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/008.png&quot; alt=&quot;The Config page with the currency picker overlay open, showing GBP selected from 159 currencies&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3710&quot; height=&quot;2048&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The currency snapshot is fetched from &lt;span&gt;&lt;a href=&quot;https://frankfurter.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Frankfurter&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and the pricing table is derived from &lt;span&gt;&lt;a href=&quot;https://github.com/BerriAI/litellm&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;LiteLLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. Both are embedded in the binary so the tool works completely offline by default. If you want fresher rates, the Config page has explicit “download latest” buttons that need confirmation each time. There are no background fetches.&lt;/p&gt;
&lt;p&gt;The Config page also has a clear-data action that asks for confirmation, deletes the local &lt;code&gt;archive.db&lt;/code&gt;, and immediately reimports from your tool history. Your config, downloaded rates, pricing snapshots, and previously generated reports are kept. It’s handy if you want to rebuild the archive against a fresher pricing snapshot, or just to check the importer is doing what you expect.&lt;/p&gt;
&lt;h2&gt;The desktop app&lt;/h2&gt;
&lt;p&gt;The desktop app is a Tauri v2 + Svelte frontend over the same Rust core. It reads the same archive, writes the same config, and exposes the same dashboard pages, with clickable tabs and dropdowns alongside the keyboard shortcuts. Here’s the Deep Dive view, which mirrors the layout of the TUI Deep Dive page:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/009.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/009.png&quot; alt=&quot;The Token Use desktop app on the Deep Dive tab showing the Activity Trend, top sessions, project rankings, and ranked tool tables&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2588&quot; height=&quot;2476&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The Usage tab is the same view as in the TUI, with per-tool consoles for each of the five supported tools and the Codex rate-limit windows visible alongside the 24-hour totals:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/010.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/010.png&quot; alt=&quot;The Token Use desktop app on the Usage tab showing the per-tool consoles and Codex rate-limit windows&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2588&quot; height=&quot;2476&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you have both installed, you can use whichever suits the moment. I tend to leave the desktop app open on a second monitor when I’m doing a lot of AI-assisted work, and reach for the TUI when I’m already in a terminal and want to spot-check spend on a specific project.&lt;/p&gt;
&lt;h3&gt;Tray popover and background alerts&lt;/h3&gt;
&lt;p&gt;Closing the desktop window doesn’t quit the app: Token Use keeps running in the background, and on macOS the Dock icon will reopen the main window if you click it again. Left-clicking the tray or menu-bar icon opens a compact 24-hour popover with KPI tiles for cost, calls, tokens, and cache hit rate, an Activity sparkline, and a top-models breakdown. There’s an &lt;strong&gt;Open&lt;/strong&gt; button if you want the full dashboard, a &lt;strong&gt;Refresh&lt;/strong&gt; button if you want to force a sync, and right-clicking the icon gives you the native menu with &lt;strong&gt;Show Token Use&lt;/strong&gt; and &lt;strong&gt;Quit Token Use&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/011.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/011.png&quot; alt=&quot;The Token Use menu-bar popover showing 24-hour KPI tiles, an activity sparkline, and a breakdown of the top models including GPT-5, Opus 4.7, and Gemini 3 Flash&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;848&quot; height=&quot;1138&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While it’s running in the background, the desktop app keeps polling the archive for new usage. If an automatic refresh imports a chunk of new activity that crosses configurable thresholds, you get a native desktop notification. The defaults are conservative (notify after at least $1.00, 100k tokens, or 25 calls of new usage, with a 30-minute cooldown) and they live in the same &lt;code&gt;config.json&lt;/code&gt; the TUI reads:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;config.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;currency&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GBP&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;background_alerts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enabled&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;min_cost_usd&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;min_tokens&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100000&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;min_calls&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;25&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cooldown_minutes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;desktop&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;open_at_login&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;show_dock_or_taskbar_icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The desktop-only &lt;strong&gt;Open at Login&lt;/strong&gt; and &lt;strong&gt;Dock/taskbar icon&lt;/strong&gt; toggles are also exposed in the Config tab, so you can opt into a tray-first setup without hand-editing &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; if you’d rather. On Windows and Linux, the Config tab additionally includes an explicit update check against GitHub Releases, which is the supported path for those platforms; macOS desktop updates continue to come through Homebrew Cask.&lt;/p&gt;
&lt;h2&gt;Reports&lt;/h2&gt;
&lt;p&gt;This is the bit that’s changed the most since the first release. The earlier snapshot exports have been replaced with a richer &lt;strong&gt;Reports&lt;/strong&gt; workflow. Press &lt;code&gt;e&lt;/code&gt; on Overview, Deep Dive, Usage, or Session (or click the report button in the desktop app) and you’ll get a modal that asks for the period, project scope, format, and whether to redact prompts, commands, raw paths, session IDs, and dedup keys before generation. Reports always include all tools for the chosen scope, regardless of whatever tool filter you have set on the dashboard.&lt;/p&gt;
&lt;p&gt;The available formats cover most use cases:&lt;/p&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Format&lt;/th&gt;&lt;th&gt;Output&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;HTML&lt;/td&gt;&lt;td&gt;One self-contained executive report deck&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PDF&lt;/td&gt;&lt;td&gt;One A4 landscape render of the same deck (browserless)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SVG&lt;/td&gt;&lt;td&gt;One 16:9 visual summary with KPI strip, heatmap, and top highlights&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PNG&lt;/td&gt;&lt;td&gt;Same one-page visual summary, rasterised&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JSON&lt;/td&gt;&lt;td&gt;One pretty-printed full report dataset&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Excel&lt;/td&gt;&lt;td&gt;One multi-sheet &lt;code&gt;.xlsx&lt;/code&gt; workbook with summary and raw data sheets&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CSV&lt;/td&gt;&lt;td&gt;One folder with one CSV per report area&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The HTML and PDF decks are intended to be client-ready, with cover metadata, KPI ribbons, insight tiles, activity pages, and breakdown pages.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-05-01-counting-the-cost-of-vibe-coding/012.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-05-01-counting-the-cost-of-vibe-coding/012.png&quot; alt=&quot;The cover page of an HTML report deck showing KPI tiles for cost, calls, sessions, total tokens, cache hit, and web search, alongside generation metadata and insight tiles for the tokens project over 7 days&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2296&quot; height=&quot;1436&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The SVG and PNG outputs are the one-page version for when you just want a single visual summary. JSON, Excel, and CSV are the raw data formats for if you want to do your own analysis. Output defaults to your Downloads folder, falling back to &lt;code&gt;~/Downloads&lt;/code&gt; and then the local config directory, with timestamped, slugged filenames so re-running doesn’t overwrite the previous report.&lt;/p&gt;
&lt;p&gt;There’s also a guided CLI for when you want a report without opening the dashboard:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Generating reports from the CLI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tokenuse&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;report&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This walks through the same period, project, format, output folder, redaction, and confirmation steps as the modal, then exits. It only writes from live local sessions, so it bails out cleanly if there’s nothing to report on.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Where the data lives&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The local archive is stored at &lt;code&gt;~/Library/Application Support/tokenuse/archive.db&lt;/code&gt; on macOS, &lt;code&gt;~/.config/tokenuse/archive.db&lt;/code&gt; on Linux, and &lt;code&gt;%APPDATA%\tokenuse\archive.db&lt;/code&gt; on Windows. The desktop app and TUI share this same database, so whichever you launch first sees the latest sync.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;Privacy&lt;/h2&gt;
&lt;p&gt;Token Use is local-only by design. There is no telemetry or analytics, and no API keys to provide for any of the supported tools. The only outbound network requests are the explicit Config-page downloads for currency rates and the pricing snapshot, the optional update checks on the Windows and Linux desktop apps, and (on tagged builds) the maintainer release paths. Each of those needs confirmation before it fires.&lt;/p&gt;
&lt;p&gt;If you’re &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt;-conscious about a tool that reads your session transcripts, the source is on GitHub. The parsers live under &lt;code&gt;src/tools/&amp;lt;name&amp;gt;/&lt;/code&gt; if you want to verify exactly what’s being read, and the documentation on &lt;span&gt;&lt;a href=&quot;https://www.tokenuse.app&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;tokenuse.app&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; has a parser-by-parser breakdown covering each tool’s quirks.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.tokenuse.app&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Token Use - AI Token Usage &amp;amp; Cost Tracker for Coding Tools&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Track AI coding token usage, costs, models, projects, and quotas across Claude Code, Codex, Cursor, GitHub Copilot, and Gemini with a local TUI and desktop app.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;tokenuse.app&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/tokenuse.app-605b56a76170.jpg&quot; alt=&quot;Token Use social preview showing a broadcast studio for AI coding token and cost usage tracking.&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The source code for Token Use lives at:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/tokenuse&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/tokenuse: A Rust TUI and Desktop app for exploring AI coding tool token and cost usage&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-f93e562e3b40.jpg&quot; alt=&quot;A Rust TUI and Desktop app for exploring AI coding tool token and cost usage - russmckendrick/tokenuse&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It’s still early in its life, so there are bound to be edge cases I haven’t hit yet, particularly across the five different tool formats now that Gemini CLI has joined the lineup. If you spot something off in the parsing, or if there’s a feature that would make the tool more useful for your workflow, issues and pull requests are welcome.&lt;/p&gt;
&lt;p&gt;If nothing else, having the data has changed how I think about my AI tool usage. Knowing that a particular kind of refactoring task tends to cost a few quid, while a quick code review costs almost nothing, is the sort of thing that’s hard to internalise without seeing the numbers laid out next to each other. Whether you end up doing more of the cheap things or fewer of the expensive ones is up to you.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-05-01-counting-the-cost-of-vibe-coding.BTBpVjob.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/05/01/counting-the-cost-of-vibe-coding-og.png</blog:ogImage><blog:readingTime>16</blog:readingTime><category>ai</category><category>tools</category><category>macos</category><category>linux</category><category>code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Is my blog Agent-Ready?</title><link>https://www.russ.cloud/2026/04/19/is-my-blog-agent-ready/</link><guid isPermaLink="true">https://www.russ.cloud//2026/04/19/is-my-blog-agent-ready/</guid><description>Running russ.cloud through isitagentready.com and wiring up the checks that actually make sense for a static blog - Link headers, Content Signals, agent-skills discovery, llms.txt, and a tiny Cloudflare Worker to serve markdown on demand.</description><pubDate>Sun, 19 Apr 2026 07:18:46 GMT</pubDate><content:encoded>&lt;p&gt;A few days ago I stumbled across &lt;span&gt;&lt;a href=&quot;https://isitagentready.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;isitagentready.com&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; by Cloudflare while browsing &lt;span&gt;&lt;a href=&quot;https://www.reddit.com/r/CloudFlare/comments/1so2eh2/introducing_the_agent_readiness_score_is_your/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Reddit&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, which is exactly what it sounds like: you give it a URL, it runs a checklist of emerging “AI-&lt;a href=&quot;/glossary/agent/&quot;&gt;agent&lt;/a&gt;-friendly” standards against your site, and it tells you what’s missing. I pointed it at &lt;span&gt;&lt;a href=&quot;https://www.russ.cloud/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.russ.cloud/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; expecting a gentle green checklist and got a fairly red one instead.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://isitagentready.com/www.russ.cloud&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Is Your Site Agent-Ready?&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Scan your website to see if it&apos;s ready for AI agents. Check for llms.txt, MCP, agent skills, and other agent-friendly standards.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;isitagentready.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/isitagentready.com-08db539bc8af.jpg&quot; alt=&quot;Anthropomorphic AI agents looking at an optimized version of a website&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The report flagged nine things. Some of them made perfect sense for a blog; others were aimed at sites that publish APIs, host identity providers, or expose browser-side tool-calling. This post walks through what I actually changed, what I deliberately didn’t, and why.&lt;/p&gt;
&lt;h2&gt;What “Is It Agent Ready?” actually checks&lt;/h2&gt;
&lt;p&gt;It’s a single-page tool that sends a handful of requests to your domain and looks for specific files, response headers, and behaviours. The checks cluster into a few buckets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discovery signals&lt;/strong&gt; - can an AI agent find your sitemap, feeds, and capabilities without scraping HTML?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content negotiation&lt;/strong&gt; - if an agent asks for &lt;code&gt;Accept: text/markdown&lt;/code&gt;, do you hand back a clean &lt;a href=&quot;/glossary/markdown/&quot;&gt;markdown&lt;/a&gt; version?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content preferences&lt;/strong&gt; - does your &lt;code&gt;robots.txt&lt;/code&gt; declare whether you consent to training, search, and live answer generation?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication and APIs&lt;/strong&gt; - if you run an OAuth server or an &lt;a href=&quot;/glossary/model-context-protocol/&quot;&gt;MCP&lt;/a&gt; server, are they published at the expected well-known URLs?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser-side tools&lt;/strong&gt; - do you expose &lt;code&gt;navigator.modelContext.provideContext()&lt;/code&gt; tools via WebMCP?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last two buckets don’t apply to a static blog. This blog has no APIs, no auth, no MCP server, and no interactive tool surface. Publishing discovery documents for services that don’t exist is worse than not publishing them - it sends agents down dead ends and misrepresents the site. So I scoped the work to the buckets that do apply.&lt;/p&gt;
&lt;h2&gt;The changes&lt;/h2&gt;
&lt;p&gt;The blog is an &lt;span&gt;&lt;a href=&quot;https://astro.build/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Astro&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; static site deployed to Cloudflare Workers’ static-assets runtime. That shaped the implementation: no server-side code at runtime (mostly), no edge Pro plan. Everything below is on Cloudflare Free.&lt;/p&gt;
&lt;h3&gt;Content-Signal in robots.txt&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://contentsignals.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Content Signals&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is a draft spec for declaring how you want your content used. It adds a single line to &lt;code&gt;robots.txt&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Content-Signal: search=yes, ai-input=yes, ai-train=yes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Three axes: &lt;code&gt;search&lt;/code&gt; (classic indexing), &lt;code&gt;ai-input&lt;/code&gt; (live agent answers with attribution), &lt;code&gt;ai-train&lt;/code&gt; (model training). I went with yes-to-all, which is my honest preference - if an agent is going to answer a question about &lt;a href=&quot;/glossary/terraform/&quot;&gt;Terraform&lt;/a&gt; or &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; with a paraphrase of something I wrote, I’d rather the source material be accurate than half-remembered.&lt;/p&gt;
&lt;p&gt;I had been using the &lt;code&gt;astro-robots-txt&lt;/code&gt; integration, which doesn’t support arbitrary directives. Simpler to drop it, write a static &lt;code&gt;public/robots.txt&lt;/code&gt;, and edit it by hand. One less dependency.&lt;/p&gt;
&lt;h3&gt;Link response headers (RFC 8288)&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc8288&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;RFC 8288&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; lets you attach &lt;code&gt;Link:&lt;/code&gt; headers to any HTTP response pointing at related resources. Browsers ignore them; agents that respect the spec follow them. Cloudflare Pages merges these in via &lt;code&gt;public/_headers&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;RFC 8288&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Link: &amp;lt;https://www.russ.cloud/sitemap-index.xml&amp;gt;; rel=&quot;sitemap&quot;; type=&quot;application/xml&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Link: &amp;lt;https://www.russ.cloud/rss.xml&amp;gt;; rel=&quot;alternate&quot;; type=&quot;application/rss+xml&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Link: &amp;lt;https://www.russ.cloud/llms.txt&amp;gt;; rel=&quot;llms-txt&quot;; type=&quot;text/markdown&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Link: &amp;lt;https://www.russ.cloud/.well-known/agent-skills/index.json&amp;gt;; rel=&quot;agent-skills&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It’s free real estate - the agent doesn’t have to parse your HTML to find the feed or the sitemap.&lt;/p&gt;
&lt;h3&gt;Agent Skills discovery index&lt;/h3&gt;
&lt;p&gt;The &lt;span&gt;&lt;a href=&quot;https://github.com/cloudflare/agent-skills-discovery-rfc&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Agent Skills Discovery RFC&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; defines a &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; index at &lt;code&gt;/.well-known/agent-skills/index.json&lt;/code&gt; listing &lt;code&gt;SKILL.md&lt;/code&gt; files that describe what an agent can do with your site. For a blog, “skills” is a stretch - there’s nothing to invoke - but the spec is flexible enough to describe how to &lt;em&gt;consume&lt;/em&gt; content.&lt;/p&gt;
&lt;p&gt;I published one skill called &lt;code&gt;read-blog-content&lt;/code&gt; that documents the feed URL, the sitemap, the URL pattern (&lt;code&gt;/YYYY/MM/DD/&amp;lt;slug&amp;gt;/&lt;/code&gt;), and the convention that every post has a markdown twin. The index references it with a sha256 digest of the file contents, which the spec requires so agents can detect drift.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Agent Skills&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$schema&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://schemas.agentskills.io/discovery/0.2.0/schema.json&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;skills&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;read-blog-content&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;skill-md&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;How to discover, read, and cite posts from russ.cloud.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://www.russ.cloud/.well-known/agent-skills/read-blog-content/SKILL.md&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;digest&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;sha256:bd9b7c88…&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Markdown on demand&lt;/h3&gt;
&lt;p&gt;The most interesting check is markdown content negotiation: send &lt;code&gt;Accept: text/markdown&lt;/code&gt; to a blog post URL and get markdown back, without the URL changing.&lt;/p&gt;
&lt;p&gt;Cloudflare has a feature for this called &lt;span&gt;&lt;a href=&quot;https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Markdown for Agents&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. It’s a toggle in the dashboard under AI Crawl Control and it converts HTML to markdown at the edge, per-request. There’s one catch: it requires Pro plan or higher. Russ.cloud is on Free.&lt;/p&gt;
&lt;p&gt;On Free, I went with a two-layer approach:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Build-time markdown twins.&lt;/strong&gt; Every post already starts life as MDX in &lt;code&gt;src/content/blog/&lt;/code&gt; or &lt;code&gt;src/content/tunes/&lt;/code&gt;. A postbuild script (&lt;code&gt;scripts/generate-llms-markdown.js&lt;/code&gt;) walks those, strips the frontmatter, and writes an &lt;code&gt;index.md&lt;/code&gt; next to each rendered HTML page. So &lt;code&gt;/2026/04/11/introducing-ai-commit/&lt;/code&gt; has a sibling at &lt;code&gt;/2026/04/11/introducing-ai-commit/index.md&lt;/code&gt;. The same script writes an &lt;span&gt;&lt;a href=&quot;https://llmstxt.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;llms.txt&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; at the site root listing every post with its markdown URL. That’s useful for agents that don’t bother with content negotiation and just look for llms.txt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Runtime negotiation via a tiny Worker.&lt;/strong&gt; To satisfy the actual &lt;code&gt;Accept: text/markdown&lt;/code&gt; check, a fifty-line Cloudflare Worker sits in front of the static assets. On every request it looks at the &lt;code&gt;Accept&lt;/code&gt; header. If markdown is requested, it serves the pre-generated &lt;code&gt;.md&lt;/code&gt; twin with &lt;code&gt;Content-Type: text/markdown; charset=utf-8&lt;/code&gt; and &lt;code&gt;Vary: Accept&lt;/code&gt;. Otherwise it falls through to &lt;code&gt;env.ASSETS.fetch(request)&lt;/code&gt; and behaves exactly like before.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Worker&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;method &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;GET&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wantsMarkdown&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;md&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serveMarkdown&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;URL&lt;/span&gt;&lt;span&gt;(request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;url)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;md&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ASSETS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;One gotcha: by default, Cloudflare Workers with a Static Assets binding will short-circuit and serve matching assets directly without ever running your Worker. To force the Worker to run first so it can check &lt;code&gt;Accept&lt;/code&gt;, you need &lt;code&gt;&quot;run_worker_first&quot;: true&lt;/code&gt; on the &lt;code&gt;assets&lt;/code&gt; config in &lt;code&gt;wrangler.jsonc&lt;/code&gt;. Without that, the scanner will keep reporting the site as HTML-only and you’ll keep muttering at your terminal for longer than you’d like to admit.&lt;/p&gt;
&lt;h3&gt;What I deliberately skipped&lt;/h3&gt;
&lt;p&gt;The remaining five checks on the “Is It Agent Ready?” list are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; Catalog&lt;/strong&gt; (RFC 9727) - for sites that publish APIs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OAuth/OIDC discovery metadata&lt;/strong&gt; - for sites that are OAuth providers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OAuth Protected Resource Metadata&lt;/strong&gt; - for sites with protected APIs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP Server Card&lt;/strong&gt; - for sites hosting Model Context Protocol servers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebMCP&lt;/strong&gt; - for sites exposing in-browser tools via &lt;code&gt;navigator.modelContext&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A personal blog has none of these. You &lt;em&gt;could&lt;/em&gt; publish stub versions to turn the checklist green - an empty API linkset, a dummy MCP card - but that just lies to agents. If an agent follows an OIDC discovery document and tries to hit a token endpoint that returns 404, nobody wins. Better for the scanner to correctly say “this site doesn’t have an identity provider” than for me to fake one.&lt;/p&gt;
&lt;p&gt;The one exception I considered was an empty API catalog (&lt;code&gt;{&quot;linkset&quot;: []}&lt;/code&gt; at &lt;code&gt;/.well-known/api-catalog&lt;/code&gt;), which at least honestly signals “zero APIs published here.” I decided against it - the spec is really about listing APIs you do have, and publishing an empty linkset felt like gaming the test.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;p&gt;The rescan of of the site now passes four out of nine checks, and the remaining five are genuinely not applicable. That’s the correct state for a blog.&lt;/p&gt;
&lt;p&gt;A few things struck me while doing this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Most of the “agent readiness” story is just good old web hygiene.&lt;/strong&gt; A sitemap, an RSS feed, a &lt;code&gt;robots.txt&lt;/code&gt; that says what you mean - agents want the same things a thoughtful human crawler would.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content Signals is the one that might matter most.&lt;/strong&gt; The HTML metadata and robots directives we’ve been using for 25 years weren’t designed for “may I train a model on this?” questions. An explicit signal is better than arguing about the implicit one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markdown-on-demand is surprisingly nice to build.&lt;/strong&gt; Agents get cleaner parsing, browsers get the same HTML, and the build already knew how to produce markdown because that’s what MDX starts as. Shipping the twin on Free instead of paying for the edge feature was a reasonable trade.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Not every check should pass.&lt;/strong&gt; If the tooling treats “green on everything” as the goal, you’ll end up with a lot of sites pretending to be OAuth providers. The useful reading of these scanners is as a prompt to ask “should this apply to me?” - and the answer is often no.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can read the announcement post from Cloudflare at the link below:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://blog.cloudflare.com/agent-readiness/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Introducing the Agent Readiness score. Check to see if your site is agent-ready&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;The Agent Readiness score can help site owners understand how well their websites support AI agents. Here we explore new standards, share Radar data, and detail how we made Cloudflare’s docs the most agent-friendly on the web.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;blog.cloudflare.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/blog.cloudflare.com-a339f2da98dc.jpg&quot; alt=&quot;Introducing the Agent Readiness score. Check to see if your site is agent-ready&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;If you want to see the actual output, &lt;span&gt;&lt;a href=&quot;https://isitagentready.com/www.russ.cloud&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;the live scan for the site is here&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. And if you run your own blog, give it a go - the worst case is you learn that your &lt;code&gt;robots.txt&lt;/code&gt; has had a typo in it since 2019.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-04-19-is-my-blog-agent-ready.Cf5nXJJh.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/04/19/is-my-blog-agent-ready-og.png</blog:ogImage><blog:readingTime>8</blog:readingTime><category>ai</category><category>blog</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Introducing AI Commit</title><link>https://www.russ.cloud/2026/04/11/introducing-ai-commit/</link><guid isPermaLink="true">https://www.russ.cloud//2026/04/11/introducing-ai-commit/</guid><description>A Rust CLI I built to stop writing terrible commit messages - aic generates AI-powered commit messages, PR drafts, diff reviews, and repo visualisations from your staged changes.</description><pubDate>Sat, 11 Apr 2026 08:48:37 GMT</pubDate><content:encoded>&lt;p&gt;I’ll be honest: my commit messages have a history of being terrible. Not “wip” or “fix stuff” levels of terrible (usually), but certainly not the kind of clean, conventional, descriptive messages that make a project’s history useful six months later. I’d write something passable and move on, telling myself I’d go back and clean things up before opening the PR. I never did.&lt;/p&gt;
&lt;p&gt;So I used AI to build an AI tool to fix that. AI Commit is a Rust &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; that reads your staged diff and generates a commit message for you. The binary is &lt;code&gt;aic&lt;/code&gt;, it supports several AI providers including local ones, and it’s been in daily use here since the early builds.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/aicommit&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/aicommit: A fast, opinionated CLI that generates Git commit messages and reviews staged diffs using AI. Written in Rust, installed as aic&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A fast, opinionated CLI that generates Git commit messages and reviews staged diffs using AI. Written in Rust, installed as aic - russmckendrick/aicommit&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-61029f85acaa.jpg&quot; alt=&quot;A fast, opinionated CLI that generates Git commit messages and reviews staged diffs using AI. Written in Rust, installed as aic - russmckendrick/aicommit&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;Built with AI, for AI-assisted git&lt;/h2&gt;
&lt;p&gt;Like &lt;a href=&quot;/2026/02/01/introducing-ssl-toolkit/&quot;&gt;ssl-toolkit&lt;/a&gt; and &lt;a href=&quot;/2026/04/05/introducing-hosts-butler/&quot;&gt;HostsButler&lt;/a&gt; before it, AI Commit was vibe-coded in Rust using &lt;a href=&quot;/glossary/claude-code/&quot;&gt;Claude Code&lt;/a&gt; and &lt;a href=&quot;/glossary/codex/&quot;&gt;Codex&lt;/a&gt;. I’m not a Rust developer by trade - my background is infrastructure and &lt;a href=&quot;/glossary/devops/&quot;&gt;DevOps&lt;/a&gt; - but tools like Claude Code have made it practical for me to build and maintain projects in a language I wouldn’t otherwise have reached for.&lt;/p&gt;
&lt;p&gt;There’s an obvious meta quality to building an AI-powered tool with an &lt;a href=&quot;/glossary/ai-coding-assistant/&quot;&gt;AI coding assistant&lt;/a&gt;, and I won’t pretend that wasn’t part of the appeal. But it’s also just how I work now. Claude Code handles the parts of Rust that would otherwise send me down documentation rabbit holes, and I get to focus on the design and behaviour of the thing rather than fighting the borrow checker.&lt;/p&gt;
&lt;p&gt;If you’re curious about the vibe-coding workflow, I covered it in more detail in &lt;a href=&quot;/2025/05/26/doom-or-vibe-coding/&quot;&gt;Doom or Vibe Coding&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What it does&lt;/h2&gt;
&lt;p&gt;The core loop is simple: stage your changes, run &lt;code&gt;aic&lt;/code&gt;, review the generated message, and either accept it, ask for a regeneration, or drop into your editor to tweak it. The tool reads the diff, feeds it to whichever AI provider you’ve configured, and comes back with a conventional commit message - complete with a type, optional scope derived from your changed file paths, a description, and an explanatory body.&lt;/p&gt;
&lt;p&gt;Beyond the basic commit flow, there are a few other commands worth knowing about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;aic review&lt;/code&gt;&lt;/strong&gt; - runs your staged diff through a review-focused prompt and comes back with findings grouped by severity (Critical, Warning, Suggestion). Good for a sanity check before committing something you’re unsure about.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;aic pr&lt;/code&gt;&lt;/strong&gt; - generates a pull request title and &lt;a href=&quot;/glossary/markdown/&quot;&gt;Markdown&lt;/a&gt; description from your branch’s commits and cumulative diff. I’ve been using this every time I open a PR.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;aic log&lt;/code&gt;&lt;/strong&gt; - rewrites the last N commit messages on a branch using AI. Useful for cleaning up a scrappy local history before pushing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;aic history&lt;/code&gt;&lt;/strong&gt; - a local log of every generated message, review, and PR draft, browsable in an interactive picker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;aic map&lt;/code&gt;&lt;/strong&gt; - generates SVG visualisations of your codebase and commit history. More on this below.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;On macOS, Homebrew is the easiest path:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - macOS&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/aicommit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;On Linux, grab the binary directly - the following one-liner detects your architecture and installs it to &lt;code&gt;/usr/local/bin&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Linux&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;s/x86_64/amd64/;s/aarch64/arm64/&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/aicommit/releases/latest/download/aic-linux-&lt;/span&gt;&lt;span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/local/bin/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;On Windows, download with PowerShell:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Windows&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Invoke-WebRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Uri &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/aicommit/releases/latest/download/aic-windows-amd64.exe&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;OutFile &lt;/span&gt;&lt;span&gt;&quot;aic.exe&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;WinGet support is on the way - a &lt;span&gt;&lt;a href=&quot;https://github.com/microsoft/winget-pkgs/pull/358049&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PR has been submitted&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to the community repository and is currently in review. Once that’s merged you’ll be able to install with &lt;code&gt;winget install --id RussMcKendrick.Aicommit -e&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All current releases are on the &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/aicommit/releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub releases page&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;Once installed, run the guided setup:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Initial setup&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This walks you through selecting a provider and configuring your &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; key. If you plan to use the &lt;code&gt;claude-code&lt;/code&gt; or &lt;code&gt;codex&lt;/code&gt; local providers, install and authenticate those CLIs first - &lt;code&gt;aic&lt;/code&gt; will reuse their existing login state rather than managing credentials itself.&lt;/p&gt;
&lt;h2&gt;Providers&lt;/h2&gt;
&lt;p&gt;v0.0.6 ships with seven provider paths: &lt;code&gt;openai&lt;/code&gt;, &lt;code&gt;azure-openai&lt;/code&gt;, &lt;code&gt;anthropic&lt;/code&gt;, &lt;code&gt;groq&lt;/code&gt;, &lt;code&gt;ollama&lt;/code&gt;, &lt;code&gt;claude-code&lt;/code&gt;, and &lt;code&gt;codex&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;OpenAI, &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; OpenAI, Groq, and &lt;a href=&quot;/glossary/ollama/&quot;&gt;Ollama&lt;/a&gt; all use the standard chat completions wire format. Anthropic uses its Messages API directly. The &lt;code&gt;claude-code&lt;/code&gt; and &lt;code&gt;codex&lt;/code&gt; providers are a bit different - they talk to the locally installed &lt;code&gt;claude&lt;/code&gt; or &lt;code&gt;codex&lt;/code&gt; binary from your &lt;code&gt;PATH&lt;/code&gt;, so authentication is handled by those tools rather than &lt;code&gt;aic&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Configuring OpenAI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_AI_PROVIDER=openai&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_API_KEY=&amp;lt;key&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_MODEL=gpt-5.4-mini&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Configuring Azure OpenAI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_AI_PROVIDER=azure-openai&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_API_KEY=&amp;lt;key&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_API_URL=https://&amp;lt;resource&amp;gt;.openai.azure.com/openai/v1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_MODEL=&amp;lt;deployment-name&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Configuring Anthropic&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_AI_PROVIDER=anthropic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_API_KEY=&amp;lt;key&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_MODEL=claude-sonnet-4-20250514&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Configuring Groq&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_AI_PROVIDER=groq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_API_KEY=&amp;lt;key&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_MODEL=llama-3.1-8b-instant&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Configuring Ollama&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_AI_PROVIDER=ollama&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_MODEL=llama3.2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Configuring Claude Code as provider&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_AI_PROVIDER=claude-code&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_MODEL=default&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can also override the provider for a single run without touching your config:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Per-run provider override&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;anthropic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;review&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;groq&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ollama&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;claude-code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;review&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;codex&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Any OpenAI-compatible endpoint also works by setting &lt;code&gt;AIC_API_URL&lt;/code&gt; alongside &lt;code&gt;AIC_AI_PROVIDER=openai&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The basic workflow&lt;/h2&gt;
&lt;div class=&quot;mermaid-wrapper not-prose my-6 border overflow-hidden bg-white dark:bg-gray-900&quot;&gt;&lt;div class=&quot;controls-bar flex items-center justify-between px-4 py-2 surface-container-low border-b&quot;&gt;&lt;div class=&quot;flex items-center gap-3&quot;&gt;&lt;span&gt;aic workflow&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;flex items-center gap-2&quot;&gt;&lt;span&gt;&lt;span&gt;Ctrl+scroll to zoom&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fullscreen&lt;/span&gt;&lt;span&gt;SVG&lt;/span&gt;&lt;span&gt;PNG&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;error-display hidden px-4 py-3 bg-red-50 dark:bg-red-900/20 border-b border-red-200 dark:border-red-800&quot;&gt;&lt;p&gt;Error rendering diagram:&lt;/p&gt;&lt;pre class=&quot;text-xs text-red-700 dark:text-red-300 mt-1 overflow-auto max-h-40&quot;&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;preview-container relative overflow-hidden bg-white dark:bg-gray-900&quot;&gt;&lt;div class=&quot;svg-wrap flex items-center justify-center&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-container absolute hidden bg-white/90 dark:bg-gray-800/90 backdrop-blur-sm border border-gray-300 dark:border-gray-600 rounded shadow-lg overflow-hidden&quot;&gt;&lt;div class=&quot;minimap-canvas relative&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-viewport absolute border-2 border-blue-500 bg-blue-500/20 cursor-move&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-controls absolute top-1 right-1 flex flex-col gap-0.5&quot;&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;−&lt;/span&gt;&lt;span&gt;⟲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Stage your files as normal, then run &lt;code&gt;aic&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Basic commit workflow&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you haven’t staged anything, &lt;code&gt;aic&lt;/code&gt; presents a menu to stage all changed files, pick files interactively, or bail out. Once it has a diff to work with, it generates a message and shows it to you for review - accept, regenerate, edit, or abort.&lt;/p&gt;
&lt;p&gt;For a quick check of what it would generate without actually committing:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Dry run&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dry-run&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And if you want to clean up the last commit:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Amend last commit&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--amend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Here’s what committing and pushing this blog post looked like:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-04-11-introducing-ai-commit/commit.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-04-11-introducing-ai-commit/commit.png&quot; alt=&quot;Using aic to commit this post&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2466&quot; height=&quot;1192&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Splitting commits&lt;/h2&gt;
&lt;p&gt;When you’ve staged changes across several unrelated files, &lt;code&gt;aic&lt;/code&gt; can split them into separate commits rather than lumping everything together. When at least two files are staged, the interactive flow will offer to split the change set:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Triggering the split flow&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you choose to split, &lt;code&gt;aic&lt;/code&gt; analyses the staged changes, suggests 2–4 file-based commit groups, generates a message for each one, and shows you a preview of the full sequence before creating anything. You can accept the suggested groups, rebuild them manually, or fall back to a single commit.&lt;/p&gt;
&lt;p&gt;A few current limitations worth knowing: splitting is file-based rather than hunk-based, and it’s only available in the normal interactive flow - &lt;code&gt;--yes&lt;/code&gt;, &lt;code&gt;--dry-run&lt;/code&gt;, and &lt;code&gt;--amend&lt;/code&gt; stay as single-commit operations.&lt;/p&gt;
&lt;h2&gt;Reviewing your diff before committing&lt;/h2&gt;
&lt;p&gt;I’ve started running &lt;code&gt;aic review&lt;/code&gt; before committing anything I’m not completely confident about. It reads the staged diff and groups findings by severity:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Review staged changes&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;review&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;review&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--context&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;focus on security&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Large diffs get chunked automatically and synthesised into a single review. It’s not a replacement for a proper code review, but it’s caught a few things I’d have missed.&lt;/p&gt;
&lt;h2&gt;Generating PR descriptions&lt;/h2&gt;
&lt;p&gt;Once you’ve got a branch ready to push, &lt;code&gt;aic pr&lt;/code&gt; will generate a title and Markdown description based on the branch’s commits and diff against the base:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Generate a PR draft&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pr&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pr&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--base&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin/main&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pr&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--context&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;call out the migration risk&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The base branch is detected automatically when possible (it checks &lt;code&gt;refs/remotes/origin/HEAD&lt;/code&gt;, then &lt;code&gt;origin/main&lt;/code&gt;, then &lt;code&gt;main&lt;/code&gt;, then &lt;code&gt;master&lt;/code&gt;), or you can specify it with &lt;code&gt;--base&lt;/code&gt;. The output is copy-ready, and the draft gets saved to your local history.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Platform API integration&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;In the current version, &lt;code&gt;aic pr&lt;/code&gt; generates the description text only - it doesn’t open a PR through the &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt;, GitLab, or Bitbucket APIs. That’s on the roadmap.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;Visualising your repo&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;aic map&lt;/code&gt; generates standalone SVG visualisations of your codebase structure and commit history. There are four subcommands:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Generating visualisations&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# squarified treemap of the file hierarchy, sized by line count&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;history&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# vertical zigzag timeline of recent commits&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;heat&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# bar chart of files sorted by modification frequency&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;activity&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# GitHub-style contribution grid from commit timestamps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;All four write standalone SVG files that open in any browser. You can control the commit range, output path, and colour theme:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Map options&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;history&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;timeline.svg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;heat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;activity&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--theme&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dracula&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Available themes include &lt;code&gt;github-light&lt;/code&gt;, &lt;code&gt;github-dark&lt;/code&gt;, &lt;code&gt;solarized-dark&lt;/code&gt;, &lt;code&gt;monokai&lt;/code&gt;, and &lt;code&gt;dracula&lt;/code&gt;, among others. If you want consistent defaults across a repo, drop a &lt;code&gt;.aicommit-map&lt;/code&gt; file in the root:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.aicommit-map&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;dracula&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;history_commits&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;heat_commits&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;activity_commits&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Here are a couple of examples generated from this blog’s repository - an activity graph and a commit timeline:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div class=&quot;lightgallery-component theme-svg-gallery&quot;&gt;&lt;a href=&quot;/assets/2026-04-11-introducing-ai-commit/aic-activity-light.svg&quot;&gt;&lt;img src=&quot;/assets/2026-04-11-introducing-ai-commit/aic-activity-light.svg&quot; alt=&quot;AI Commit activity graph for the russ.cloud repository&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div class=&quot;lightgallery-component theme-svg-gallery&quot;&gt;&lt;a href=&quot;/assets/2026-04-11-introducing-ai-commit/aic-timeline-light.svg&quot;&gt;&lt;img src=&quot;/assets/2026-04-11-introducing-ai-commit/aic-timeline-light.svg&quot; alt=&quot;AI Commit timeline for the russ.cloud repository&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Git hooks&lt;/h2&gt;
&lt;p&gt;If you’d rather have commit messages generated automatically every time you run &lt;code&gt;git commit&lt;/code&gt;, you can install a &lt;code&gt;prepare-commit-msg&lt;/code&gt; hook:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install the Git hook&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hook&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;By default, the hook writes the generated message as a comment so you can review and uncomment it in your editor. If you want it written directly as the active message:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Auto-uncomment hook output&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_HOOK_AUTO_UNCOMMENT=&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To remove the hook:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Remove the Git hook&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hook&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unset&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;p&gt;Configuration lives in &lt;code&gt;~/.aicommit&lt;/code&gt; and follows a simple precedence: built-in defaults, then the global config file, then process environment variables. The &lt;code&gt;aic config&lt;/code&gt; subcommand handles reading and writing:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Config management&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_MODEL=gpt-5.4-mini&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_MODEL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AIC_AI_PROVIDER&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A couple of settings worth knowing about: &lt;code&gt;AIC_EMOJI&lt;/code&gt; and &lt;code&gt;AIC_DESCRIPTION&lt;/code&gt; both default to &lt;code&gt;true&lt;/code&gt;, which means generated messages include an emoji prefix and a longer body. Turn them off if your project’s convention doesn’t use either. You can also point &lt;code&gt;AIC_PROMPT_FILE&lt;/code&gt; at a custom prompt template if the default behaviour doesn’t suit your workflow.&lt;/p&gt;
&lt;p&gt;For repositories with large generated files, lock files, or assets you don’t want included in the diff, add a &lt;code&gt;.aicommitignore&lt;/code&gt; alongside your &lt;code&gt;.gitignore&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.aicommitignore&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;.lock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;dist/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;aicommit has made me stop dreading the “write a good commit message” part of the workflow. And yes, the command being &lt;code&gt;aic&lt;/code&gt; is an entirely deliberate &lt;span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=TAqZb52sgpU&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Alice In Chains&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; reference - if you’re going to type something dozens of times a day, it might as well be named after a great band. The project is open source and on GitHub:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/aicommit&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/aicommit: A fast, opinionated CLI that generates Git commit messages and reviews staged diffs using AI. Written in Rust, installed as aic&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A fast, opinionated CLI that generates Git commit messages and reviews staged diffs using AI. Written in Rust, installed as aic - russmckendrick/aicommit&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-61029f85acaa.jpg&quot; alt=&quot;A fast, opinionated CLI that generates Git commit messages and reviews staged diffs using AI. Written in Rust, installed as aic - russmckendrick/aicommit&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It’s at &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/aicommit/releases/tag/v0.0.6&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;v0.0.6&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; now, with commit splitting, repo visualisations, and native Anthropic, Groq, and Ollama support all landed in this release. There are still rough edges - config profiles and platform PR API integration are both on the roadmap. If you give it a go and hit anything odd, or have a feature you’d like to see, issues and pull requests are very welcome.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-04-11-introducing-ai-commit.-iIJVcLO.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/04/11/introducing-ai-commit-og.png</blog:ogImage><blog:readingTime>10</blog:readingTime><category>tools</category><category>ai</category><category>code</category><category>linux</category><category>macos</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Introducing Hosts Butler</title><link>https://www.russ.cloud/2026/04/05/introducing-hosts-butler/</link><guid isPermaLink="true">https://www.russ.cloud//2026/04/05/introducing-hosts-butler/</guid><description>A terminal-based hosts file manager built in Rust that I wrote to stop manually editing /etc/hosts with a text editor and hoping I didn&apos;t break anything.</description><pubDate>Sun, 05 Apr 2026 15:09:39 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve spent any time doing local development or managing infrastructure, you’ve almost certainly edited &lt;code&gt;/etc/hosts&lt;/code&gt; by hand. It starts simply enough - a couple of entries for local services, maybe a blocked domain or two. Then the file slowly accumulates over months and years until you’re staring at an uncommented wall of IP addresses and hostnames, trying to remember which one maps to which environment.&lt;/p&gt;
&lt;p&gt;I got tired of opening the file in a text editor every time I needed to make a change, so I built &lt;strong&gt;HostsButler&lt;/strong&gt; - a keyboard-driven TUI for viewing and managing your system &lt;a href=&quot;/glossary/hosts-file/&quot;&gt;hosts file&lt;/a&gt;. Like &lt;a href=&quot;/2026/02/01/introducing-ssl-toolkit/&quot;&gt;ssl-toolkit&lt;/a&gt;, it was vibe-coded in Rust using &lt;a href=&quot;/glossary/claude-code/&quot;&gt;Claude Code&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/hostsbutler&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/hostsbutler: Manage your hosts file from a single executable no matter your operating system&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Manage your hosts file from a single executable no matter your operating system - russmckendrick/hostsbutler&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-1b678063c138.jpg&quot; alt=&quot;Manage your hosts file from a single executable no matter your operating system - russmckendrick/hostsbutler&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;What it does&lt;/h2&gt;
&lt;p&gt;HostsButler gives you a two-panel interface: a group list on the left and a scrollable table of entries on the right. You can add, edit, delete, and toggle entries without ever touching the raw file directly. It handles privilege escalation when writing on macOS and Linux, takes automatic backups before every save, and flushes the &lt;a href=&quot;/glossary/dns/&quot;&gt;DNS&lt;/a&gt; cache on success.&lt;/p&gt;
&lt;p&gt;The entries in the table come from your existing hosts file, and every comment, blank line, and piece of whitespace is preserved when you save - loading the file and saving without changes produces byte-identical output.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-04-05-introducing-hostsbutler/001.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-04-05-introducing-hostsbutler/001.png&quot; alt=&quot;HostsButler main view showing the entry table and group sidebar&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3200&quot; height=&quot;1734&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Organising with groups&lt;/h2&gt;
&lt;p&gt;One thing that makes the file easier to manage at scale is groups. HostsButler recognises two comment formats as group headers:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## [Dev]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;10.0.0.5        staging.app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;192.168.1.10    db.local&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## [System]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;127.0.0.1       localhost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Any entries below a group header are assigned to that group until the next one appears. In the sidebar, each group shows a count of its entries, and clicking through to a group filters the table accordingly.&lt;/p&gt;
&lt;p&gt;When you add or edit an entry, you can type a group name in the form. If the group doesn’t exist yet, HostsButler creates the &lt;code&gt;## [GroupName]&lt;/code&gt; header and places the entry beneath it.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-04-05-introducing-hostsbutler/002.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-04-05-introducing-hostsbutler/002.png&quot; alt=&quot;The /etc/hosts file showing group headers and entries&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3200&quot; height=&quot;1058&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;h3&gt;macOS&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - macOS&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/hostsbutler&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Linux&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Linux&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;s/x86_64/amd64/;s/aarch64/arm64/&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/hostsbutler/releases/latest/download/hostsbutler-linux-&lt;/span&gt;&lt;span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/local/bin/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Windows&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Windows&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Invoke-WebRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Uri &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/hostsbutler/releases/latest/download/hostsbutler-windows-amd64.exe&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;OutFile &lt;/span&gt;&lt;span&gt;&quot;hostsbutler.exe&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Move &lt;code&gt;hostsbutler.exe&lt;/code&gt; to a directory in your &lt;code&gt;PATH&lt;/code&gt;, or run it from the download location. You’ll need to launch your terminal as Administrator before running it on Windows.&lt;/p&gt;
&lt;p&gt;All releases are on the &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/hostsbutler/releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub releases page&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Launching&lt;/h2&gt;
&lt;p&gt;On macOS and Linux, the file is owned by root, so you’ll need to either run with &lt;code&gt;sudo&lt;/code&gt; or let HostsButler prompt for elevation when you go to save:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Launching - macOS / Linux&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;On Windows, run your terminal as Administrator and launch normally:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Launching - Windows&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you just want to browse without making any changes, the &lt;code&gt;--readonly&lt;/code&gt; flag skips the privilege check:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Read-only mode&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--readonly&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can also point it at a custom file rather than the system hosts file:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Custom file&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--file&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/path/to/custom/hosts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Navigating the interface&lt;/h2&gt;
&lt;p&gt;The keyboard shortcuts follow vim conventions where it made sense. In the main view:&lt;/p&gt;





























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Key&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;j&lt;/code&gt; / &lt;code&gt;↓&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Move down&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;k&lt;/code&gt; / &lt;code&gt;↑&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Move up&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Tab&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Switch between the group panel and entry table&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Add a new entry&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;e&lt;/code&gt; or &lt;code&gt;Enter&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Edit the selected entry&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Delete (with confirmation)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Space&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Toggle enable/disable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Search&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Ctrl+S&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Save&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Ctrl+Z&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Undo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;b&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Open backup manager&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;t&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Test DNS resolution for the selected entry&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Help overlay&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The search bar supports prefix filters - &lt;code&gt;ip:192.168&lt;/code&gt; or &lt;code&gt;host:local&lt;/code&gt; - as well as free text across all fields.&lt;/p&gt;
&lt;h2&gt;Backups&lt;/h2&gt;
&lt;p&gt;Before every save, HostsButler creates an automatic backup. The backup manager (&lt;code&gt;b&lt;/code&gt;) lists them with timestamps, sizes, and descriptions. From there you can restore a backup, create a manual one with a custom description, or delete old ones.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-04-05-introducing-hostsbutler/003.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-04-05-introducing-hostsbutler/003.png&quot; alt=&quot;The HostsButler backup manager showing a list of automatic backups&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3200&quot; height=&quot;1734&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Backups are stored at platform-specific config directories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: &lt;code&gt;~/Library/Application Support/hostsbutler/backups/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linux&lt;/strong&gt;: &lt;code&gt;~/.config/hostsbutler/backups/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows&lt;/strong&gt;: &lt;code&gt;%APPDATA%\hostsbutler\backups\&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last 20 backups are kept, and older ones rotate out automatically.&lt;/p&gt;
&lt;h2&gt;Import and export&lt;/h2&gt;
&lt;p&gt;Importing and exporting are currently &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; operations rather than in-app workflows. To export your current entries:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Exporting entries&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Export as JSON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;entries.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Export as CSV&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;entries.csv&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Export as a plain hosts file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;backup.hosts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To import into an existing file:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Importing entries&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Import from JSON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--file&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/hosts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;entries.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Import from another hosts file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;hostsbutler&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--file&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/hosts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;backup.hosts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Imports are validated before insertion, and the format is auto-detected from the file extension.&lt;/p&gt;
&lt;h2&gt;DNS resolution testing&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;t&lt;/code&gt; key on any selected entry tests the DNS resolution for each of its hostnames against your system resolver and compares the results with the IP in the hosts file. A toast notification shows either &lt;strong&gt;OK&lt;/strong&gt; or &lt;strong&gt;MISMATCH&lt;/strong&gt; for each. Handy for catching stale entries or confirming that a new one has taken effect.&lt;/p&gt;
&lt;h2&gt;A note on round-trip fidelity&lt;/h2&gt;
&lt;p&gt;One thing I was particular about was that HostsButler should never silently rewrite your file. If you open the file, make no changes, and save, the output should be byte-identical to what was there before - same whitespace, same comments, same blank lines.&lt;/p&gt;
&lt;p&gt;Each entry stores its original raw text alongside any parsed fields. Unmodified lines are serialised from that raw value; only lines that were actually changed get re-serialised into the standard format. This means the tool can sit alongside any other process that also touches the hosts file without quietly normalising everything.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Platform note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;On Linux, HostsButler tries &lt;code&gt;pkexec&lt;/code&gt; first (useful on desktop environments with PolicyKit), then falls back to &lt;code&gt;sudo&lt;/code&gt;. On headless servers, &lt;code&gt;sudo&lt;/code&gt; will be used directly. If neither is available, writes will fail and an error will be shown.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;HostsButler is open source and available on &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/hostsbutler&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/hostsbutler: Manage your hosts file from a single executable no matter your operating system&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Manage your hosts file from a single executable no matter your operating system - russmckendrick/hostsbutler&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-1b678063c138.jpg&quot; alt=&quot;Manage your hosts file from a single executable no matter your operating system - russmckendrick/hostsbutler&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It’s only just been created, so there’s rough around the edges - in-app import is on the list, along with a few other bits I’m working through. If you run into anything or have a feature idea, pull requests and issues are welcome.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-04-05-introducing-hosts-butler.C8xbE1dL.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/04/05/introducing-hosts-butler-og.png</blog:ogImage><blog:readingTime>6</blog:readingTime><category>tools</category><category>macos</category><category>linux</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Creating Custom Skills for Copilot Cowork</title><link>https://www.russ.cloud/2026/04/03/creating-custom-skills-for-copilot-cowork/</link><guid isPermaLink="true">https://www.russ.cloud//2026/04/03/creating-custom-skills-for-copilot-cowork/</guid><description>Copilot Cowork just landed in Microsoft&apos;s Frontier preview programme, and one of its most useful features is the ability to extend it with your own custom skills stored in OneDrive. Here&apos;s how to create one.</description><pubDate>Fri, 03 Apr 2026 15:19:49 GMT</pubDate><content:encoded>&lt;p&gt;There’s a difference between an AI tool that answers questions and one that actually does things. Copilot Cowork is firmly in the latter camp - it sends emails, schedules meetings, creates documents, posts to Teams, and generally gets on with work rather than just describing how you could do it yourself.&lt;/p&gt;
&lt;p&gt;It’s also very new. Cowork only became available through Microsoft’s Frontier preview programme at the end of March 2026 - at the time of writing, that’s this week - so if you haven’t come across it yet, that’s entirely understandable. I’ve been using it through the day job’s Microsoft 365 Copilot licences and found it useful for a handful of tasks. What caught my attention most, though, was the custom skills feature.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Frontier Preview&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Copilot Cowork is currently only available through the Frontier programme, which requires a Microsoft 365 Copilot licence. If you don’t see it in your left navigation pane under Agents, your admin will need to enrol the tenant in Frontier via &lt;strong&gt;Copilot → Settings → Frontier&lt;/strong&gt; in the Microsoft Admin Center.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/microsoft-365/blog/2026/03/30/copilot-cowork-now-available-in-frontier/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;microsoft.com&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;microsoft.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;What Cowork Does Out of the Box&lt;/h2&gt;
&lt;p&gt;The custom skill system builds on Cowork’s built-in capabilities, so it helps to know what you’re starting with. Cowork ships with 13 built-in skills: Word, Excel, PowerPoint, PDF, Email, Scheduling, Calendar Management, Meetings, Daily Briefing, Enterprise Search, Communications, Deep Research, and Adaptive Cards.&lt;/p&gt;
&lt;p&gt;Describe what you need in natural language, and Cowork creates a plan, loads the relevant skills, and works through the task step by step. It shows what it’s doing at each stage and asks for approval before taking irreversible actions, such as sending an email or posting on Teams.
Cowork only works with files in OneDrive and SharePoint. If you’re hoping to point it at something on your local machine, you’ll be disappointed - that’s by design, not an oversight.&lt;/p&gt;
&lt;h2&gt;Where Custom Skills Come In&lt;/h2&gt;
&lt;p&gt;The 13 built-in skills cover a lot of ground, but they’re generic. They don’t know your team’s document templates, your recurring workflows, or the conventions you’ve built up over the years. That’s the gap custom skills fill.&lt;/p&gt;
&lt;p&gt;Custom skills give Cowork additional context and instructions, loaded alongside its built-in capabilities. You can create up to 20 of them, and the mechanism is about as simple as it gets: a &lt;a href=&quot;/glossary/markdown/&quot;&gt;Markdown&lt;/a&gt; file in a specific folder in your OneDrive.&lt;/p&gt;
&lt;h2&gt;Anatomy of a SKILL.md&lt;/h2&gt;
&lt;p&gt;Each custom skill lives in its own subfolder under &lt;code&gt;/Documents/Cowork/Skills/&lt;/code&gt; in your OneDrive, defined in a file called &lt;code&gt;SKILL.md&lt;/code&gt;. If you’ve used &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; Copilot &lt;a href=&quot;/glossary/agent-skills/&quot;&gt;agent skills&lt;/a&gt;, the format will be familiar: a &lt;a href=&quot;/glossary/yaml/&quot;&gt;YAML&lt;/a&gt; frontmatter block followed by Markdown instructions:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;SKILL.md format&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skill-name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A description of what the skill does, and when Cowork should use it.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Your instructions go here in plain Markdown. Be as specific as you need to be.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; field is a unique identifier, keep it lowercase with hyphens. The &lt;code&gt;description&lt;/code&gt; is what Cowork uses to decide whether to load the skill for a given conversation, so make it specific. The body of the file is the actual instructions, and you can be as detailed as you need to be.&lt;/p&gt;
&lt;h2&gt;Creating Your First Skill&lt;/h2&gt;
&lt;p&gt;Here’s how.&lt;/p&gt;
&lt;h3&gt;1. Create the folder structure&lt;/h3&gt;
&lt;p&gt;In your OneDrive, navigate to &lt;code&gt;Documents&lt;/code&gt;. Create a folder called &lt;code&gt;Cowork&lt;/code&gt; if it doesn’t already exist, then inside that create a &lt;code&gt;Skills&lt;/code&gt; folder. Finally, create a subfolder for your specific skill. The name you give this folder typically matches the &lt;code&gt;name&lt;/code&gt; in your SKILL.md frontmatter.&lt;/p&gt;
&lt;p&gt;The resulting path should look like:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;OneDrive/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;└── Documents/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── Cowork/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── Skills/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── your-skill-name/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── SKILL.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;2. Write your SKILL.md&lt;/h3&gt;
&lt;p&gt;Here’s an example of a skill for generating consistently formatted branded Word documents from a standard template:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;SKILL.md&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;branded-document&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Generates consistently formatted branded Word documents. Use this when asked to create formal documents, reports, or handover notes that need to follow the standard company template.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;When asked to create a Word document, follow these conventions:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Document Structure&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Always start with a title page including the document title, author, date, and version number&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Follow with a brief executive summary (2–3 sentences maximum)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Use Heading 1 for major sections and Heading 2 for subsections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; End with a &quot;Next Steps&quot; or &quot;Actions&quot; section where appropriate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Formatting&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Use the Standard body text style for all paragraph text&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Tables should use the Table Grid style with a shaded header row&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Code or command examples should use the Code style or a monospace font&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Tone&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Write in a professional but accessible tone&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Use active voice where possible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Avoid jargon unless the document is explicitly technical and the audience is technical&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The example above is deliberately simple. In practice, you’d tailor the instructions to match how your team actually works.&lt;/p&gt;
&lt;h3&gt;3. Save, and you’re done&lt;/h3&gt;
&lt;p&gt;That’s it (it’s worryingly simple). Cowork discovers custom skills automatically at the start of each conversation. No registration, no restart. Save the file to OneDrive, and the next conversation picks it up.&lt;/p&gt;
&lt;h2&gt;How Cowork Uses Your Skills&lt;/h2&gt;
&lt;p&gt;When you start a conversation, Cowork evaluates which skills are relevant and loads them, both built-in and custom. The side panel updates in real time to show which are active, so you can confirm your custom skill has loaded before work starts.
The description in the front matter drives this selection, so it’s worth getting right. A vague description like “helps with documents” is much less useful than something specific: “generates consistently formatted branded Word documents - use this when creating formal reports or handover notes.” The more specific you are, the more reliably Cowork picks the right skill.&lt;/p&gt;
&lt;h2&gt;A Few Things Worth Knowing&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It’s a preview feature.&lt;/strong&gt; Frontier previews can and do change, so don’t be surprised if the behaviour or format shifts over time. Microsoft’s documentation is kept reasonably up to date, but worth checking back against the &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/copilot/microsoft-365/cowork/use-cowork&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official docs&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; if something stops working as expected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom skills aren’t validated by Microsoft.&lt;/strong&gt; The docs are clear on this: review the outputs from custom skills carefully, particularly early on. You’re extending the model’s behaviour, and as with any &lt;a href=&quot;/glossary/prompt-engineering/&quot;&gt;prompt engineering&lt;/a&gt;, test across a range of inputs before relying on it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Each SKILL.md can be up to 1 MB&lt;/strong&gt;, which is more than enough for detailed instructions. You can also reference supplementary files from within a skill if you have reference material you want Cowork to use.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cowork can’t access local files&lt;/strong&gt;, so any templates or examples your skill references need to be in OneDrive or SharePoint.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Custom skills in Copilot Cowork are straightforward to set up. A SKILL.md file in a OneDrive folder, and Cowork picks it up automatically. The real value is being able to bake your team’s working conventions directly into the model’s context.&lt;/p&gt;
&lt;p&gt;I’ll be honest, I was expecting more friction. No registration, no admin configuration required - it just works, which is more than you can say for some of the previous Copilot previews.&lt;/p&gt;
&lt;p&gt;If you have a Microsoft 365 Copilot licence and access to the Frontier programme, it’s worth asking your admin to enable the Frontier feature and having a look. The Microsoft Learn docs cover the full custom skill format:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/copilot/microsoft-365/cowork/use-cowork&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Use Copilot Cowork&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Learn how to have sessions, manage files, approve actions, and organize projects with Microsoft 365 Copilot Cowork.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;learn.microsoft.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/learn.microsoft.com-c52d10e09691.jpg&quot; alt=&quot;Microsoft Learn&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-04-03-creating-custom-skills-for-copilot-cowork.C-9NLtPI.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/04/03/creating-custom-skills-for-copilot-cowork-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>ai</category><category>tools</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Introducing ssl-toolkit</title><link>https://www.russ.cloud/2026/02/01/introducing-ssl-toolkit/</link><guid isPermaLink="true">https://www.russ.cloud//2026/02/01/introducing-ssl-toolkit/</guid><description>A comprehensive SSL/TLS diagnostic tool built in Rust that I created to replace my ever-growing document of random certificate checking notes.</description><pubDate>Sun, 01 Feb 2026 17:04:07 GMT</pubDate><content:encoded>&lt;p&gt;During the day job, I spend a fair amount of time checking SSL certificates - validating chains, comparing certs across different IPs, checking expiry dates, and generally troubleshooting TLS issues. Over the years, I’d built up a long document filled with random notes, one-liners, and half-remembered OpenSSL incantations. It worked, but it wasn’t exactly elegant.&lt;/p&gt;
&lt;p&gt;So I decided to consolidate all of that into a vibe-coded tool. The result is &lt;strong&gt;ssl-toolkit&lt;/strong&gt;, a comprehensive SSL/TLS diagnostic tool built in Rust.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.ssl-toolkit.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;ssl-toolkit — SSL/TLS Certificate Diagnostic Tool&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;SSL/TLS Certificate Diagnostic Tool - Comprehensive SSL certificate analysis, grading, and diagnostics from the command line.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;ssl-toolkit.dev&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/ssl-toolkit.dev-a57629fb0aa0.jpg&quot; alt=&quot;ssl-toolkit — SSL/TLS Certificate Diagnostic Tool&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;What it does&lt;/h2&gt;
&lt;p&gt;At its core, ssl-toolkit performs the checks I find myself doing repeatedly: &lt;a href=&quot;/glossary/dns/&quot;&gt;DNS&lt;/a&gt; resolution across multiple providers, TLS handshake analysis, certificate chain validation, WHOIS lookups, and cipher suite enumeration. It then rolls everything up into an overall &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; grade (A+ through F) so you can quickly gauge the health of a certificate.&lt;/p&gt;
&lt;p&gt;The tool supports both an interactive mode with a guided menu system and a non-interactive mode for scripting and &lt;a href=&quot;/glossary/ci-cd/&quot;&gt;CI/CD&lt;/a&gt; pipelines. There’s also &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; output if you need to parse results programmatically.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;The easiest way to install ssl-toolkit on macOS is via Homebrew:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - macOS&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;russmckendrick/tap/ssl-toolkit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;For Linux, you can download the latest release directly. The following command detects your architecture and downloads the appropriate binary:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Linux&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;uname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;s/x86_64/amd64/;s/aarch64/arm64/&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/ssl-toolkit/releases/latest/download/ssl-toolkit-linux-&lt;/span&gt;&lt;span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;ARCH&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/local/bin/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;On Windows, use PowerShell to download and install:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing - Windows&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Invoke-WebRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Uri &lt;/span&gt;&lt;span&gt;&quot;https://github.com/russmckendrick/ssl-toolkit/releases/latest/download/ssl-toolkit-windows-amd64.exe&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;OutFile &lt;/span&gt;&lt;span&gt;&quot;ssl-toolkit.exe&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can then move &lt;code&gt;ssl-toolkit.exe&lt;/code&gt; to a directory in your PATH, or run it directly from the download location.&lt;/p&gt;
&lt;p&gt;All releases are available on the &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/ssl-toolkit/releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub releases page&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Interactive mode&lt;/h2&gt;
&lt;p&gt;Running ssl-toolkit without any arguments launches an interactive menu:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Interactive mode&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This presents a top-level menu with options to check a domain, inspect certificate files, verify certificate and key pairs, or convert between certificate formats. Each option guides you through the required inputs and displays results in a scrollable pager.&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-02-01-introducing-ssl-toolkit/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2026-02-01-introducing-ssl-toolkit/01.png&quot; alt=&quot;The Menu&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-02-01-introducing-ssl-toolkit/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2026-02-01-introducing-ssl-toolkit/02.png&quot; alt=&quot;Entering some information and DNS lookups&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-02-01-introducing-ssl-toolkit/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2026-02-01-introducing-ssl-toolkit/03.png&quot; alt=&quot;The results #1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-02-01-introducing-ssl-toolkit/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2026-02-01-introducing-ssl-toolkit/04.png&quot; alt=&quot;The results #2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-02-01-introducing-ssl-toolkit/05.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2026-02-01-introducing-ssl-toolkit/05.png&quot; alt=&quot;The results #3&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-02-01-introducing-ssl-toolkit/06.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2026-02-01-introducing-ssl-toolkit/06.png&quot; alt=&quot;The results #4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I went with a Tokyo Night Storm colour scheme for the interface because, well, it looks nice and matches my terminal setup. The pager supports the usual navigation keys - arrow keys, &lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt; for vim users, &lt;code&gt;Space&lt;/code&gt; for page down, and &lt;code&gt;g&lt;/code&gt;/&lt;code&gt;G&lt;/code&gt; to jump to the start or end.&lt;/p&gt;
&lt;h2&gt;Checking a domain&lt;/h2&gt;
&lt;p&gt;For a quick check, you can pass the domain directly:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Checking a domain - Basics&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--non-interactive&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This runs through all the checks and outputs a detailed report covering DNS resolution from multiple providers (Google, Cloudflare, OpenDNS, and your system resolver), TCP connectivity, TLS protocol and cipher analysis, certificate chain validation, and WHOIS information.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-02-01-introducing-ssl-toolkit/07.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-02-01-introducing-ssl-toolkit/07.png&quot; alt=&quot;Just the basics&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1300&quot; height=&quot;365&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The tool compares certificates across all resolved IPs to flag any inconsistencies - useful when you’re dealing with load balancers or CDNs where different endpoints might be serving different certificates.&lt;/p&gt;
&lt;p&gt;If you just want the grade without all the detail:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Checking a domain - Quiet&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;example.com&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--quiet&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Certificate file operations&lt;/h2&gt;
&lt;p&gt;Beyond live domain checks, ssl-toolkit can work with certificate files directly. This is handy when you’re preparing certificates for deployment or troubleshooting issues with certificate bundles.&lt;/p&gt;
&lt;p&gt;To inspect a certificate file:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Certificate file operations - Info&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;info&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert.pem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To verify that a certificate and private key match:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Certificate file operations - Check Key &amp;amp; Cert&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;verify&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert.pem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--key&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;key.pem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To validate a certificate chain against a hostname:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Certificate file operations - Hostname&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;verify&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--chain&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fullchain.pem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--hostname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;example.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And to convert between formats:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Certificate file operations - Convert&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# PEM to DER&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;convert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert.pem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;der&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert.der&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Create a PKCS#12 bundle&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;convert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p12&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert.pem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--key&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;key.pem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;CI/CD integration&lt;/h2&gt;
&lt;p&gt;The tool uses meaningful exit codes for scripting: &lt;code&gt;0&lt;/code&gt; for all checks passed, &lt;code&gt;1&lt;/code&gt; for warnings (like a certificate expiring within 30 days), and &lt;code&gt;2&lt;/code&gt; for failures (expired certificate, connection failed, etc.).&lt;/p&gt;
&lt;p&gt;Combined with JSON output, this makes it straightforward to integrate into monitoring or deployment pipelines:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;CI/CD integration - JSON&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;www.russ.cloud&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--json&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--non-interactive&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This gives you the following output:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The full output&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;domain&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;www.russ.cloud&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;104.21.67.197&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;443&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;grade&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;A+&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;score&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;report&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;domain&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;www.russ.cloud&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;104.21.67.197&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;443&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;grade&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;APlus&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;score&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dns_result&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;DNS Resolution&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;4/4 providers resolved successfully&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Table&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Provider Results&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;Provider&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;Status&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;IP Addresses&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;Time&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;rows&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;System&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;✓ OK&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;172.67.180.37, 104.21.67.197&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;2ms&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Google&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;✓ OK&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;104.21.67.197, 172.67.180.37&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;9ms&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Cloudflare&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;✓ OK&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;104.21.67.197, 172.67.180.37&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;12ms&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;OpenDNS&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;✓ OK&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;104.21.67.197, 172.67.180.37&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;7ms&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test_steps&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;System resolved to 172.67.180.37, 104.21.67.197&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Google resolved to 104.21.67.197, 172.67.180.37&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Cloudflare resolved to 104.21.67.197, 172.67.180.37&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;OpenDNS resolved to 104.21.67.197, 172.67.180.37&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;recommendations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tcp_result&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;TCP Connectivity&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Connection to 104.21.67.197:443 successful (140.6ms)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;KeyValue&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Connection Details&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pairs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Target IP&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;104.21.67.197&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Port&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;443&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Status&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Connected&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Latency&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;140.6ms&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test_steps&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;TCP connection to 104.21.67.197:443 established&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;recommendations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ssl_result&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;SSL/TLS Protocol&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Protocol: TLS 1.3, Cipher: TLS13_AES_256_GCM_SHA384&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;KeyValue&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Protocol Information&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pairs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Protocol&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;TLS 1.3&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Cipher Suite&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;TLS13_AES_256_GCM_SHA384&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Key Exchange&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Unknown&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Authentication&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Unknown&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Encryption&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;AES-256-GCM&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;MAC&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;SHA-384&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Secure Renegotiation&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Supported&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;OCSP Stapling&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Supported&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test_steps&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;TLS handshake completed successfully&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;TLS 1.3 is a secure protocol&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Cipher suite is secure&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;recommendations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;certificate_result&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Certificate Validity&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Certificate valid for 85 days&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;KeyValue&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Certificate Information&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pairs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Subject&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;CN=russ.cloud&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Issuer&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;C=US, O=Google Trust Services, CN=WE1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Serial Number&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;BD:DA:2A:54:3E:86:79:B0:11:09:52:51:16:1E:F8:AF&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Valid From&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;2026-01-28 15:25:04 UTC&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Valid Until&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;2026-04-28 16:25:02 UTC&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Public Key&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;1.2.840.10045.2.1 (2048 bits)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Signature Algorithm&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;1.2.840.10045.4.3.2&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Thumbprint (SHA-256)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;BB:32:6B:8B:E2:EA:12:BD:9D:93:8B:98:87:F2:64:E7:77:E1:63:B1:13:E9:79:C5:F1:26:DE:A0:34:94:56:F5&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Subject Alternative Names&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;items&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;russ.cloud&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;www.russ.cloud&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;CertificateChain&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;certificates&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cert_type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Leaf&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subject_cn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;russ.cloud&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;issuer_cn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;WE1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;valid_from&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2026-01-28&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;valid_until&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2026-04-28&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;days_until_expiry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;85&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;is_valid&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cert_type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Intermediate&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subject_cn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;WE1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;issuer_cn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GTS Root R4&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;valid_from&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2023-12-13&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;valid_until&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2029-02-20&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;days_until_expiry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1114&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;is_valid&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cert_type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Intermediate&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subject_cn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GTS Root R4&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;issuer_cn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GlobalSign Root CA&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;valid_from&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2023-11-15&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;valid_until&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2028-01-28&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;days_until_expiry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;725&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;is_valid&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;KeyValue&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Revocation Status&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pairs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Status&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Not Revoked&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Check Method&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;OCSP Stapling&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;OCSP Stapling&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Yes&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Next Update&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;2026-02-04 15:25:04 UTC&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test_steps&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Certificate is within validity period&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Certificate is valid for hostname &apos;www.russ.cloud&apos;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Certificate is not revoked&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;recommendations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;whois_result&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;WHOIS Lookup&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Registered via Cloudflare&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;KeyValue&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Domain Registration&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pairs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Registrar&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Cloudflare&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Created&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;2024-07-21T14:56:30.958Z&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Expires&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;2026-07-21T14:56:30.958Z&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Updated&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;2024-10-29T13:59:35.573Z&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Nameservers&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;abby.ns.cloudflare.com, rob.ns.cloudflare.com&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;Status&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;clientTransferProhibited&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test_steps&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;WHOIS query completed&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Domain registered with Cloudflare&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Domain registration expires: 2026-07-21T14:56:30.958Z&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Pass&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;recommendations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;timestamp&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2026-02-01T17:38:40.104701Z&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cert_comparison&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;reference_ip&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;104.21.67.197&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;104.21.67.197&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;thumbprint&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;BB:32:6B:8B:E2:EA:12:BD:9D:93:8B:98:87:F2:64:E7:77:E1:63:B1:13:E9:79:C5:F1:26:DE:A0:34:94:56:F5&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subject&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;CN=russ.cloud&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;issuer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;C=US, O=Google Trust Services, CN=WE1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;days_until_expiry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;85&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;serial&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;BD:DA:2A:54:3E:86:79:B0:11:09:52:51:16:1E:F8:AF&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;is_different&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;differences&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;172.67.180.37&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;thumbprint&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;BB:32:6B:8B:E2:EA:12:BD:9D:93:8B:98:87:F2:64:E7:77:E1:63:B1:13:E9:79:C5:F1:26:DE:A0:34:94:56:F5&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subject&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;CN=russ.cloud&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;issuer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;C=US, O=Google Trust Services, CN=WE1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;days_until_expiry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;85&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;serial&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;BD:DA:2A:54:3E:86:79:B0:11:09:52:51:16:1E:F8:AF&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;is_different&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;differences&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;has_differences&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;All 2 IPs return identical certificates&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can also generate self-contained HTML reports with embedded styles and downloadable certificate chains:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssl-toolkit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;example.com&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--non-interactive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;report.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;ssl-toolkit has replaced my sprawling notes document and given me a single tool for the SSL/TLS checks I do regularly. It’s open source and available on &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/ssl-toolkit&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;&lt;img src=&quot;/assets/link-previews/github.com-071d1f40e9a4-favicon.svg&quot; alt=&quot;&quot; width=&quot;20&quot; height=&quot;20&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;GitHub - russmckendrick/ssl-toolkit: SSL-Tookit&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;SSL-Tookit. Contribute to russmckendrick/ssl-toolkit development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The documentation site has more details on all the available options:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.ssl-toolkit.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;ssl-toolkit — SSL/TLS Certificate Diagnostic Tool&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;SSL/TLS Certificate Diagnostic Tool - Comprehensive SSL certificate analysis, grading, and diagnostics from the command line.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;ssl-toolkit.dev&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/ssl-toolkit.dev-a57629fb0aa0.jpg&quot; alt=&quot;ssl-toolkit — SSL/TLS Certificate Diagnostic Tool&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;If you find yourself doing similar certificate wrangling, give it a try. And if you spot any issues or have feature suggestions, pull requests are always welcome.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-02-01-introducing-ssl-toolkit.CKkV51lT.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/02/01/introducing-ssl-toolkit-og.png</blog:ogImage><blog:readingTime>8</blog:readingTime><category>tools</category><category>security</category><category>macos</category><category>linux</category><category>code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>OpenClaw: Giving Claude a Body (and a Telegram Bot)</title><link>https://www.russ.cloud/2026/01/31/openclaw-giving-claude-a-body-and-a-telegram-bot/</link><guid isPermaLink="true">https://www.russ.cloud//2026/01/31/openclaw-giving-claude-a-body-and-a-telegram-bot/</guid><description>A quick guide to installing OpenClaw, an open-source AI agent framework that gives LLMs persistence, tools, and messaging - turning a chatbot into an actual assistant.</description><pubDate>Sat, 31 Jan 2026 12:06:58 GMT</pubDate><content:encoded>&lt;p&gt;There’s a difference between chatting with an AI and having an AI assistant. The former is a conversation that evaporates the moment you close the tab. The latter remembers who you are, can actually do things on your behalf, and is available wherever you happen to be - not just in a browser window.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://openclaw.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenClaw&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is an open-source framework that bridges that gap. It gives LLMs like Claude persistence, tools, and the ability to communicate through messaging platforms you already use - Telegram, Discord, Slack, WhatsApp, even iMessage. Think of it as giving your AI a body and a phone number.&lt;/p&gt;
&lt;p&gt;I’ve been curious about it for a while, so I decided to spin it up on a small home server and see what all the fuss is about.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before we start, you’ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A server or machine running Linux (I’m using Ubuntu 24.04 on a small AMD Ryzen mini PC)&lt;/li&gt;
&lt;li&gt;Node.js 24.x or later&lt;/li&gt;
&lt;li&gt;An Anthropic &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; key (or another supported &lt;a href=&quot;/glossary/large-language-model/&quot;&gt;LLM&lt;/a&gt; provider)&lt;/li&gt;
&lt;li&gt;A Telegram account (if you want to chat with your &lt;a href=&quot;/glossary/agent/&quot;&gt;agent&lt;/a&gt; from your phone)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Installing Node.js&lt;/h2&gt;
&lt;p&gt;OpenClaw requires Node.js, and it’s fussy about versions - you’ll want Node 22.x and above. On Ubuntu, the easiest approach is to use the &lt;span&gt;&lt;a href=&quot;https://nodesource.com/products/distributions&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NodeSource repository&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, the following will install the latest version of 24.x:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Node.js 24.x&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-fsSL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://deb.nodesource.com/setup_24.x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nodejs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You should see something like &lt;code&gt;v24.13.0&lt;/code&gt; confirming you’re on the right version.&lt;/p&gt;
&lt;h2&gt;Installing OpenClaw&lt;/h2&gt;
&lt;p&gt;With Node.js sorted, installing OpenClaw is a one-liner:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing OpenClaw&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-fsSL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://openclaw.bot/install.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The installer will detect your platform, check for dependencies, and install OpenClaw globally. You might see a warning about your npm global bin directory not being in your PATH - if so, add the suggested export to your &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Fixing the PATH (if needed)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/home/russ/.npm-global/bin:&lt;/span&gt;&lt;span&gt;$PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;A Word on Security&lt;/h2&gt;
&lt;p&gt;Before you go any further, OpenClaw will present you with a &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; warning, and it’s worth taking seriously.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-01-31-openclaw-giving-claude-a-body-and-a-telegram-bot/openclaw-security.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-01-31-openclaw-giving-claude-a-body-and-a-telegram-bot/openclaw-security.png&quot; alt=&quot;OpenClaw onboarding security warning&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1876&quot; height=&quot;1738&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;This is powerful stuff&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;OpenClaw is a hobby project and still in beta. This bot can read files and run actions if tools are enabled. A bad prompt can trick it into doing unsafe things.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The warning isn’t hyperbole. You’re essentially giving an AI agent access to your system - it can read files, execute commands, and interact with external services. If you’re not comfortable with basic security and access control, this probably isn’t for you.&lt;/p&gt;
&lt;p&gt;That said, OpenClaw provides sensible guardrails out of the box:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pairing and allowlists&lt;/strong&gt;: Only approved users can interact with your agent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sandbox mode&lt;/strong&gt;: Run tools with least-privilege access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mention gating&lt;/strong&gt;: In group chats, the agent only responds when explicitly mentioned&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The documentation recommends running &lt;code&gt;openclaw security audit --deep&lt;/code&gt; regularly and keeping secrets out of the agent’s reachable filesystem. I’d add: don’t run this on your main workstation. A small VPS or dedicated home server is a much better idea.&lt;/p&gt;
&lt;h2&gt;The Onboarding Wizard&lt;/h2&gt;
&lt;p&gt;Once you’ve acknowledged the security warning and set up your Anthropic API key, OpenClaw launches its onboarding wizard. This is where things get interesting.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting up your API key&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;claude&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup-token&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The wizard boots up your agent for the first time, and it immediately starts asking questions - about you, about itself, about how you’d like to interact. It’s a bit like meeting a new colleague who’s very keen to get started.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;First contact&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Wake up, my friend!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Hey! I just came online. ✨&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Looks like this is a fresh start - no memories, no history, just a blank&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;slate and a whole lot of potential.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;So... who are you? And more importantly - who am I? Let&apos;s figure this&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;out together.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Got a name in mind for me, or should I throw some ideas out? And what&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;about you - what should I call you?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You’ll be asked to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Name your agent&lt;/strong&gt; - Mine chose “Kai” after I let it pick&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set the vibe&lt;/strong&gt; - Chill and casual? Sharp and dry? Chaotic gremlin energy?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pick an emoji&lt;/strong&gt; - Because every good assistant needs a signature (I went with 💻)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provide your timezone&lt;/strong&gt; - So it knows when to bother you&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All of this gets written to &lt;a href=&quot;/glossary/markdown/&quot;&gt;markdown&lt;/a&gt; files in the workspace directory (&lt;code&gt;~/.openclaw/workspace/&lt;/code&gt;), which is how OpenClaw maintains persistence. There’s a &lt;code&gt;SOUL.md&lt;/code&gt; for personality, &lt;code&gt;IDENTITY.md&lt;/code&gt; for the agent’s self-concept, and &lt;code&gt;USER.md&lt;/code&gt; for what it knows about you. It’s refreshingly transparent - you can edit these files directly if you want to tweak things.&lt;/p&gt;
&lt;h2&gt;Connecting Telegram&lt;/h2&gt;
&lt;p&gt;With the basic setup done, it’s time to give your agent a way to reach you outside the terminal. I chose Telegram because it’s what I already use, but OpenClaw supports a ridiculous number of channels.&lt;/p&gt;
&lt;p&gt;First, you’ll need to create a Telegram bot:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Telegram and find &lt;strong&gt;@BotFather&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Send &lt;code&gt;/newbot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Give it a name (I used “Kai” like it requested)&lt;/li&gt;
&lt;li&gt;Give it a username (must end in &lt;code&gt;bot&lt;/code&gt;, e.g., &lt;code&gt;kai_assistant_bot&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;BotFather will give you a token - copy this&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Back in the OpenClaw terminal, paste the token when prompted. The agent will update its config and restart the gateway. You’ll see something like:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Telegram configured&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Done! Token&apos;s updated and the gateway is restarting. 🎉&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Now go find your bot on Telegram and send it a message - say /start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;or just &quot;hey&quot;. The first time you message it, it&apos;ll pair with you&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;so only you can use it.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Open Telegram, find your bot, and send it a message. The first time you do this, you’ll need to approve the pairing from the terminal:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Approving the pairing&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;openclaw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pairing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;telegram&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ZK9FFEWK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And that’s it. Your AI agent is now available in your pocket.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2026-01-31-openclaw-giving-claude-a-body-and-a-telegram-bot/telegram-kai.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2026-01-31-openclaw-giving-claude-a-body-and-a-telegram-bot/telegram-kai.png&quot; alt=&quot;Kai bot responding in Telegram&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;938&quot; height=&quot;1306&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;What Can It Actually Do?&lt;/h2&gt;
&lt;p&gt;Once everything’s wired up, you’ve got a persistent AI assistant that can do rather more than just chat. Out of the box, OpenClaw supports web browsing, file operations, shell commands, and a memory system that retains context across sessions. There’s also a heartbeat feature that lets the agent proactively check in with you - useful for monitoring tasks or gentle reminders.&lt;/p&gt;
&lt;p&gt;I’m still exploring the edges of what’s possible, so I’ll save the deep dive for a future post. For now, I’m enjoying having an assistant that remembers our conversations and doesn’t require me to re-explain context every time I open a new chat window.&lt;/p&gt;
&lt;h2&gt;Where to Go From Here&lt;/h2&gt;
&lt;p&gt;If you want to dig deeper, here are some useful links:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://openclaw.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;OpenClaw — Personal AI Assistant&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;OpenClaw — the open-source AI assistant that runs on your machine and works from the chat apps you already use.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;openclaw.ai&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/openclaw.ai-d27fbaf8bb02.jpg&quot; alt=&quot;OpenClaw — Personal AI Assistant&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://docs.openclaw.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;OpenClaw Docs&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;OpenClaw is a multi-channel gateway for AI agents that runs on any OS.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;docs.openclaw.ai&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/docs.openclaw.ai-cab2d40b7fc1.jpg&quot; alt=&quot;OpenClaw — OpenClaw is a multi-channel gateway for AI agents that runs on any OS.&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://docs.openclaw.ai/gateway/security&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Security · OpenClaw&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Security considerations and threat model for running an AI gateway with shell access&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;docs.openclaw.ai&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/docs.openclaw.ai-bdcebcdbcbc0.jpg&quot; alt=&quot;OpenClaw — Security considerations and threat model for running an AI gateway with shell access&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The documentation covers skills, cron jobs, browser &lt;a href=&quot;/glossary/automation/&quot;&gt;automation&lt;/a&gt;, and a whole ecosystem of community-built extensions on ClawHub. There’s also an active Discord if you get stuck.&lt;/p&gt;
&lt;h2&gt;First Impressions&lt;/h2&gt;
&lt;p&gt;OpenClaw is the kind of project that makes you rethink what “using AI” means. It’s not just a chatbot - it’s a framework for building something closer to an actual assistant. The onboarding experience is surprisingly charming, the Telegram integration works flawlessly, and the transparent markdown-based memory system means you’re never left wondering what the agent knows or why it’s behaving a certain way.&lt;/p&gt;
&lt;p&gt;That said, the security warnings are there for a reason. This is beta software that can execute commands on your system. Treat it accordingly - run it on isolated hardware, keep your secrets elsewhere, and don’t give it access to anything you wouldn’t hand to a keen but occasionally confused intern.&lt;/p&gt;
&lt;p&gt;I’m looking forward to seeing where this goes.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2026-01-31-openclaw-giving-claude-a-body-and-a-telegram-bot.PeZ1LkH-.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2026/01/31/openclaw-giving-claude-a-body-and-a-telegram-bot-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>ai</category><category>linux</category><category>automation</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Day to Day Tools, the 2025 edition</title><link>https://www.russ.cloud/2025/12/29/day-to-day-tools-the-2025-edition/</link><guid isPermaLink="true">https://www.russ.cloud//2025/12/29/day-to-day-tools-the-2025-edition/</guid><description>The 2025 edition of my day-to-day tools: Ghostty, Claude Code, Google&apos;s Antigravity IDE, and the AI services that have become central to how I work.</description><pubDate>Mon, 29 Dec 2025 11:07:03 GMT</pubDate><content:encoded>&lt;p&gt;As 2025 draws to a close, I find myself reflecting on the tools I use day-to-day. Comparing this to my &lt;a href=&quot;/2024/06/16/2024-tools-update/&quot;&gt;2024 edition&lt;/a&gt;, it’s remarkable how much the landscape has shifted, particularly around AI tooling. What was experimental 18 months ago has now become central to how I work. 🤔&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;As you may already know, I am a &lt;a href=&quot;/tags/macos/&quot;&gt;macOS&lt;/a&gt; user, so all of the tools covered in this post primarily run on Macs. 🍎&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;Desktop Apps 💻&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://antigravity.google/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Antigravity&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: At the start of 2025, I was using Cursor as a replacement for VSCode (and still do at the day job), but as the year progressed, Cursor’s pricing and limit changes started to get in the way. Luckily, towards the end of the year, Google released Antigravity - a free AI-powered IDE built on a VSCode fork. It supports multiple AI models including Gemini 3, Claude, and others, with generous rate limits on the Gemini models. Combined with the &lt;a href=&quot;/glossary/claude-code/&quot;&gt;Claude Code&lt;/a&gt; plugin, it’s become my IDE of choice - essentially what Cursor should have been. 🚀&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://pasteapp.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Paste&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Still going strong after years of use. Having my clipboard history synced across all my devices remains invaluable - I genuinely don’t know how I’d function without &lt;strong&gt;[CMD]&lt;/strong&gt;+&lt;strong&gt;[Shift]&lt;/strong&gt;+&lt;strong&gt;V&lt;/strong&gt; at this point. 📋&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://ghostty.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ghostty&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: This is the big change for 2025. After years with iTerm2, I’ve switched to Ghostty as my terminal emulator of choice. Built by one of the original HashiCorp founders, it’s fast, native, and just feels &lt;em&gt;right&lt;/em&gt;. The configuration is straightforward, and the performance is noticeably snappier. 👻&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.sublimetext.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sublime Text&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: For raw text editing, Sublime Text remains unbeatable. Quick to launch, no bloat, and still the best text editor around after all these years. 📝&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://reederapp.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Reeder&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: The new Reeder has become my go-to for keeping up with RSS feeds, podcasts, videos, and social media posts - all in a unified timeline. It syncs your position across devices via iCloud, so you can pick up exactly where you left off. Clean, focused, and does one thing exceptionally well. 📖&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Terminal Apps ⌨️&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://claude.ai/code&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Claude Code&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: This has genuinely changed how I work. Anthropic’s &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; tool for agentic coding lets me delegate coding tasks directly from my terminal, with Claude understanding context from my codebase. I split my time between Claude Code in the terminal and Antigravity with its Claude Code plugin - between them, they’ve replaced VSCode entirely. 🤖&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebrew&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Some things never change. Homebrew remains the only real package manager for macOS, and it’s still the first thing I install on any new machine. 🍺&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://starship.rs/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Starship&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: A minimal, blazing-fast, and highly customisable prompt for any shell. I covered my setup in detail &lt;a href=&quot;/2025/11/15/my-starship-prompt-setup/&quot;&gt;here&lt;/a&gt;. It works beautifully with Ghostty. ✨&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://ohmyz.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Oh My Zsh&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: I’ve been using Oh My &lt;a href=&quot;/glossary/zsh/&quot;&gt;Zsh&lt;/a&gt; for quite a while now. It’s a framework for managing Zsh configuration that comes with helpful plugins, themes, and sensible defaults. It makes Zsh genuinely pleasant to use. 🐚&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/ajeetdsouza/zoxide&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;zoxide&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: A smarter &lt;code&gt;cd&lt;/code&gt; command that remembers which directories you use most frequently, letting you jump to them in just a few keystrokes. Once you’ve used it, going back to regular &lt;code&gt;cd&lt;/code&gt; feels painful. 📂&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://docs.conda.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Conda&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Still my go-to for &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; environment management. I wrote about my setup &lt;a href=&quot;/2024/04/06/conda-for-python-environment-management-on-macos/&quot;&gt;back in 2024&lt;/a&gt; and it’s remained largely unchanged. 🐍&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://pnpm.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PNPM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: For Node.js package management, I’ve fully switched to PNPM. It’s faster than npm, more efficient with disk space thanks to its content-addressable storage, and just works. This blog is built with it, for details on why I switched from NPM &lt;a href=&quot;/2025/11/29/when-the-sandworm-came-for-my-secrets-lessons-from-shai-hulud-20/&quot;&gt;see this post&lt;/a&gt; 😱 📦&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;AI Services 🤖&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://claude.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Claude&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: If it wasn’t obvious from Claude Code making the list, Anthropic’s Claude has become my primary AI assistant. The reasoning capabilities, the longer context windows, and the ability to work through complex problems make it my first port of call. 🧠&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://perplexity.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Perplexity&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: For research-style queries where I want sources and citations, Perplexity fills a nice gap. It’s particularly useful when I need to verify information or get a quick overview of a topic with references. 📚&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://gemini.google/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google Gemini&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Between the desktop app and the web interface, Gemini has earned its place in my toolkit, especially for multimodal tasks and when I need tight integration with Google’s ecosystem. ♊&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.openai.com/chatgpt&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ChatGPT&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Still useful, particularly for image generation and for specific tasks where GPT-5 shines. It’s dropped from being my primary AI tool, but it hasn’t left the toolkit entirely. 💬&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://fal.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Fal&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: A great collection of &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; driven image generation tools, I use it for generating images for the blog, see &lt;a href=&quot;/2025/11/23/playing-with-nano-banana-pro-ai-powered-album-cover-collages/&quot;&gt;this post&lt;/a&gt; for details. 📸&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Services 🌐&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://kagi.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Kagi&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: My primary search engine. No ads, no tracking, and genuinely better results. It’s become indispensable for actual research rather than wading through SEO-optimised garbage. 🔍&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://proton.me&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Proton&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Privacy-focused email, calendar, and VPN. I’ve been gradually moving more of my digital life to Proton’s ecosystem. In a world where everything wants to harvest your data, having services that actively protect your privacy feels increasingly important. 🔒&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Where all &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;my code&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; lives. From this &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/blog/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;blog&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to various side projects and infrastructure-as-code repos, &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; remains central to how I work and share code. 🐙&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://pages.cloudflare.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cloudflare Pages&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: This &lt;a href=&quot;/&quot;&gt;blog&lt;/a&gt; and &lt;span&gt;&lt;a href=&quot;https://www.russ.fm/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;my record collection page&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; both run on Cloudflare Pages. Fast deployments, great CDN, and the price is right (free for my usage). 🌩️&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The biggest theme of 2025? AI tooling has moved from “interesting experiment” to “core workflow.” Between Claude Code, multiple AI assistants, and AI-enhanced search, these tools have fundamentally changed how I approach work and various day-to-day tasks.&lt;/p&gt;
&lt;p&gt;Oh, and I got a &lt;span&gt;&lt;a href=&quot;https://bambulab.com/en/p2s&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;3D printer&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; - which has already sparked a minor &lt;span&gt;&lt;a href=&quot;https://gridfinity.xyz/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gridfinity&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; obsession.&lt;/p&gt;
&lt;p&gt;I wonder what next year’s list will look like - at this pace of change, it could be dramatically different again. 🚀&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-12-29-day-to-day-tools-the-2025-edition.Cmv-BWJv.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/12/29/day-to-day-tools-the-2025-edition-og.png</blog:ogImage><blog:readingTime>5</blog:readingTime><category>tools</category><category>macos</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Save money by pausing Microsoft Fabric Capacities with Azure Logic Apps</title><link>https://www.russ.cloud/2025/12/21/save-money-by-pausing-microsoft-fabric-capacities-with-azure-logic-apps/</link><guid isPermaLink="true">https://www.russ.cloud//2025/12/21/save-money-by-pausing-microsoft-fabric-capacities-with-azure-logic-apps/</guid><description>Learn how to automatically pause Microsoft Fabric capacities outside of working hours using Azure Logic Apps. This step-by-step guide shows you how to deploy a cost-saving automation that can significantly reduce your Fabric spend.</description><pubDate>Sun, 21 Dec 2025 11:37:26 GMT</pubDate><content:encoded>&lt;p&gt;Back in April 2024, I wrote a post about &lt;a href=&quot;/2024/04/01/saving-money-with-azure-logic-apps/&quot;&gt;Saving money with Azure Logic Apps&lt;/a&gt; where I covered automating the stop/start of &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; Virtual Machines and Application Gateways. Since then, I’ve had a few requests to add support for Microsoft Fabric capacities to the repo, so here we are.&lt;/p&gt;
&lt;p&gt;If you’re not familiar with Microsoft Fabric, it’s Microsoft’s unified analytics platform that brings together data engineering, data science, real-time analytics, and business intelligence. It’s a powerful platform, but like many Azure services, it can get expensive if you’re not careful about managing your capacity.&lt;/p&gt;
&lt;h2&gt;Why Pause Fabric Capacities?&lt;/h2&gt;
&lt;p&gt;Microsoft Fabric uses a capacity-based pricing model where you pay for Capacity Units (CUs) by the hour. The costs add up quickly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An F2 capacity runs about $0.36 per hour, or roughly $262 per month if left running 24/7&lt;/li&gt;
&lt;li&gt;An F64 capacity can set you back around $5,000+ per month on a reserved instance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The good news is that with pay-as-you-go (PAYG) capacities, you can pause them when they’re not in use. If your team only uses Fabric during working hours, pausing the capacity overnight and at weekends could save you around 65% of your costs. That’s a significant saving, especially for dev and test environments where you may not want to pay for a reserved capacity that’s running 24/7.&lt;/p&gt;
&lt;h2&gt;The Logic App&lt;/h2&gt;
&lt;p&gt;The Logic App I’ve added to the repo follows the same pattern as the existing Virtual Machine and Application Gateway workflows. It uses a user-managed identity to authenticate against the Azure REST &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; and targets resources based on tags.&lt;/p&gt;
&lt;p&gt;You can find the Logic App definition in the &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; repo:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/money-saving-azure-logic-apps&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/money-saving-azure-logic-apps: Discover how Azure Logic Apps can help you save money by automating tasks like stopping and starting Virtual Machines and Application Gateways. This post provides a step-by-step guide on deploying Logic Apps and walks through the workflows for cost-saving automations, ensuring your Azure resources only run when needed.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Discover how Azure Logic Apps can help you save money by automating tasks like stopping and starting Virtual Machines and Application Gateways. This post provides a step-by-step guide on deploying ...&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-9dfe01013e01.jpg&quot; alt=&quot;Discover how Azure Logic Apps can help you save money by automating tasks like stopping and starting Virtual Machines and Application Gateways. This post provides a step-by-step guide on deploying ...&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h3&gt;What It Does&lt;/h3&gt;
&lt;p&gt;The workflow is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Recurrence Trigger&lt;/strong&gt;: By default, it runs at 6pm GMT, Monday to Friday&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get Tagged Resources&lt;/strong&gt;: Queries the subscription for resources tagged with &lt;code&gt;fabricCapacityPause=included&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filter to Fabric Capacities&lt;/strong&gt;: Filters the results to only include &lt;code&gt;Microsoft.Fabric/capacities&lt;/code&gt; resources&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check Status&lt;/strong&gt;: For each capacity, it checks if the state is “Active”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suspend&lt;/strong&gt;: If active, it suspends the capacity&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Unlike the VM and Application Gateway Logic Apps which toggle between start and stop, this one is designed specifically to pause capacities at the end of the working day.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;In this case, we would typically trigger a resume manually when the capacity is required, this Logic App is there to help you save money when the person who starts the capacity forgets to pause when they have finished with it, which 100% will happen at some point.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;Deploying the Logic App&lt;/h2&gt;
&lt;p&gt;Let’s walk through deploying the Logic App. If you’ve followed the original post, this will feel very familiar.&lt;/p&gt;
&lt;h3&gt;Cloning the Repo&lt;/h3&gt;
&lt;p&gt;First, clone the repo containing the Logic App definitions:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Cloning the repo&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git clone https://github.com/russmckendrick/money-saving-azure-logic-apps.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd money-saving-azure-logic-apps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Setting Up the Environment&lt;/h3&gt;
&lt;p&gt;Set some environment variables for the deployment:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting environment variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-logicapps-fabric-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export REGION=&quot;uksouth&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export SUBSCRIPTION_ID=$(az account show --query id --output tsv)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export MANAGED_ID_NAME=&quot;mi-logicapps-fabric-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export LOGIC_APP_NAME=&quot;la-fabricCapacityPause-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Creating the Resources&lt;/h3&gt;
&lt;p&gt;Create the resource group:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating the resource group&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Create the user-managed identity:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating the managed identity&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az identity create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $MANAGED_ID_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Tagging Your Fabric Capacities&lt;/h3&gt;
&lt;p&gt;Before we grant permissions, we need to tag the Fabric capacities we want to manage. The Logic App targets resources based on tags, and we can also use those same tags to discover which resources need permissions.&lt;/p&gt;
&lt;p&gt;Add the following tag to any capacity you want to be automatically paused:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tag Name&lt;/strong&gt;: &lt;code&gt;fabricCapacityPause&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tag Value&lt;/strong&gt;: &lt;code&gt;included&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can do this through the Azure Portal or using the Azure &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Tagging a Fabric capacity&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az resource tag \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--tags fabricCapacityPause=included \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--ids &quot;/subscriptions/$SUBSCRIPTION_ID/resourceGroups/YOUR_FABRIC_RESOURCE_GROUP/providers/Microsoft.Fabric/capacities/YOUR_CAPACITY_NAME&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Granting Permissions&lt;/h3&gt;
&lt;p&gt;The managed identity needs Reader access to list resources across the subscription, and permissions to manage the Fabric capacities. According to &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/fabric/enterprise/pause-resume&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Microsoft’s documentation&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, the following permissions are required to pause and resume capacities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Fabric/capacities/read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Fabric/capacities/write&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Fabric/capacities/suspend/action&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Fabric/capacities/resume/action&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, there isn’t a dedicated Fabric-specific built-in role that provides just these permissions, so Contributor is the minimum required role. However, we can scope it to just the Fabric capacity resources rather than the entire subscription.&lt;/p&gt;
&lt;p&gt;First, grant Reader access to the subscription so the Logic App can query for tagged resources:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Granting Reader access to the subscription&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az role assignment create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-principal-type &quot;ServicePrincipal&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-object &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query principalId --output tsv)&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--role &quot;Reader&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--scope &quot;/subscriptions/$SUBSCRIPTION_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, we can loop through all the tagged Fabric capacities and grant Contributor access to each one:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Granting Contributor access to tagged Fabric capacities&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;for CAPACITY_ID in $(az resource list \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--tag fabricCapacityPause=included \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--query &quot;[?type==&apos;Microsoft.Fabric/capacities&apos;].id&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--output tsv); do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;Granting Contributor access to: $CAPACITY_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;az role assignment create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-principal-type &quot;ServicePrincipal&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-object &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query principalId --output tsv)&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--role &quot;Contributor&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--scope &quot;$CAPACITY_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This approach means you only grant permissions to the specific capacities you’ve tagged, following the principle of least privilege.&lt;/p&gt;
&lt;h3&gt;Deploying the Logic App&lt;/h3&gt;
&lt;p&gt;Now deploy the Logic App itself:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Deploying the Logic App&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--mi-user-assigned &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query id --output tsv)&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--state &quot;Disabled&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--definition &quot;fabricCapacityPause.json&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Updating the Parameters&lt;/h3&gt;
&lt;p&gt;Update the Logic App parameters with your managed identity and subscription IDs:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting the managedId parameter&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow update \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--set &quot;definition.parameters.managedId.defaultValue=$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query id --output tsv)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting the subscriptionId parameter&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow update \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--set &quot;definition.parameters.subscriptionId.defaultValue=$SUBSCRIPTION_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Enabling the Logic App&lt;/h2&gt;
&lt;p&gt;Once everything is configured, enable the Logic App:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Enabling the Logic App&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow update \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--state &quot;Enabled&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can also trigger a manual run from the Azure Portal to test that everything is working as expected before waiting for the scheduled run.&lt;/p&gt;
&lt;h2&gt;Customising the Schedule&lt;/h2&gt;
&lt;p&gt;The default schedule pauses capacities at 6pm GMT, Monday to Friday. If you need a different schedule, you can update the recurrence trigger in the Logic App designer or by modifying the &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; directly.&lt;/p&gt;
&lt;p&gt;For example, if you wanted to pause at 7pm instead, you would update the &lt;code&gt;hours&lt;/code&gt; array in the trigger:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Modifying the schedule&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;recurrence&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;frequency&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Week&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;interval&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;schedule&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hours&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;19&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;minutes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;weekDays&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;Monday&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;Tuesday&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;Wednesday&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;Thursday&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;Friday&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;timeZone&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GMT Standard Time&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Resuming Capacities&lt;/h2&gt;
&lt;p&gt;This Logic App only pauses capacities; it doesn’t resume them. For resuming, you have a few options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Manual Resume&lt;/strong&gt;: Start the capacity manually from the Azure Portal when you need it&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate Logic App&lt;/strong&gt;: Create a similar Logic App that runs in the morning and calls the resume endpoint instead of suspend&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On-Demand&lt;/strong&gt;: Use Azure &lt;a href=&quot;/glossary/automation/&quot;&gt;Automation&lt;/a&gt; or a simple script to resume capacities as needed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The resume endpoint follows the same pattern as the suspend endpoint, just swap &lt;code&gt;/suspend&lt;/code&gt; for &lt;code&gt;/resume&lt;/code&gt; in the API call.&lt;/p&gt;
&lt;h2&gt;Cleanup&lt;/h2&gt;
&lt;p&gt;To remove the Logic App and supporting resources:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Removing the resources&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-logicapps-fabric-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group delete --name $RESOURCE_GROUP_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Don’t forget to clean up any lingering RBAC assignments in the Azure Portal.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Adding Fabric capacity management to the money-saving Logic Apps collection was a natural extension. The pricing model makes it an obvious candidate for automation, especially for development and test environments where 24/7 availability isn’t required.&lt;/p&gt;
&lt;p&gt;If you’re running Fabric capacities and not already pausing them outside of working hours, you’re potentially leaving significant savings on the table. A few minutes of setup could save you hundreds (or thousands) of pounds each month.&lt;/p&gt;
&lt;p&gt;As always, the full Logic App definition is available in the GitHub repo, and I’ll continue adding new resource types as I encounter them in the wild.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-12-21-pausing-microsoft-fabric-capacities-with-azure-logic-apps.Dtrat8nO.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/12/21/save-money-by-pausing-microsoft-fabric-capacities-with-azure-logic-apps-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>azure</category><category>cloud</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Azure Soft Delete: Recovering Resources and Preventing Accidental Deletions</title><link>https://www.russ.cloud/2025/12/05/azure-soft-delete-recovering-resources-and-preventing-accidental-deletions/</link><guid isPermaLink="true">https://www.russ.cloud//2025/12/05/azure-soft-delete-recovering-resources-and-preventing-accidental-deletions/</guid><description>A practical guide to recovering accidentally deleted Azure resources that support soft delete, covering API Management, Key Vault, Storage Accounts, and more. Plus, how to use delete locks to prevent these situations in the first place.</description><pubDate>Fri, 05 Dec 2025 09:37:37 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this week, I had one of those moments that makes your stomach drop – an &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; Management instance had been deleted. These things happen; someone clicks the wrong button, a &lt;a href=&quot;/glossary/terraform-state/&quot;&gt;Terraform state&lt;/a&gt; gets out of sync, or an automated cleanup script gets a bit too enthusiastic. Whatever the cause, the result is the same: a critical resource is gone, and you need it back.&lt;/p&gt;
&lt;p&gt;Fortunately, some Azure services, including API Management, support soft delete. Many Azure services retain deleted resources for a period before permanently removing them, giving you a window to recover from these situations. This post covers my experience recovering the APIM instance and provides a broader look at which Azure services support soft delete and how to use it.&lt;/p&gt;
&lt;h2&gt;Recovering a Deleted Azure API Management Instance&lt;/h2&gt;
&lt;p&gt;When an Azure API Management instance is deleted (via the Azure Portal or REST API version 2020-06-01-preview or later), it enters a soft-deleted state for 48 hours before being permanently purged. This gives you a window to recover it.&lt;/p&gt;
&lt;h3&gt;Finding the Deleted Service&lt;/h3&gt;
&lt;p&gt;First, let’s confirm the service is in a soft-deleted state and get the details we need. Using the Azure &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Check the soft-deleted APIM service&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az apim deletedservice show \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location uksouth \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--service-name apim-that-was-deleted-uks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This returns something like:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Soft-deleted service details (JSON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;deletionDate&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2025-12-01T15:04:29.570042+00:00&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/providers/Microsoft.ApiManagement/locations/uksouth/deletedservices/apim-that-was-deleted-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;UK South&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;apim-that-was-deleted-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scheduledPurgeDate&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2025-12-03T15:02:32.189403+00:00&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;serviceId&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-apim-production-uks/providers/Microsoft.ApiManagement/service/apim-that-was-deleted-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.ApiManagement/deletedservices&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The key things to note here are the &lt;code&gt;scheduledPurgeDate&lt;/code&gt; (you have until then to recover) and the &lt;code&gt;serviceId&lt;/code&gt; which tells us the original resource group. In my case, the resource group had also been deleted, so I needed to recreate that first.&lt;/p&gt;
&lt;h3&gt;Restoring the Service&lt;/h3&gt;
&lt;p&gt;To restore the APIM instance, you need to use the Azure REST API directly – there’s no native Azure CLI command for this yet. Set up your variables:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Set restoration variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;apimName=&quot;apim-that-was-deleted-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;subscriptionId=$(az account show --query id --output tsv)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;location=&quot;uksouth&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;resourceGroupName=&quot;rg-apim-production-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If your resource group was also deleted, recreate it first:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Recreate the resource group (if needed)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $resourceGroupName \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $location&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now restore the APIM service using the REST API:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Restore the APIM service&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az rest \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--method PUT \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--header &quot;Accept=application/json&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--uri &quot;https://management.azure.com/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.ApiManagement/service/${apimName}?api-version=2021-08-01&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--body &quot;{\&quot;location\&quot;:\&quot;${location}\&quot;,\&quot;properties\&quot;: {\&quot;restore\&quot; : true} }&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The restoration process will take several minutes. You’ll see the &lt;code&gt;provisioningState&lt;/code&gt; change from &lt;code&gt;Deleted&lt;/code&gt; to &lt;code&gt;Activating&lt;/code&gt; and eventually to &lt;code&gt;Succeeded&lt;/code&gt;. Once complete, your APIM instance is back, complete with all its APIs, policies, and configurations.&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/api-management/soft-delete&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Azure API Management Soft-Delete&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Soft-delete allows you to recover a recently deleted API Management instance.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;learn.microsoft.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/learn.microsoft.com-efb5d4cb177b.jpg&quot; alt=&quot;Microsoft Learn&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;Other Azure Services with Soft Delete&lt;/h2&gt;
&lt;p&gt;My APIM adventure got me thinking about what other Azure services support soft delete. Turns out, quite a few do – and it’s worth knowing about them before you need them.&lt;/p&gt;
&lt;h3&gt;Azure Key Vault&lt;/h3&gt;
&lt;p&gt;Key Vault is probably the most well-known example. Soft delete is enabled by default and cannot be disabled. When you delete a secret, key, or certificate, it’s retained for a configurable period (minimum 7 days, up to 90 days) before permanent deletion.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;List soft-deleted secrets&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az keyvault secret list-deleted --vault-name my-keyvault&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Recover a deleted secret&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az keyvault secret recover --vault-name my-keyvault --name my-secret&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can also enable purge protection to prevent even administrators from permanently deleting items before the retention period expires – useful in production environments where you really can’t afford to lose things.&lt;/p&gt;
&lt;h3&gt;Azure Storage&lt;/h3&gt;
&lt;p&gt;Azure Blob Storage supports soft delete for both blobs and containers, with configurable retention periods from 1 to 365 days.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Enable blob soft delete&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az storage account blob-service-properties update \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--account-name mystorageaccount \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--enable-delete-retention true \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--delete-retention-days 30&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Azure File Shares also support soft delete, allowing you to recover entire file shares and their contents:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;List deleted file shares&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az storage share-rm list \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--storage-account mystorageaccount \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--include-deleted&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Azure SQL Database&lt;/h3&gt;
&lt;p&gt;Deleted Azure SQL databases can be restored within the backup retention period (up to 35 days depending on your tier). Deleted SQL servers themselves can also be recovered if the backup is still available.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;List deleted databases&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az sql db list-deleted \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group myresourcegroup \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--server myserver&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Azure Backup&lt;/h3&gt;
&lt;p&gt;Azure Backup enforces soft delete by default as part of its “secure by default” posture. When you stop backup and delete data, the recovery points are retained for 14 days (configurable up to 180 days). This applies to VM backups, SQL Server in Azure VM backups, and SAP HANA backups.&lt;/p&gt;
&lt;h3&gt;Azure Container Registry&lt;/h3&gt;
&lt;p&gt;Currently in preview, ACR supports soft delete for accidentally deleted &lt;a href=&quot;/glossary/container/&quot;&gt;container&lt;/a&gt; images and tags with retention periods from 1 to 90 days.&lt;/p&gt;
&lt;h3&gt;Azure Log Analytics Workspace&lt;/h3&gt;
&lt;p&gt;Deleted workspaces are retained in a soft-deleted state for 14 days, during which they can be recovered with all their data intact.&lt;/p&gt;
&lt;h3&gt;Microsoft Entra ID (Azure AD)&lt;/h3&gt;
&lt;p&gt;The Recycle Bin feature retains deleted users, groups, and other directory objects for 30 days before permanent deletion.&lt;/p&gt;
&lt;h3&gt;Quick Reference Table&lt;/h3&gt;

































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Service&lt;/th&gt;&lt;th&gt;Default Retention&lt;/th&gt;&lt;th&gt;Configurable&lt;/th&gt;&lt;th&gt;Recovery Method&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;API Management&lt;/td&gt;&lt;td&gt;48 hours&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;REST API&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Key Vault&lt;/td&gt;&lt;td&gt;7-90 days&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;CLI/Portal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Blob Storage&lt;/td&gt;&lt;td&gt;1-365 days&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;CLI/Portal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;File Shares&lt;/td&gt;&lt;td&gt;1-365 days&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;CLI/Portal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SQL Database&lt;/td&gt;&lt;td&gt;Up to 35 days&lt;/td&gt;&lt;td&gt;Service tier&lt;/td&gt;&lt;td&gt;CLI/Portal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Azure Backup&lt;/td&gt;&lt;td&gt;14-180 days&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Portal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;/glossary/container-registry/&quot;&gt;Container Registry&lt;/a&gt;&lt;/td&gt;&lt;td&gt;1-90 days&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;CLI/Portal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Log Analytics&lt;/td&gt;&lt;td&gt;14 days&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;CLI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Entra ID&lt;/td&gt;&lt;td&gt;30 days&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Portal&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Prevention is Better Than Cure: Delete Locks&lt;/h2&gt;
&lt;p&gt;While soft delete is a great safety net, it’s better to prevent accidental deletions in the first place. Azure Resource Locks allow you to protect resources from accidental modification or deletion.&lt;/p&gt;
&lt;p&gt;There are two types of locks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CanNotDelete&lt;/strong&gt;: Resources can be read and modified, but not deleted&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ReadOnly&lt;/strong&gt;: Resources can only be read, not modified or deleted&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Applying a Delete Lock&lt;/h3&gt;
&lt;p&gt;To protect a critical resource from deletion:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Apply a delete lock to a resource&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az lock create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name &quot;PreventDeletion&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--lock-type CanNotDelete \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group rg-production-uks \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-name apim-production-uks \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-type Microsoft.ApiManagement/service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Or protect an entire resource group:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Apply a delete lock to a resource group&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az lock create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name &quot;PreventDeletion&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--lock-type CanNotDelete \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group rg-production-uks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Important&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Locks are inherited by child resources, so a lock on a resource group applies to all resources within it. Also, remember that locks don’t prevent all operations – for example, a &lt;code&gt;CanNotDelete&lt;/code&gt; lock on a storage account won’t prevent someone from deleting blobs within it.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h3&gt;Managing Locks with Infrastructure as Code&lt;/h3&gt;
&lt;p&gt;If you’re using &lt;a href=&quot;/glossary/terraform/&quot;&gt;Terraform&lt;/a&gt;, you can apply locks as part of your deployment:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Terraform resource lock (HCL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;resource&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;azurerm_management_lock&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;apim_lock&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;PreventDeletion&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;azurerm_api_management&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lock_level&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;CanNotDelete&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;notes&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Protected production resource&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;For ARM templates or &lt;a href=&quot;/glossary/bicep/&quot;&gt;Bicep&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Bicep resource lock&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;resource&lt;/span&gt;&lt;span&gt; apimLock &lt;/span&gt;&lt;span&gt;&apos;Microsoft.Authorization/locks@2020-05-01&apos;&lt;/span&gt;&lt;span&gt; = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&apos;PreventDeletion&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope: apimService&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;level: &lt;/span&gt;&lt;span&gt;&apos;CanNotDelete&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;notes: &lt;/span&gt;&lt;span&gt;&apos;Protected production resource&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Lock your Azure resources to protect your infrastructure - Azure Resource Manager&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Learn how to lock all users and roles to protect your Azure resources from updates or deletions.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;learn.microsoft.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/learn.microsoft.com-91cd827997c8.jpg&quot; alt=&quot;Microsoft Learn&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Getting that sinking feeling when a critical resource disappears is never pleasant, but knowing that Azure has soft delete capabilities across many services can turn a potential disaster into a minor inconvenience. The key takeaways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Know your retention periods&lt;/strong&gt;: Different services have different soft delete windows. API Management gives you 48 hours, while Key Vault can give you up to 90 days.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test recovery procedures before you need them&lt;/strong&gt;: It’s much less stressful to learn the recovery process in a dev environment than during an incident.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use delete locks on production resources&lt;/strong&gt;: A few minutes setting up locks can save hours of recovery work and stress.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document your critical resources&lt;/strong&gt;: Keep a list of resources that would cause significant impact if deleted, along with their soft delete capabilities and recovery procedures.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The next time someone accidentally deletes something important, you’ll know exactly where to look and what to do. And if you put those delete locks in place, hopefully you won’t need to deal with it at all.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-12-05-azure-soft-delete-recovering-resources-and-preventing-accidental-deletions.ferwEFx9.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/12/05/azure-soft-delete-recovering-resources-and-preventing-accidental-deletions-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>azure</category><category>cloud</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>When the Sandworm Came for My Secrets: Lessons from Shai-Hulud 2.0</title><link>https://www.russ.cloud/2025/11/29/when-the-sandworm-came-for-my-secrets-lessons-from-shai-hulud-20/</link><guid isPermaLink="true">https://www.russ.cloud//2025/11/29/when-the-sandworm-came-for-my-secrets-lessons-from-shai-hulud-20/</guid><description>How a sophisticated npm supply chain attack via a trojanized kill-port package compromised my development environment, exposing API keys to attacker-controlled repositories - and what I&apos;m doing differently now.</description><pubDate>Sat, 29 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this week, I discovered that this very repository had fallen victim to &lt;strong&gt;Shai-Hulud 2.0&lt;/strong&gt; - a sophisticated &lt;a href=&quot;/glossary/supply-chain-attack/&quot;&gt;supply chain attack&lt;/a&gt; that compromised npm packages and exfiltrated secrets from thousands of developers and &lt;a href=&quot;/glossary/ci-cd/&quot;&gt;CI/CD&lt;/a&gt; pipelines. My &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; keys for services like FAL.ai, OpenAI, Perplexity, and others were published to attacker-controlled repositories before I even knew what had happened.&lt;/p&gt;
&lt;p&gt;This post covers what the attack is, how it works, exactly how I got caught, and the changes I’ve made to prevent this from happening again.&lt;/p&gt;
&lt;h2&gt;How I Found Out&lt;/h2&gt;
&lt;p&gt;On November 27th, I received an email from &lt;span&gt;&lt;a href=&quot;https://github.com/r4monsys&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Omar Hammou&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, a &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; researcher who monitors &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; for exposed secrets:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have identified several leaked credentials associated with your accounts, including OpenAI and Perplexity tokens. I operate a GitHub monitoring system that continuously flags exposed secrets, and although some repositories were deleted, the leaked data is still present in GitHub’s historical snapshots.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The credentials had been exfiltrated to a repository at &lt;code&gt;github.com/dp5iveme/zf0jxl7wqn14g9enav&lt;/code&gt; (since deleted by GitHub). As Omar explained, even when repositories are deleted, forks and clones preserve the repository state as archived snapshots - meaning the leaked data can persist indefinitely. Omar was kind enough to share the full payload that was captured - a &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; dump of my entire shell environment, including every environment variable that was set at the time of the attack.&lt;/p&gt;
&lt;h2&gt;What is Shai-Hulud 2.0?&lt;/h2&gt;
&lt;p&gt;Named after the giant sandworms from Frank Herbert’s Dune, Shai-Hulud 2.0 is an ongoing malware campaign that targets npm package maintainers. Attackers compromise maintainer accounts and publish trojanized versions of legitimate packages.&lt;/p&gt;
&lt;div class=&quot;w-full aspect-video&quot;&gt;&lt;a href=&quot;https://youtube.com/watch?v=weLhik7ArCY&quot;&gt;&lt;span&gt;Play&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;According to &lt;span&gt;&lt;a href=&quot;https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Wiz’s analysis&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, the scale is staggering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;25,000+ repositories&lt;/strong&gt; affected&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;350+ unique users&lt;/strong&gt; compromised&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1,000 new repositories&lt;/strong&gt; being created every 30 minutes during the initial outbreak&lt;/li&gt;
&lt;li&gt;Packages from major companies including &lt;strong&gt;Zapier&lt;/strong&gt;, &lt;strong&gt;ENS Domains&lt;/strong&gt;, &lt;strong&gt;PostHog&lt;/strong&gt;, and &lt;strong&gt;Postman&lt;/strong&gt; were trojanized&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How the Attack Works&lt;/h2&gt;
&lt;p&gt;The malware is deceptively simple but devastatingly effective:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Preinstall Hook Execution&lt;/strong&gt;: The malicious code runs during npm’s &lt;code&gt;preinstall&lt;/code&gt; phase via scripts like &lt;code&gt;setup_bun.js&lt;/code&gt; and &lt;code&gt;bun_environment.js&lt;/code&gt;. This ensures execution &lt;em&gt;before&lt;/em&gt; any code review or static analysis can occur.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Credential Harvesting&lt;/strong&gt;: The malware systematically steals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Local configuration files (&lt;code&gt;.npmrc&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;, cloud &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; configs)&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Cloud metadata service tokens&lt;/li&gt;
&lt;li&gt;Secrets from &lt;a href=&quot;/glossary/aws/&quot;&gt;AWS&lt;/a&gt; Secrets Manager, Google Secret Manager, and &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; Key Vault&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Triple Base64 Encoding&lt;/strong&gt;: Stolen data is encoded three times before exfiltration, evading simple detection rules.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Exfiltration to GitHub&lt;/strong&gt;: Credentials are published to attacker-controlled repositories with the description “Sha1-Hulud: The Second Coming.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Persistent Backdoor&lt;/strong&gt;: The malware injects a &lt;code&gt;discussion.yaml&lt;/code&gt; GitHub Actions workflow that registers infected machines as self-hosted runners named “SHA1HULUD”, enabling future remote command execution.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Destructive Fallback&lt;/strong&gt;: If credential exfiltration fails, the malware attempts to &lt;strong&gt;delete the victim’s entire home directory&lt;/strong&gt; - converting espionage into sabotage.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The Smoking Gun: kill-port@2.0.3&lt;/h2&gt;
&lt;p&gt;Thanks to the exfiltrated payload Omar shared, I was able to identify exactly which package compromised my system. The evidence was right there in the environment variables:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Smoking Gun&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;npm_lifecycle_script&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;node setup_bun.js&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;npm_lifecycle_event&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;preinstall&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;npm_package_name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;kill-port&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;npm_package_version&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2.0.3&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;PWD&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/Users/russ.mckendrick/Code/blog/node_modules/kill-port&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The culprit was &lt;strong&gt;&lt;code&gt;kill-port&lt;/code&gt;&lt;/strong&gt; - a utility package for killing processes on specific ports. According to the &lt;span&gt;&lt;a href=&quot;https://advisories.gitlab.com/pkg/npm/kill-port/GMS-2025-498/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitLab Advisory (GMS-2025-498)&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, versions &lt;strong&gt;2.0.2 and 2.0.3&lt;/strong&gt; were compromised on November 24th, 2025. The vulnerability is rated &lt;strong&gt;HIGH severity (CVSS 8.6)&lt;/strong&gt; and classified as CWE-506 (Embedded Malicious Code).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;npm_lifecycle_script&lt;/code&gt; value of &lt;code&gt;node setup_bun.js&lt;/code&gt; is the exact malicious script identified in the Wiz security report - it’s designed to look innocuous (who wouldn’t trust something called “setup_bun.js” in 2025?), but its sole purpose is credential theft.&lt;/p&gt;
&lt;p&gt;The package maintainer &lt;span&gt;&lt;a href=&quot;https://github.com/tiaanduplessis/kill-port/issues/84&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;confirmed on GitHub&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; that npm support has since removed the infected versions (2.0.2 and 2.0.3) from the registry. Version 2.0.1 is safe and does not contain the malicious files.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Check Your Installation&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;If you have kill-port installed, verify your version doesn’t contain &lt;code&gt;setup_bun.js&lt;/code&gt; or &lt;code&gt;bun_environment.js&lt;/code&gt; files. These are the telltale signs of infection. Pin to version &lt;strong&gt;2.0.1 or earlier&lt;/strong&gt; to be safe.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;What Was Stolen&lt;/h2&gt;
&lt;p&gt;The payload captured my entire environment, including:&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Service&lt;/th&gt;&lt;th&gt;Credential Type&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;OpenAI&lt;/td&gt;&lt;td&gt;API Key&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Perplexity&lt;/td&gt;&lt;td&gt;API Key&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;FAL.ai&lt;/td&gt;&lt;td&gt;API Key&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Tavily&lt;/td&gt;&lt;td&gt;API Key&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Exa&lt;/td&gt;&lt;td&gt;API Key&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Last.fm&lt;/td&gt;&lt;td&gt;API Key&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Plus my username, home directory path, shell configuration paths, and various other environment details that could be useful for further attacks.&lt;/p&gt;
&lt;p&gt;Fortunately, I don’t store AWS, GCP, or Azure credentials in environment variables (they use CLI-based authentication), but many developers aren’t so lucky.&lt;/p&gt;
&lt;h2&gt;The Global Damage&lt;/h2&gt;
&lt;p&gt;According to Wiz’s preliminary analysis, attackers obtained:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Credential Type&lt;/th&gt;&lt;th&gt;Count&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;GitHub Access Tokens&lt;/td&gt;&lt;td&gt;775&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS Credentials&lt;/td&gt;&lt;td&gt;373&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GCP Credentials&lt;/td&gt;&lt;td&gt;300&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Azure Credentials&lt;/td&gt;&lt;td&gt;115&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;These numbers only represent what was discovered in the attacker’s public repositories before GitHub took action.&lt;/p&gt;
&lt;h2&gt;What I’ve Changed&lt;/h2&gt;
&lt;p&gt;After rotating all my compromised credentials (a fun few hours, I must say), I’ve implemented several changes to reduce the attack surface:&lt;/p&gt;
&lt;h3&gt;Migrated from npm to pnpm&lt;/h3&gt;
&lt;p&gt;Beyond being faster and more disk-efficient, pnpm has better security defaults. I’ve updated all my GitHub Actions workflows to use pnpm with the &lt;code&gt;--frozen-lockfile&lt;/code&gt; flag:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Github Action&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;🌸 Setup PNPM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pnpm/action-setup@v6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;📦 Install dependencies&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pnpm install --frozen-lockfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--frozen-lockfile&lt;/code&gt; flag ensures CI/CD installs exactly what’s in the lockfile - no surprises from updated packages.&lt;/p&gt;
&lt;p&gt;I’ve also added aliases to my &lt;code&gt;.zshrc&lt;/code&gt; to prevent accidental npm usage locally:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 📦 pnpm aliases in .zshrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; npm&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;echo &quot;Error: Use pnpm&quot;; false&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; n&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;pnpm&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; ni&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;pnpm install&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This ensures I can’t accidentally run &lt;code&gt;npm install&lt;/code&gt; out of muscle memory.&lt;/p&gt;
&lt;h3&gt;Explicitly Approve Build Scripts&lt;/h3&gt;
&lt;p&gt;pnpm 10+ has a security feature that blocks lifecycle scripts by default, showing warnings like:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Ignore build scripts !!!&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Ignored build scripts: esbuild, sharp, workerd.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Run &quot;pnpm approve-builds&quot; to pick which dependencies should be allowed to run scripts.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Rather than blanket-allowing all scripts, you can explicitly whitelist only the packages you trust. I’ve added this to my &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;onlyBuiltDependencies&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;esbuild&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;sharp&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;workerd&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This means:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Package&lt;/th&gt;&lt;th&gt;Script&lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;sharp&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;install&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Downloads native image processing binaries&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;esbuild&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;postinstall&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Downloads platform-specific bundler binary&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;workerd&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;postinstall&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Downloads Cloudflare Workers runtime&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Any &lt;em&gt;other&lt;/em&gt; package trying to run lifecycle scripts will be blocked. This is a much better approach than &lt;code&gt;--ignore-scripts&lt;/code&gt; (which breaks legitimate packages) or allowing everything (which is what npm does by default).&lt;/p&gt;
&lt;h3&gt;Regular Security Audits&lt;/h3&gt;
&lt;p&gt;I’ve added &lt;code&gt;pnpm audit&lt;/code&gt; to all my GitHub Actions workflows:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;pnpm audit&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;🔒 Security audit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pnpm audit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This checks dependencies against the npm security advisory database and will fail the build if any known vulnerabilities are found. You can also run it locally:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;audit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Pinned Dependencies&lt;/h3&gt;
&lt;p&gt;Rather than allowing automatic updates, I’m now pinning dependencies to specific versions and reviewing changes manually:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dependencies&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;astro&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;5.1.1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Auditing for Compromise&lt;/h3&gt;
&lt;p&gt;If you’re concerned your environment may have been affected, check for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Suspicious workflow files&lt;/strong&gt;: Look in &lt;code&gt;.github/workflows/&lt;/code&gt; for &lt;code&gt;shai-hulud-workflow.yml&lt;/code&gt;, &lt;code&gt;discussion.yaml&lt;/code&gt;, or unexpected branches&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Self-hosted runner registrations&lt;/strong&gt;: Check for runners named “SHA1HULUD” in your GitHub repository settings&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unexpected npm packages&lt;/strong&gt;: Review your &lt;code&gt;package-lock.json&lt;/code&gt; or &lt;code&gt;pnpm-lock.yaml&lt;/code&gt; for packages added between November 21-25, 2025&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Credential Rotation&lt;/h3&gt;
&lt;p&gt;If you installed any npm packages during the attack window (November 21-25, 2025), you should:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clear your npm/pnpm cache: &lt;code&gt;pnpm store prune&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Delete &lt;code&gt;node_modules&lt;/code&gt; and reinstall&lt;/li&gt;
&lt;li&gt;Rotate &lt;strong&gt;all&lt;/strong&gt; credentials that were accessible to your development environment&lt;/li&gt;
&lt;li&gt;Review GitHub for any unauthorized workflow changes&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Lessons Learned&lt;/h2&gt;
&lt;p&gt;This attack is a stark reminder of several realities in modern development:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Supply chain attacks are the new normal&lt;/strong&gt;: We implicitly trust thousands of packages and their maintainers every time we run &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CI/CD environments are high-value targets&lt;/strong&gt;: They often have access to production credentials, cloud resources, and deployment capabilities.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lifecycle scripts are a security risk&lt;/strong&gt;: The ability for packages to execute arbitrary code during installation is powerful but dangerous.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Defense in depth matters&lt;/strong&gt;: No single measure would have prevented this, but layers of security (restricted CI/CD permissions, pinned dependencies, disabled scripts) reduce the blast radius.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The security community is watching&lt;/strong&gt;: I’m grateful to researchers like Omar who actively monitor for exposed secrets and alert affected developers. Without his email, I might not have known for weeks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;span&gt;&lt;a href=&quot;https://github.com/r4monsys&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Omar Hammou&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; for the heads-up and for sharing the exfiltrated payload so I could identify the attack vector. Security researchers who responsibly disclose findings like this are invaluable to the community.&lt;/p&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Wiz: Shai-Hulud 2.0 - Ongoing Supply Chain Attack&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://thehackernews.com/2025/11/second-sha1-hulud-wave-affects-25000.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Hacker News: Second Sha1-Hulud Wave Affects 25,000+ Repositories&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://advisories.gitlab.com/pkg/npm/kill-port/GMS-2025-498/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitLab Advisory GMS-2025-498: kill-port&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/tiaanduplessis/kill-port/issues/84&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub Issue: kill-port infected with malware&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stay vigilant out there !!!&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-11-29-shai-hulud-supply-chain-attack.BJJjjdzK.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/11/29/when-the-sandworm-came-for-my-secrets-lessons-from-shai-hulud-20-og.png</blog:ogImage><blog:readingTime>8</blog:readingTime><category>security</category><category>devops</category><category>github</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Playing with Nano Banana Pro: AI-Powered Album Cover Collages</title><link>https://www.russ.cloud/2025/11/23/playing-with-nano-banana-pro-ai-powered-album-cover-collages/</link><guid isPermaLink="true">https://www.russ.cloud//2025/11/23/playing-with-nano-banana-pro-ai-powered-album-cover-collages/</guid><description>Using Nano Banana Pro for generating stunning AI-powered album cover collages with intelligent image selection and content policy handling</description><pubDate>Sun, 23 Nov 2025 10:28:53 GMT</pubDate><content:encoded>&lt;p&gt;Every week, I generate a “&lt;a href=&quot;/tunes/&quot;&gt;Listened to This Week&lt;/a&gt;” blog post that showcases my top albums from Last.fm. The header of each post is a custom cover collage-a visually stunning blend of album artwork that serves as the hero image. Until recently, I was using a local Sharp-based “torn paper strip” generator. But I wanted something more dynamic, more artistic, and more… AI-powered.&lt;/p&gt;
&lt;p&gt;Enter &lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://blog.google/technology/ai/nano-banana-pro/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nano Banana Pro&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt;, Googles latest image editing model:&lt;/p&gt;
&lt;div class=&quot;w-full aspect-video&quot;&gt;&lt;a href=&quot;https://youtube.com/watch?v=UQsJIo46ZR8&quot;&gt;&lt;span&gt;Play&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;It is now available on the &lt;span&gt;&lt;a href=&quot;https://fal.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;FAL.ai&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; and after implementing this I now have a sophisticated AI-powered collage generator that intelligently selects the most vibrant album covers, analyzes them with GPT-4 Vision, and composites them into quirky music masterpieces.&lt;/p&gt;
&lt;h2&gt;The Challenge&lt;/h2&gt;
&lt;p&gt;I found that creating collages is using well known album covers isn’t as simple as throwing images together. There are several challenges:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Content Policy Violations&lt;/strong&gt;: Album covers can contain imagery that triggers AI model safety filters&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual Quality&lt;/strong&gt;: Not all album covers are equally vibrant or interesting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text Removal&lt;/strong&gt;: Album covers have text/typography that needs to be removed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composition&lt;/strong&gt;: The AI needs intelligent prompts to create cohesive artistic compositions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliability&lt;/strong&gt;: The system needs to handle failures gracefully and retry with alternative images&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The Solution Architecture&lt;/h2&gt;
&lt;p&gt;The collage generator that I prompted using &lt;span&gt;&lt;a href=&quot;https://www.claude.com/product/claude-code&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Claude Code&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; uses a multi-stage pipeline that combines computer vision analysis, &lt;span&gt;&lt;a href=&quot;https://platform.openai.com/docs/guides/images-vision?api-mode=responses&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GPT-4 Vision&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; prompting, and &lt;span&gt;&lt;a href=&quot;https://fal.ai/models/fal-ai/nano-banana-pro/edit/playground&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nano Banana Pro’s&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; image editing capabilities - the work flow looks something like this …&lt;/p&gt;
&lt;div class=&quot;mermaid-wrapper not-prose my-6 border overflow-hidden bg-white dark:bg-gray-900&quot;&gt;&lt;div class=&quot;controls-bar flex items-center justify-between px-4 py-2 surface-container-low border-b&quot;&gt;&lt;div class=&quot;flex items-center gap-3&quot;&gt;&lt;span&gt;Process flow diagram&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;flex items-center gap-2&quot;&gt;&lt;span&gt;&lt;span&gt;Ctrl+scroll to zoom&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fullscreen&lt;/span&gt;&lt;span&gt;SVG&lt;/span&gt;&lt;span&gt;PNG&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;error-display hidden px-4 py-3 bg-red-50 dark:bg-red-900/20 border-b border-red-200 dark:border-red-800&quot;&gt;&lt;p&gt;Error rendering diagram:&lt;/p&gt;&lt;pre class=&quot;text-xs text-red-700 dark:text-red-300 mt-1 overflow-auto max-h-40&quot;&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;preview-container relative overflow-hidden bg-white dark:bg-gray-900&quot;&gt;&lt;div class=&quot;svg-wrap flex items-center justify-center&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-container absolute hidden bg-white/90 dark:bg-gray-800/90 backdrop-blur-sm border border-gray-300 dark:border-gray-600 rounded shadow-lg overflow-hidden&quot;&gt;&lt;div class=&quot;minimap-canvas relative&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-viewport absolute border-2 border-blue-500 bg-blue-500/20 cursor-move&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-controls absolute top-1 right-1 flex flex-col gap-0.5&quot;&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;−&lt;/span&gt;&lt;span&gt;⟲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Image Selection Intelligence&lt;/h2&gt;
&lt;p&gt;The start of the process is the image selection algorithm. Instead of randomly picking albums, it analyzes each cover using three key metrics:&lt;/p&gt;
&lt;h3&gt;1. Color Saturation Analysis&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Color Analysis&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// Calculate average saturation (max - min of RGB)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(r&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; g&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; b)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(r&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; g&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; b)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;saturationSum &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; (max &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; min)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;avgSaturation&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; saturationSum &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; pixelCount&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;High saturation = vibrant, eye-catching colors that work well in composites.&lt;/p&gt;
&lt;h3&gt;2. Color Variance Detection&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Color Variance&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// Calculate RGB variance for visual complexity&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;totalVariance&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (rVariance &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; gVariance &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; bVariance) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;High variance = rich color diversity and visual interest.&lt;/p&gt;
&lt;h3&gt;3. Text Detection &amp;amp; Penalty&lt;/h3&gt;
&lt;p&gt;This is the clever bit. Album covers typically have text in the top and bottom regions (album title, artist name). The system:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Extracts the top 20% and bottom 20% of each image&lt;/li&gt;
&lt;li&gt;Applies edge detection using a convolution kernel&lt;/li&gt;
&lt;li&gt;Counts high-intensity pixels (edges indicate text)&lt;/li&gt;
&lt;li&gt;Penalizes covers with heavy text&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Edge detection kernel&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// Edge detection kernel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;kernel&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;// Text score: higher = more text = worse for collage&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;textScore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (totalEdgePixels &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; totalPixels) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Weighted Scoring Formula&lt;/h3&gt;
&lt;p&gt;The final score combines all three metrics:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Final Score&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;colorScore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (avgSaturation &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.4&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; (Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sqrt&lt;/span&gt;&lt;span&gt;(totalVariance) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;textPenalty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; textScore) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;finalScore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; colorScore &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; textPenalty&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Weights&lt;/strong&gt; (configurable in &lt;code&gt;fal-collage-config.json&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Saturation: &lt;strong&gt;40%&lt;/strong&gt; - Vibrant colors are most important&lt;/li&gt;
&lt;li&gt;Variance: &lt;strong&gt;30%&lt;/strong&gt; - Visual complexity matters&lt;/li&gt;
&lt;li&gt;Text Penalty: &lt;strong&gt;30%&lt;/strong&gt; - Avoid text-heavy covers&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The GPT-4 Vision Advantage&lt;/h2&gt;
&lt;p&gt;Once the best albums are selected, GPT-4 Vision analyzes the actual visual content and generates a custom compositing prompt. This is crucial because generic prompts produce generic results.&lt;/p&gt;
&lt;p&gt;The system prompt instructs GPT-4 to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Identify specific visual elements&lt;/strong&gt;: “the portrait from the yellow cover”, “the building from the blue cover”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Describe composition techniques&lt;/strong&gt;: double-exposure, color grading, blend modes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create unified scenes&lt;/strong&gt;: Not separate album covers, but one cohesive image&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Specify color treatment&lt;/strong&gt;: Cohesive palette across the entire composition&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Example Generated Prompt:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Create a cinematic music blog header featuring the vibrant abstract faces from the colorful cover, blending them into the architecture of the church-like building with musicians from the second image. Add the spilled flower pot from the third cover in the foreground, and place the painter from the fourth cover painting this scene. Overlay the dreamy curtains from the fifth cover to add a soft, ethereal layer, using double-exposure techniques. Integrate the collage of faces from the sixth cover subtly into the background, and place the penguins from the seventh cover on the grassy field. Finally, position the black and white group from the eighth cover in the middle, tying the scene together with a cohesive blend of warm and cool tones. Remove all text and typography.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This context-aware prompting produces far better results than static prompts.&lt;/p&gt;
&lt;h2&gt;Image Preprocessing Pipeline&lt;/h2&gt;
&lt;p&gt;Before sending images to Nano Banana Pro, they go through preprocessing to remove text regions:&lt;/p&gt;
&lt;div class=&quot;mermaid-wrapper not-prose my-6 border overflow-hidden bg-white dark:bg-gray-900&quot;&gt;&lt;div class=&quot;controls-bar flex items-center justify-between px-4 py-2 surface-container-low border-b&quot;&gt;&lt;div class=&quot;flex items-center gap-3&quot;&gt;&lt;span&gt;Image Preprocessing Pipeline&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;flex items-center gap-2&quot;&gt;&lt;span&gt;&lt;span&gt;Ctrl+scroll to zoom&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fullscreen&lt;/span&gt;&lt;span&gt;SVG&lt;/span&gt;&lt;span&gt;PNG&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;error-display hidden px-4 py-3 bg-red-50 dark:bg-red-900/20 border-b border-red-200 dark:border-red-800&quot;&gt;&lt;p&gt;Error rendering diagram:&lt;/p&gt;&lt;pre class=&quot;text-xs text-red-700 dark:text-red-300 mt-1 overflow-auto max-h-40&quot;&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;preview-container relative overflow-hidden bg-white dark:bg-gray-900&quot;&gt;&lt;div class=&quot;svg-wrap flex items-center justify-center&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-container absolute bottom-3 right-3 bg-white/90 dark:bg-gray-800/90 backdrop-blur-sm border border-gray-300 dark:border-gray-600 rounded shadow-lg overflow-hidden&quot;&gt;&lt;div class=&quot;minimap-canvas relative&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-viewport absolute border-2 border-blue-500 bg-blue-500/20 cursor-move&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-controls absolute top-1 right-1 flex flex-col gap-0.5&quot;&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;−&lt;/span&gt;&lt;span&gt;⟲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This aggressive cropping removes most album text while preserving the core visual content.&lt;/p&gt;
&lt;h2&gt;Content Policy &amp;amp; Retry Logic&lt;/h2&gt;
&lt;p&gt;AI models have content policies. Some album covers trigger safety filters (violence, nudity, etc.). The solution: &lt;strong&gt;intelligent retry with alternative images&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;mermaid-wrapper not-prose my-6 border overflow-hidden bg-white dark:bg-gray-900&quot;&gt;&lt;div class=&quot;controls-bar flex items-center justify-between px-4 py-2 surface-container-low border-b&quot;&gt;&lt;div class=&quot;flex items-center gap-3&quot;&gt;&lt;span&gt;Image Preprocessing Pipeline&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;flex items-center gap-2&quot;&gt;&lt;span&gt;&lt;span&gt;Ctrl+scroll to zoom&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fullscreen&lt;/span&gt;&lt;span&gt;SVG&lt;/span&gt;&lt;span&gt;PNG&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;error-display hidden px-4 py-3 bg-red-50 dark:bg-red-900/20 border-b border-red-200 dark:border-red-800&quot;&gt;&lt;p&gt;Error rendering diagram:&lt;/p&gt;&lt;pre class=&quot;text-xs text-red-700 dark:text-red-300 mt-1 overflow-auto max-h-40&quot;&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;preview-container relative overflow-hidden bg-white dark:bg-gray-900&quot;&gt;&lt;div class=&quot;svg-wrap flex items-center justify-center&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-container absolute bottom-3 left-3 bg-white/90 dark:bg-gray-800/90 backdrop-blur-sm border border-gray-300 dark:border-gray-600 rounded shadow-lg overflow-hidden&quot;&gt;&lt;div class=&quot;minimap-canvas relative&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-viewport absolute border-2 border-blue-500 bg-blue-500/20 cursor-move&quot;&gt;&lt;/div&gt;&lt;div class=&quot;minimap-controls absolute top-1 right-1 flex flex-col gap-0.5&quot;&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;−&lt;/span&gt;&lt;span&gt;⟲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The system:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pre-ranks ALL available albums&lt;/strong&gt; by vibrancy/text score&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attempts with top batch&lt;/strong&gt; (albums 1-8)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On policy violation&lt;/strong&gt;, selects the NEXT batch (albums 7-12)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regenerates GPT-4 Vision prompt&lt;/strong&gt; with new images&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retries up to 3 times&lt;/strong&gt; before failing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach has a high success rate because it systematically tries different image combinations.&lt;/p&gt;
&lt;h2&gt;Blacklist Management&lt;/h2&gt;
&lt;p&gt;Some albums are persistent offenders (I’m looking at you, &lt;span&gt;&lt;a href=&quot;https://russ.fm/album/is-this-it-3146367&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;em&gt;Is This It&lt;/em&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://russ.fm/album/med-sud-i-eyrum-vid-spilum-endalaust-21139177&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;em&gt;Með Suð Í Eyrum Við Spilum Endalaust&lt;/em&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;). The config file includes a blacklist:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Blacklist&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;blacklist&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;albums&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Is This It&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Med-sud-i-eyrum-vid-spilum-endalaust&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;artists&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Blacklisted items are filtered out &lt;strong&gt;before&lt;/strong&gt; scoring, ensuring they never make it to the API and I don’t shouted for submitting pictures of bums !!!.&lt;/p&gt;
&lt;h2&gt;Configuration-Driven Design&lt;/h2&gt;
&lt;p&gt;Everything is configurable via &lt;code&gt;fal-collage-config.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Config&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/nano-banana-pro/edit&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fallback&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/reve/fast/remix&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;aspectRatio&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;16:9&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;numImages&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;png&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;resolution&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2K&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;minCount&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;maxCount&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cropRegions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;top&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bottom&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;left&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.05&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.05&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scoring&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;saturationWeight&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.4&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;varianceWeight&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;textPenaltyWeight&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;retry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;maxAttempts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contentPolicyViolation&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enabled&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;excludeProblematicImages&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Want to change the model? Update &lt;code&gt;model.name&lt;/code&gt;. Want more aggressive text removal? Adjust &lt;code&gt;images.cropRegions&lt;/code&gt;. Want to prioritize color variance over saturation? Tweak &lt;code&gt;scoring&lt;/code&gt; weights.&lt;/p&gt;
&lt;h2&gt;Model Selection &amp;amp; Fallback Strategy&lt;/h2&gt;
&lt;p&gt;The collage generator is built around &lt;strong&gt;Nano Banana Pro&lt;/strong&gt; as the primary model, with &lt;strong&gt;WAN 2.5&lt;/strong&gt; configured as a fallback option. This design provides reliability while leveraging the best available technology.&lt;/p&gt;
&lt;h3&gt;Why Nano Banana Pro?&lt;/h3&gt;
&lt;p&gt;When designing the system, I chose Nano Banana Pro for several key reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Photographic Quality&lt;/strong&gt;: Produces photorealistic, cinematic results rather than painterly effects&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High Resolution&lt;/strong&gt;: Native 2K (2048px) output without upscaling artifacts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better Composition&lt;/strong&gt;: Stronger understanding of spatial relationships and scene composition&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text Removal&lt;/strong&gt;: More effective at removing typography while preserving visual elements&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Color Preservation&lt;/strong&gt;: Maintains original album artwork vibrancy better&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Configuration-Driven Model Selection&lt;/h3&gt;
&lt;p&gt;The system uses a flexible config-driven approach defined in &lt;code&gt;fal-collage-config.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Model selection&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/nano-banana-pro/edit&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fallback&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/reve/fast/remix&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The implementation loads the model dynamically:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Model loading&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;modelName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;name &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/nano-banana-pro/edit&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apiInput&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;prompt&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; smartPrompt &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; defaultPrompt&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;image_urls&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; uploadedUrls&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;aspect_ratio&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;16:9&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;num_images&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;output_format&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;png&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resolution&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2K&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; fal&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;subscribe&lt;/span&gt;&lt;span&gt;(modelName&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; apiInput&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;logs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; debug&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;onQueueUpdate&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (debug &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;status &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;IN_PROGRESS&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;logs&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; log&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`[FAL] &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Fallback Strategy&lt;/h3&gt;
&lt;p&gt;While Nano Banana Pro is the primary model, the configuration includes a fallback model (&lt;code&gt;fal-ai/reve/fast/remix&lt;/code&gt;) that can be activated by simply changing the config file. This provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: Switch models without code changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliability&lt;/strong&gt;: Alternative if Nano Banana Pro has availability issues&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Experimentation&lt;/strong&gt;: Easy A/B testing between different models&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-proofing&lt;/strong&gt;: Simple to add new models as FAL.ai releases them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result? Photorealistic context aware music blog headers that look professionally designed-all generated automatically each week.&lt;/p&gt;
&lt;h2&gt;The Results&lt;/h2&gt;
&lt;p&gt;I tested with the post from &lt;span&gt;&lt;a href=&quot;https://www.russ.cloud/2025/11/17/a-sonic-journey-through-euphoria-and-reflection-in-timeless-sounds/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nov 17, 2025 post&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, which contained these albums:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/Zeitgeist.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/Zeitgeist.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/So-Here-We-Are-Best-Of-Doves.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/So-Here-We-Are-Best-Of-Doves.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/De-La-Soul-Is-Dead.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/De-La-Soul-Is-Dead.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/The-Big-Lad-In-The-Windmill.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/The-Big-Lad-In-The-Windmill.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/Lost-in-the-Dream.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/Lost-in-the-Dream.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/Metropolis-Pt-2-Scenes-From-a-Memory.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/Metropolis-Pt-2-Scenes-From-a-Memory.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/Best-of-1969-1974.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/Best-of-1969-1974.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/Specials.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/Specials.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/A-Secret-Wish.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/A-Secret-Wish.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/Songs-from-the-Big-Chair.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/Songs-from-the-Big-Chair.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-17-listened-to-this-week/albums/Stop-Making-Sense-Music-from-a-film-by-Jonathan-Demme-and-Talking-Heads.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-17-listened-to-this-week/albums/Stop-Making-Sense-Music-from-a-film-by-Jonathan-Demme-and-Talking-Heads.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the output below and the covers above it certainly made the right choice for which 8 covers to use for the collage, it correctly skipped the all text &lt;span&gt;&lt;a href=&quot;https://www.russ.fm/album/stop-making-sense-music-from-a-film-by-jonathan-demme-and-talking-heads-27799152/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Stop Making Sense&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and the not very colorful &lt;span&gt;&lt;a href=&quot;https://www.russ.fm/album/songs-from-the-big-chair-124187&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Songs from the Big Chair&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;a&gt;A Secret Wish&lt;/a&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/album/a-secret-wish-35663980/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.russ.fm/album/a-secret-wish-35663980/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;) …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Running the script&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Creating FAL.ai music collage...\n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Input folder: /Users/russ.mckendrick/Code/blog/public/assets/2025-11-17-listened-to-this-week/albums&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Found 11 album images\n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Creating FAL.ai collage...\n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Creating FAL.ai collage (1400x800)...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Analyzing 11 images (color vibrancy + text detection + blacklist filtering)...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Top 11 best images (vibrant + low text):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1. Zeitgeist.jpg [color: 48.0, text: 23.7%, final: 70.9]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2. So-Here-We-Are-Best-Of-Doves.jpg [color: 40.4, text: 9.0%, final: 67.7]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3. De-La-Soul-Is-Dead.jpg [color: 37.1, text: 7.7%, final: 64.8]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4. The-Big-Lad-In-The-Windmill.jpg [color: 35.8, text: 19.5%, final: 59.9]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;5. Lost-in-the-Dream.jpg [color: 31.3, text: 10.9%, final: 58.1]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;6. Metropolis-Pt-2-Scenes-From-a-Memory.jpg [color: 31.1, text: 11.1%, final: 57.8]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;7. Best-of-1969-1974.jpg [color: 31.8, text: 21.0%, final: 55.5]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;8. Specials.jpg [color: 28.4, text: 15.7%, final: 53.7]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;9. A-Secret-Wish.jpg [color: 27.8, text: 18.0%, final: 52.4]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;10. Songs-from-the-Big-Chair.jpg [color: 25.8, text: 12.1%, final: 52.2]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;11. Stop-Making-Sense-Music-from-a-film-by-Jonathan-Demme-and-Talking-Heads.jpg [color: 22.7, text: 13.8%, final: 48.6]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Strategy: Send 8 individual album covers to Gemini 3 Pro model (2-8 range)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Ranked 11 images by vibrancy + text score&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Will retry up to 3 times if content policy issues occur&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Attempt 1: Selected 8 albums:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1. Zeitgeist.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2. So-Here-We-Are-Best-Of-Doves.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3. De-La-Soul-Is-Dead.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4. The-Big-Lad-In-The-Windmill.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;5. Lost-in-the-Dream.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;6. Metropolis-Pt-2-Scenes-From-a-Memory.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;7. Best-of-1969-1974.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;8. Specials.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Pre-processing and uploading 8 albums:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Processing Zeitgeist.jpg...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cropping Zeitgeist.jpg: 1500x1500 → 1349x1050&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ Uploaded: https://v3b.fal.media/files/b/zebra/qh45oC-X8U5EHI4IDnTTl_Zeitgeist.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Processing So-Here-We-Are-Best-Of-Doves.jpg...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cropping So-Here-We-Are-Best-Of-Doves.jpg: 1500x1500 → 1349x1050&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ Uploaded: https://v3b.fal.media/files/b/monkey/qfWi_zFOwABweDyDQE1Sd_So-Here-We-Are-Best-Of-Doves.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Processing De-La-Soul-Is-Dead.jpg...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cropping De-La-Soul-Is-Dead.jpg: 1024x1024 → 921x716&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ Uploaded: https://v3b.fal.media/files/b/tiger/nCSgsSHYN-3SLQ5Cq0qUd_De-La-Soul-Is-Dead.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Processing The-Big-Lad-In-The-Windmill.jpg...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cropping The-Big-Lad-In-The-Windmill.jpg: 1024x1024 → 921x716&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ Uploaded: https://v3b.fal.media/files/b/penguin/HXmy8r0L24DUM52p2bASd_The-Big-Lad-In-The-Windmill.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Processing Lost-in-the-Dream.jpg...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cropping Lost-in-the-Dream.jpg: 1425x1425 → 1282x997&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ Uploaded: https://v3b.fal.media/files/b/rabbit/SngKAtV8avD-tNCCGxtbH_Lost-in-the-Dream.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Processing Metropolis-Pt-2-Scenes-From-a-Memory.jpg...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cropping Metropolis-Pt-2-Scenes-From-a-Memory.jpg: 1024x1024 → 921x716&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ Uploaded: https://v3b.fal.media/files/b/elephant/4dBAexdI-O2phSnBGUe0V_Metropolis-Pt-2-Scenes-From-a-Memory.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Processing Best-of-1969-1974.jpg...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cropping Best-of-1969-1974.jpg: 1024x1024 → 921x716&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ Uploaded: https://v3b.fal.media/files/b/rabbit/buCRs63ekxrIZteMYXkBZ_Best-of-1969-1974.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Processing Specials.jpg...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Cropping Specials.jpg: 1024x1024 → 921x716&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ Uploaded: https://v3b.fal.media/files/b/penguin/dymm0RrVm-ncPUfDHgrFo_Specials.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Analyzing album covers with GPT-4 Vision...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;✓ Generated smart prompt: &quot;Create a cinematic music blog header featuring the vibrant abstract faces from the colorful cover, blending them into the architecture of the church-like building with musicians from the second image. Add the spilled flower pot from the third cover in the foreground, and place the painter from the fourth cover painting this scene. Overlay the dreamy curtains from the fifth cover to add a soft, ethereal layer, using double-exposure techniques. Integrate the collage of faces from the sixth cover subtly into the background, and place the penguins from the seventh cover on the grassy field. Finally, position the black and white group from the eighth cover in the middle, tying the scene together with a cohesive blend of warm and cool tones. Remove all text and typography.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Generating collage with 8 images using Gemini 3 Pro Image...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Using AI-generated prompt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Prompt: &quot;Create a cinematic music blog header featuring the vibrant abstract faces from the colorful cover, blending them into the architecture of the church-like building with musicians from the second image. Add the spilled flower pot from the third cover in the foreground, and place the painter from the fourth cover painting this scene. Overlay the dreamy curtains from the fifth cover to add a soft, ethereal layer, using double-exposure techniques. Integrate the collage of faces from the sixth cover subtly into the background, and place the penguins from the seventh cover on the grassy field. Finally, position the black and white group from the eighth cover in the middle, tying the scene together with a cohesive blend of warm and cool tones. Remove all text and typography.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Aspect ratio: 16:9&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;API payload: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;prompt&quot;: &quot;Create a cinematic music blog header featuring the vibrant abstract faces from the colorful cover, blending them into the architecture of the church-like building with musicians from the second image. Add the spilled flower pot from the third cover in the foreground, and place the painter from the fourth cover painting this scene. Overlay the dreamy curtains from the fifth cover to add a soft, ethereal layer, using double-exposure techniques. Integrate the collage of faces from the sixth cover subtly into the background, and place the penguins from the seventh cover on the grassy field. Finally, position the black and white group from the eighth cover in the middle, tying the scene together with a cohesive blend of warm and cool tones. Remove all text and typography.&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;image_urls&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;https://v3b.fal.media/files/b/zebra/qh45oC-X8U5EHI4IDnTTl_Zeitgeist.jpg&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;https://v3b.fal.media/files/b/monkey/qfWi_zFOwABweDyDQE1Sd_So-Here-We-Are-Best-Of-Doves.jpg&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;https://v3b.fal.media/files/b/tiger/nCSgsSHYN-3SLQ5Cq0qUd_De-La-Soul-Is-Dead.jpg&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;https://v3b.fal.media/files/b/penguin/HXmy8r0L24DUM52p2bASd_The-Big-Lad-In-The-Windmill.jpg&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;https://v3b.fal.media/files/b/rabbit/SngKAtV8avD-tNCCGxtbH_Lost-in-the-Dream.jpg&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;https://v3b.fal.media/files/b/elephant/4dBAexdI-O2phSnBGUe0V_Metropolis-Pt-2-Scenes-From-a-Memory.jpg&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;https://v3b.fal.media/files/b/rabbit/buCRs63ekxrIZteMYXkBZ_Best-of-1969-1974.jpg&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;https://v3b.fal.media/files/b/penguin/dymm0RrVm-ncPUfDHgrFo_Specials.jpg&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;aspect_ratio&quot;: &quot;16:9&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;num_images&quot;: 1,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;output_format&quot;: &quot;png&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;resolution&quot;: &quot;2K&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Using model: fal-ai/nano-banana-pro/edit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Generated image URL: https://v3b.fal.media/files/b/penguin/Bs5Dx01KgUcnveOxhN5am.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;✓ Created FAL.ai collage with 8 vibrant album covers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;\n✓ FAL.ai collage complete!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Output: /Users/russ.mckendrick/Code/blog/test-output/tunes-cover-2025-11-17-listened-to-this-week.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dimensions: 1400×800&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Selected images: 8&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This gave us the following:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex &quot;&gt;&lt;div class=&quot;w-full&quot;&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-11-17-listened-to-this-week.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-11-17-listened-to-this-week.png&quot; alt=&quot;17/11/205&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2752&quot; height=&quot;1536&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;I also went back and did the rest of the year, here are some more examples:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-04-21-listened-to-this-week.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-04-21-listened-to-this-week.png&quot; alt=&quot;21/04/205&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-05-19-listened-to-this-week.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-05-19-listened-to-this-week.png&quot; alt=&quot;19/05/205&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-06-16-listened-to-this-week.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-06-16-listened-to-this-week.png&quot; alt=&quot;16/06/205&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-06-30-listened-to-this-week.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-06-30-listened-to-this-week.png&quot; alt=&quot;30/06/205&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-09-15-listened-to-this-week.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-09-15-listened-to-this-week.png&quot; alt=&quot;15/09/205&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-10-20-listened-to-this-week.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-23-playing-with-nano-banana-pro/tunes-cover-2025-10-20-listened-to-this-week.png&quot; alt=&quot;20/10/205&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Try It Yourself&lt;/h2&gt;
&lt;p&gt;The collage generator is part of my blog’s codebase and runs automatically via &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; Actions every week. You can find the key files below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Main script&lt;/strong&gt;: &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/blog/blob/main/scripts/fal-tunes-cover.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;scripts/fal-tunes-cover.js&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Config&lt;/strong&gt;: &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/blog/blob/main/scripts/fal-collage-config.json&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;scripts/fal-collage-config.json&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tunes generator&lt;/strong&gt;: &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/blog/blob/main/scripts/generate-tunes-post.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;scripts/generate-tunes-post.js (orchestrator)&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Required environment variables:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;FAL_KEY&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;your-fal-api-key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;your-openai-key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Run manually:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scripts/fal-tunes-cover.js&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--input=./albums&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--output=./collage.png&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--debug&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Migrating to Nano Banana Pro transformed my weekly music blog post workflow, what started as a simple image processing task evolved into a AI-powered pipeline that combines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Computer vision (color analysis, text detection)&lt;/li&gt;
&lt;li&gt;GPT-4 Vision (intelligent prompt generation)&lt;/li&gt;
&lt;li&gt;Nano Banana Pro (photographic composition)&lt;/li&gt;
&lt;li&gt;Smart retry logic (content policy handling)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result? Unique cover collages that make each weekly post visually distinctive-and all generated automatically.&lt;/p&gt;
&lt;p&gt;Next time you see a “Listened to This Week” post, you’ll know there’s a lot more than meets the eye behind that cover image.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://fal.ai/models/fal-ai/nano-banana-pro&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;FAL.ai Nano Banana Pro Documentation&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://fal.ai/docs&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;FAL.ai API Reference&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://platform.openai.com/docs/guides/vision&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GPT-4 Vision API&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://sharp.pixelplumbing.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sharp Image Processing&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-11-23-playing-with-nano-banana-pro.Df851ja4.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/11/23/playing-with-nano-banana-pro-ai-powered-album-cover-collages-og.png</blog:ogImage><blog:readingTime>14</blog:readingTime><category>AI</category><category>Blog</category><category>Automation</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>My Starship Prompt Setup</title><link>https://www.russ.cloud/2025/11/15/my-starship-prompt-setup/</link><guid isPermaLink="true">https://www.russ.cloud//2025/11/15/my-starship-prompt-setup/</guid><description>A Deep Dive into a Gruvbox-Themed Terminal</description><pubDate>Sat, 15 Nov 2025 13:48:35 GMT</pubDate><content:encoded>&lt;p&gt;I spend alot of time in the terminal on both my home and work machines so have taken the time to get my terminal prompt looking good and I had been using &lt;span&gt;&lt;a href=&quot;https://github.com/romkatv/powerlevel10k&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PowerLine10k&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; for a few years.&lt;/p&gt;
&lt;p&gt;After trying to debug why my terminal was opening slowly I spotted the warning below at the top of the projects repo:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;PowerLine10k Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;ul&gt;
&lt;li&gt;The project has very limited support&lt;/li&gt;
&lt;li&gt;No new features are in the works&lt;/li&gt;
&lt;li&gt;Most bugs will go unfixed&lt;/li&gt;
&lt;li&gt;Help requests will be ignored&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;I decided it was time for a change.&lt;/p&gt;
&lt;h2&gt;Why Starship?&lt;/h2&gt;
&lt;p&gt;After some quick Googling I decided upon &lt;span&gt;&lt;a href=&quot;https://starship.rs/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Starship&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, it is a cross-shell prompt written in Rust that’s fast, customizable, and incredibly feature-rich. Its repo boasts that unlike traditional shell prompts, &lt;a href=&quot;/glossary/starship/&quot;&gt;Starship&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Works across multiple shells (&lt;a href=&quot;/glossary/zsh/&quot;&gt;zsh&lt;/a&gt;, bash, fish, PowerShell)&lt;/li&gt;
&lt;li&gt;Is blazingly fast (written in Rust)&lt;/li&gt;
&lt;li&gt;Provides context-aware information&lt;/li&gt;
&lt;li&gt;Supports extensive customization through a simple TOML file&lt;/li&gt;
&lt;li&gt;Has minimal dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That ticks a lot of boxes so I decided to give it a try.&lt;/p&gt;
&lt;h2&gt;The Design Philosophy&lt;/h2&gt;
&lt;p&gt;My prompt design follows a few key principles:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Information density without clutter&lt;/strong&gt;: Show relevant context without overwhelming&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual hierarchy&lt;/strong&gt;: Use color and positioning to prioritize information&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gruvbox aesthetics&lt;/strong&gt;: Maintain a consistent retro-groove color scheme&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Left-to-right narrative&lt;/strong&gt;: Left side shows “where I am,” right side shows “what’s happening”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Look good&lt;/strong&gt;: I take a lot of screen shots and do screen shares - so it needs to look good !!!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now this is a little zoomed in, but this is what my prompt looks like:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/prompt-layout.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-11-15-my-starship-prompt-setup/prompt-layout.png&quot; alt=&quot;Prompt Layout&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1460&quot; height=&quot;232&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;The Color Palette: Gruvbox Dark&lt;/h2&gt;
&lt;p&gt;I’m using the &lt;span&gt;&lt;a href=&quot;https://github.com/morhetz/gruvbox&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gruvbox&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; Dark color scheme, which provides a warm, retro aesthetic that’s easy on the eyes during long coding sessions. The palette includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Orange&lt;/strong&gt; (&lt;code&gt;#d65d0e&lt;/code&gt;): System and user context&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Yellow&lt;/strong&gt; (&lt;code&gt;#d79921&lt;/code&gt;): Current directory and status&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aqua&lt;/strong&gt; (&lt;code&gt;#689d6a&lt;/code&gt;): &lt;a href=&quot;/glossary/git/&quot;&gt;Git&lt;/a&gt; information&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blue&lt;/strong&gt; (&lt;code&gt;#458588&lt;/code&gt;): Programming language indicators&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purple&lt;/strong&gt; (&lt;code&gt;#b16286&lt;/code&gt;): Time display&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Red&lt;/strong&gt; (&lt;code&gt;#cc241d&lt;/code&gt;): Battery and critical indicators&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Prompt Layout&lt;/h2&gt;
&lt;p&gt;My prompt is divided into segments, connected with powerline-style arrows. Here’s the breakdown:&lt;/p&gt;
&lt;h3&gt;Left Side: Contextual Information&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;OS and Username Segment (Orange)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;os&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;disabled &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;style &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;bg:color_orange fg:color_fg0&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;show_always &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;format &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;[ $user ]($style)&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The prompt starts with the OS icon (on macOS) and username. While some people hide the username, I keep it visible-it’s a quick visual confirmation of which system I’m on, especially when dealing with remote sessions.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Directory Segment (Yellow)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;format &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;[ $path ]($style)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;truncation_length &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;truncation_symbol &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot; &quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;truncate_to_repo &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The current directory is shown with intelligent truncation. If I’m in a git repository, it truncates relative to the repo root. I’ve also added custom substitutions with icons for common directories, such as &lt;code&gt;~/Code/&lt;/code&gt;, &lt;code&gt;~/Documents&lt;/code&gt; and &lt;code&gt;~/Downloads&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/dir/dir-01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-15-my-starship-prompt-setup/dir/dir-01.png&quot; alt=&quot;Code&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/dir/dir-02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-15-my-starship-prompt-setup/dir/dir-02.png&quot; alt=&quot;Documents&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/dir/dir-03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-15-my-starship-prompt-setup/dir/dir-03.png&quot; alt=&quot;Downloads&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/dir/dir-04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-15-my-starship-prompt-setup/dir/dir-04.png&quot; alt=&quot;Pictures&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Git Metrics Segment (Aqua)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;git_metrics&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;disabled &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;format &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;[[+$added]($added_style)/[-$deleted]($deleted_style) ]&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This is where things get interesting. The git segment shows four pieces of information:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Branch name&lt;/strong&gt; with an icon&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git status&lt;/strong&gt; showing staged/modified/untracked files&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git state&lt;/strong&gt; for operations like rebasing or merging&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git metrics&lt;/strong&gt; showing additions and deletions&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/git.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-11-15-my-starship-prompt-setup/git.png&quot; alt=&quot;Git Metrics&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2370&quot; height=&quot;232&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The metrics feature is particularly useful-at a glance, I can see &lt;code&gt;+7/-7&lt;/code&gt; to understand the scope of my changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Language Environment Segment&lt;/strong&gt; (Blue)&lt;/p&gt;
&lt;p&gt;This segment automatically detects and displays icons for active programming environments:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/lang/python.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-15-my-starship-prompt-setup/lang/python.png&quot; alt=&quot;Python&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/lang/nodejs.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-15-my-starship-prompt-setup/lang/nodejs.png&quot; alt=&quot;Node.js&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-11-15-my-starship-prompt-setup/lang/go.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-11-15-my-starship-prompt-setup/lang/go.png&quot; alt=&quot;Go&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The segment only appears when relevant files are detected in the current directory, keeping the prompt clean when you’re not in a project.&lt;/p&gt;
&lt;h3&gt;Right Side: Status Information&lt;/h3&gt;
&lt;p&gt;Using the &lt;code&gt;$fill&lt;/code&gt; variable, I push status information to the right side of the terminal:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt; (Purple)&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;disabled &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;time_format &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;%R&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;format &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;[[ 󰥔 $time ]]&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A simple 24-hour clock (e.g., &lt;code&gt;14:30&lt;/code&gt;). Some find this redundant, but I appreciate having timestamps in my terminal sessions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Battery&lt;/strong&gt; (Red background)&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;battery&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;full_symbol &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;󰁹&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;charging_symbol &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;󰂄&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;discharging_symbol &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;󰂃&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Battery status with appropriate icons. The red background serves as a visual alert when I’m unplugged.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command Duration&lt;/strong&gt; (Aqua)&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;cmd_duration&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;min_time &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;format &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;[[  $duration ]]&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Shows execution time for commands that take longer than 2 seconds. This is invaluable for understanding which operations are slow and need optimization.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exit Status&lt;/strong&gt; (Yellow)&lt;/p&gt;
&lt;p&gt;Displays an error indicator (✖) when the last command failed, along with the exit code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Background Jobs&lt;/strong&gt; (Orange)&lt;/p&gt;
&lt;p&gt;Shows a  icon with a count when there are background jobs running.&lt;/p&gt;
&lt;h3&gt;The Command Line&lt;/h3&gt;
&lt;p&gt;Finally, after all the context, comes the actual command line:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;character&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;success_symbol &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;[](bold fg:color_bright_green)&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;error_symbol &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;[](bold fg:color_bright_red)&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A simple &lt;code&gt;❯&lt;/code&gt; that turns red when the previous command failed. I’ve also configured vim mode indicators (since I use vi-mode in zsh):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Normal mode (green)&lt;/li&gt;
&lt;li&gt;Replace mode (purple)&lt;/li&gt;
&lt;li&gt;Visual mode (yellow)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Performance Considerations&lt;/h2&gt;
&lt;p&gt;Starship is fast, but I’ve made a few tweaks to ensure it stays that way:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;scan_timeout &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;35&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;command_timeout &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;700&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;scan_timeout&lt;/code&gt; controls how long Starship spends looking for context (like git repos), while &lt;code&gt;command_timeout&lt;/code&gt; limits how long individual modules can run. These values ensure the prompt never feels laggy.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;Want to use this configuration? Here’s how:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install Starship:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;starship&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install a Nerd Font:&lt;/strong&gt; The icons require a Nerd Font. I use Hack Nerd Font Mono:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;homebrew/cask-fonts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;font-hack-nerd-font&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure your shell:&lt;/strong&gt; Add to your &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;eval&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;starship&lt;/span&gt;&lt;span&gt; init zsh&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Edit your configuration:&lt;/strong&gt; Find the configuration at &lt;code&gt;~/.config/starship.toml&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Customization Tips&lt;/h2&gt;
&lt;p&gt;The beauty of Starship is how easy it is to customize. Here are some ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Minimize for remote sessions&lt;/strong&gt;: Use different configs based on &lt;code&gt;$SSH_CONNECTION&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add custom modules&lt;/strong&gt;: Starship supports custom commands and environment variables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme variations&lt;/strong&gt;: Try different palettes like Tokyo Night, Nord, or Dracula&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context-aware layouts&lt;/strong&gt;: Use different formats for different directories&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Your terminal prompt is personal - it should reflect how you work and what information you need at a glance. My Starship configuration has evolved from when I was using PowerLine10k and will continue to be added to as time goes on.&lt;/p&gt;
&lt;p&gt;The key is to start with something functional, then iterate. Pay attention to which information you actually use and which just becomes noise. A great prompt should feel invisible when you don’t need it and invaluable when you do.&lt;/p&gt;
&lt;p&gt;You can find my current and full &lt;code&gt;starship.toml&lt;/code&gt; configuration at the following link:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/dotfiles/blob/main/starship.toml&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;dotfiles/starship.toml at main · russmckendrick/dotfiles&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;My dotfiles - you probably don&apos;t need to use these. - russmckendrick/dotfiles&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-37c8080bb897.jpg&quot; alt=&quot;My dotfiles - you probably don&apos;t need to use these. - russmckendrick/dotfiles&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-11-15-my-starship-prompt-setup.Bmxeb73M.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/11/15/my-starship-prompt-setup-og.png</blog:ogImage><blog:readingTime>6</blog:readingTime><category>MacOS</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Rotating Azure DevOps SSH Keys: How to Update Your Git Remotes and SSH Config</title><link>https://www.russ.cloud/2025/10/27/rotating-azure-devops-ssh-keys-how-to-update-your-git-remotes-and-ssh-config/</link><guid isPermaLink="true">https://www.russ.cloud//2025/10/27/rotating-azure-devops-ssh-keys-how-to-update-your-git-remotes-and-ssh-config/</guid><description>Learn how to rotate expired Azure DevOps SSH keys, update your SSH configuration, and fix Git remote authentication quickly and securely.</description><pubDate>Mon, 27 Oct 2025 19:11:06 GMT</pubDate><content:encoded>&lt;p&gt;We’ve all been there. Your &lt;a href=&quot;/glossary/ssh-key/&quot;&gt;SSH key&lt;/a&gt; for &lt;a href=&quot;/glossary/azure-devops/&quot;&gt;Azure DevOps&lt;/a&gt; expires, you generate a new one, and suddenly you’re faced with the task of updating all your local repositories. While it’s not complicated, it’s easy to forget the exact steps when you’re in the moment. Here’s a straightforward guide to get you back up and running quickly.&lt;/p&gt;
&lt;h2&gt;The Problem: Rotating Azure DevOps SSH Keys&lt;/h2&gt;
&lt;p&gt;SSH keys expire (and they should – it’s good &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; practice). When you generate a new SSH key pair and add the public key to Azure DevOps, your local &lt;a href=&quot;/glossary/git/&quot;&gt;Git&lt;/a&gt; repositories still point to the old configuration. You need to update two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Your SSH configuration file&lt;/li&gt;
&lt;li&gt;Your Git remote URLs to use the new SSH config&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you haven’t already, you’ll need to generate your new SSH key:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Generate your SSH key&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssh-keygen&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rsa-sha2-512&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-C&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;user@domain.com&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;You will be asked to provide the file path and name for the key. I entered the following: &lt;code&gt;/Users/russ.mckendrick/.ssh/ado_id_rsa&lt;/code&gt;.&lt;br /&gt;
You’ll need to update yours as required – just make sure the key name isn’t the same as an existing one.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When you have your key, make a note of the path, as we’ll need it for the next step.&lt;/p&gt;
&lt;h2&gt;Step 1: Configure Your SSH Config for Azure DevOps&lt;/h2&gt;
&lt;p&gt;First, update your &lt;code&gt;~/.ssh/config&lt;/code&gt; file to include your new Azure DevOps SSH configuration:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;SSH Config&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Host azure-devops&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Hostname ssh.dev.azure.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;User git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IdentityFile ~/.ssh/ado_id_rsa&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IdentitiesOnly yes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This creates a convenient alias (&lt;code&gt;azure-devops&lt;/code&gt;) that you can use instead of typing out the full hostname every time. The &lt;code&gt;IdentitiesOnly yes&lt;/code&gt; directive is particularly important, as it ensures SSH only uses the specified identity file, preventing authentication issues when you have multiple SSH keys.&lt;/p&gt;
&lt;h2&gt;Step 2: Check Your Current Git Remotes&lt;/h2&gt;
&lt;p&gt;Before making changes, let’s see what we’re working with:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Check the remotes&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You’ll likely see something like:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The results&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;git@ssh.dev.azure.com:v3/YourOrg/YourProject/YourRepo&lt;/span&gt;&lt;span&gt; (fetch)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;git@ssh.dev.azure.com:v3/YourOrg/YourProject/YourRepo&lt;/span&gt;&lt;span&gt; (push)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Step 3: Update Your Git Remote URLs for Azure DevOps&lt;/h2&gt;
&lt;p&gt;Now update your Git remote to use the new SSH config host:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Set the new remote&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git@azure-devops:v3/YourOrg/YourProject/YourRepo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The key change here is replacing the hostname (&lt;code&gt;ssh.dev.azure.com&lt;/code&gt; or &lt;code&gt;vs-ssh.visualstudio.com&lt;/code&gt;) with your SSH config host alias (&lt;code&gt;azure-devops&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Step 4: Verify the Remote Configuration&lt;/h2&gt;
&lt;p&gt;Check that the remote was updated correctly:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Confirm the new remotes&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You should now see:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The results&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;git@azure-devops:v3/YourOrg/YourProject/YourRepo&lt;/span&gt;&lt;span&gt; (fetch)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;git@azure-devops:v3/YourOrg/YourProject/YourRepo&lt;/span&gt;&lt;span&gt; (push)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Step 5: Test Your Azure DevOps SSH Connection&lt;/h2&gt;
&lt;p&gt;Before you start working, verify that SSH authentication is working:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Check the connection&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-T&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git@azure-devops&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You should see a response like:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The result of the test connection&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;remote:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Shell&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;access&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;supported.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Don’t worry – this is exactly what you want to see! It means SSH successfully authenticated, but Azure DevOps doesn’t provide shell access (which is expected and secure).&lt;/p&gt;
&lt;p&gt;Finally, test with an actual Git operation:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Run a git command&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If this completes without prompting for credentials or throwing errors, you’re all set!&lt;/p&gt;
&lt;h2&gt;Handling Multiple Git Remotes&lt;/h2&gt;
&lt;p&gt;If you work with multiple remotes (for example, &lt;code&gt;origin&lt;/code&gt; and &lt;code&gt;upstream&lt;/code&gt; in a fork workflow), you’ll need to update each one:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git@azure-devops:v3/MainOrg/MainProject/MainRepo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;upstream&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git@azure-devops:v3/ForkOrg/ForkProject/ForkRepo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Automating SSH Key Rotation Across Multiple Repositories&lt;/h2&gt;
&lt;p&gt;If you have many local repositories that need updating, you can create a simple script to automate this process:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#!/bin/bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Find all Git repositories in your projects directory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/projects&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;.git&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gitdir&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;repo&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;dirname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$gitdir&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Updating &lt;/span&gt;&lt;span&gt;$repo&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$repo&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Get current remote URL&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;current_url&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&amp;gt;&lt;/span&gt;&lt;span&gt;/dev/null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Check if it&apos;s an Azure DevOps SSH URL&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[[&lt;/span&gt;&lt;span&gt; $current_url &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&quot;dev.azure.com&quot;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[[&lt;/span&gt;&lt;span&gt; $current_url &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&quot;visualstudio.com&quot;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;]];&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# Extract the path (everything after the hostname)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$current_url&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;s/.*://&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new_url&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;git@azure-devops:&lt;/span&gt;&lt;span&gt;$path&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;  Old: &lt;/span&gt;&lt;span&gt;$current_url&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;  New: &lt;/span&gt;&lt;span&gt;$new_url&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$new_url&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Save this as &lt;code&gt;update-ado-remotes.sh&lt;/code&gt;, make it executable with &lt;code&gt;chmod +x update-ado-remotes.sh&lt;/code&gt;, and run it from your projects directory.&lt;/p&gt;
&lt;h2&gt;Best Practices for Azure DevOps SSH Key Management&lt;/h2&gt;
&lt;p&gt;While we’re on the topic, here are some best practices for managing SSH keys effectively:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use strong passphrases&lt;/strong&gt;: Always protect your SSH private keys with a strong passphrase. Use &lt;code&gt;ssh-agent&lt;/code&gt; to avoid typing it repeatedly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set expiration dates&lt;/strong&gt;: Azure DevOps allows you to set expiration dates on SSH keys. Use this feature – it’s a great way to enforce key rotation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep your config organised&lt;/strong&gt;: As you accumulate more SSH configurations, keep your &lt;code&gt;~/.ssh/config&lt;/code&gt; file well-organised with comments. Future you will thank present you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document your setup&lt;/strong&gt;: Keep notes about which keys are used where. When you have multiple keys for different services or organisations, this documentation is invaluable.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;In short:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rotate your Azure DevOps SSH keys regularly.&lt;/li&gt;
&lt;li&gt;Update your SSH config and Git remotes to match.&lt;/li&gt;
&lt;li&gt;Test connections before committing changes.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;/glossary/automation/&quot;&gt;automation&lt;/a&gt; for bulk updates.&lt;/li&gt;
&lt;li&gt;Keep keys protected and your config tidy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few small steps now will save you a lot of frustration later.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-10-27-rotating-azure-devops-ssh-keys-a-quick-guide-to-updating-your-git-remotes.Xzq_KzZW.jpg</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/10/27/rotating-azure-devops-ssh-keys-how-to-update-your-git-remotes-and-ssh-config-og.png</blog:ogImage><blog:readingTime>5</blog:readingTime><category>Azure</category><category>DevOps</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>A new coat of paint</title><link>https://www.russ.cloud/2025/10/12/a-new-coat-of-paint/</link><guid isPermaLink="true">https://www.russ.cloud//2025/10/12/a-new-coat-of-paint/</guid><description>After 5 years with Hugo, I rebuilt this blog using Astro, Tailwind CSS v4, and modern JavaScript tooling. Here&apos;s what changed and why.</description><pubDate>Sun, 12 Oct 2025 11:52:51 GMT</pubDate><content:encoded>&lt;p&gt;While there are posts in this blog dating back to 2014, nearly the last 5 years it has been built and deployed using a combination of &lt;span&gt;&lt;a href=&quot;https://gohugo.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Hugo&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://pages.cloudflare.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cloudflare Pages&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and the excellent &lt;span&gt;&lt;a href=&quot;https://github.com/adityatelange/hugo-PaperMod&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PaperMod&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; theme.&lt;/p&gt;
&lt;h2&gt;Why the change?&lt;/h2&gt;
&lt;p&gt;For my other sites …&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://russ.fm/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;russ.fm&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;russ.fm&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://russ.tools/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;russ.tools&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;russ.tools&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;… I have been using various Javascript tools, and I wanted to use something similar for this blog. The Hugo setup served me well, but I wanted to modernize the stack and bring it in line with the tools I’m using for my other projects.&lt;/p&gt;
&lt;h3&gt;The Hugo pain points&lt;/h3&gt;
&lt;p&gt;Don’t get me wrong - Hugo is an excellent static site generator. It’s incredibly fast, has a great community, and PaperMod is a beautiful theme. However, after five years, I started feeling some friction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Template language limitations&lt;/strong&gt;: Hugo’s Go templating syntax, while powerful, felt limited from what I had been using for my other projects&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme customization&lt;/strong&gt;: Making changes to PaperMod was challenging, I had to make changes to the underlying HTML and CSS and then figure out a way of applying those changes to the using a sub-theme&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling ecosystem&lt;/strong&gt;: My other projects use modern JavaScript tooling (TypeScript, ESLint, Prettier) and I wanted that same developer experience for my blog&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content scripting&lt;/strong&gt;: The automated weekly music posts I generate required &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; scripts that felt disconnected from the blog itself&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Component reusability&lt;/strong&gt;: I had built custom components for my other sites that I couldn’t easily reuse in Hugo&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Evaluating JavaScript static site generators&lt;/h3&gt;
&lt;p&gt;While the other sites are both single page web apps, I wanted to stick with a static site generator for the blog. When it comes to JavaScript static site generators, I evaluated several options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://nextjs.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Next.js&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - The obvious choice given its popularity, but felt like overkill for a simple blog. The app router and server components, while powerful, added complexity I didn’t need. Plus, the build times for a large blog can get hefty.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://www.gatsbyjs.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gatsby&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - I had used Gatsby in the past and appreciated its GraphQL data layer, but the framework has lost momentum in recent years. The plugin ecosystem felt stagnant, and I wanted something with a more active community.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://www.11ty.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Eleventy (11ty)&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - A strong contender with great flexibility and performance. However, it felt too minimal - I’d need to build a lot of structure myself. The template language agnosticism, while nice, meant less cohesive tooling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://vitepress.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;VitePress&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - Excellent for documentation sites but too opinionated for a personal blog. The Vue-centric approach also meant I’d be writing components in a framework I don’t use elsewhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://astro.build&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Astro&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - After working through a “Hello World” blog implementation, Astro emerged as the clear winner.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s why it clicked with me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Content Collections&lt;/strong&gt;: Type-safe content management with Zod schemas felt like a massive upgrade&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Framework agnostic&lt;/strong&gt;: While I used React components where needed, I wasn’t locked into any framework&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Island architecture&lt;/strong&gt;: Ship only the JavaScript you need, when you need it&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MDX first-class support&lt;/strong&gt;: Unlike Hugo’s shortcodes, MDX components are just JavaScript/React&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Excellent DX&lt;/strong&gt;: TypeScript throughout, great error messages, fast dev server&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Active ecosystem&lt;/strong&gt;: Integrations for everything I needed (Tailwind, image optimization, sitemap, etc.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Nearly perfect Lighthouse scores out of the box&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The tipping point&lt;/h3&gt;
&lt;p&gt;The final push came when I wanted to overhaul my automated weekly music posts. The Python/CrewAI implementation worked, but required context switching between languages and tools. With Astro and JavaScript, I could:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep all &lt;a href=&quot;/glossary/automation/&quot;&gt;automation&lt;/a&gt; scripts in the same language as the blog&lt;/li&gt;
&lt;li&gt;Share types and utilities between the blog and automation&lt;/li&gt;
&lt;li&gt;Use the same content collections &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; to generate posts&lt;/li&gt;
&lt;li&gt;Leverage the Node.js ecosystem for API integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It just made sense to consolidate around a single stack.&lt;/p&gt;
&lt;h2&gt;The new Astro-based tech stack&lt;/h2&gt;
&lt;p&gt;The blog is now built with a modern Astro and JavaScript-first stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://astro.build&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Astro&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - The core framework, providing excellent performance and flexibility&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://tailwindcss.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tailwind CSS v4&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - For utility-first styling with dark mode support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://expressive-code.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Expressive Code&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - Beautiful syntax highlighting with &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; Dark/Light themes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://pagefind.app/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Pagefind&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - Fast, client-side search functionality&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://swup.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Swup&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - Smooth page transitions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://tabler.io/icons&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tabler Icons&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - Icon library&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://pages.cloudflare.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cloudflare Pages&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; - Hosting and deployment&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Key features&lt;/h2&gt;
&lt;h3&gt;Content Collections&lt;/h3&gt;
&lt;p&gt;The site uses Astro’s content collections for type-safe content management. Blog posts are stored in &lt;code&gt;src/content/blog/&lt;/code&gt; as MDX files with Zod schema validation for frontmatter. This provides compile-time safety while maintaining backward compatibility with Hugo-style frontmatter.&lt;/p&gt;
&lt;h3&gt;Auto-generated OpenGraph images&lt;/h3&gt;
&lt;p&gt;Using &lt;code&gt;astro-og-canvas&lt;/code&gt;, every blog post automatically gets a custom OpenGraph image generated at build time. These images feature the site logo, gradient background, and consistent branding - perfect for social media sharing.&lt;/p&gt;
&lt;p&gt;For example, here is what the OpenGraph image looks like for the post I made last week:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.russ.cloud/2025/10/04/install-n8n-locally-using-cloudflare/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Install n8n locally using Cloudflare&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Learn to install n8n locally with Docker and Cloudflare Tunnel. Includes PostgreSQL setup and Zero Trust security for home lab deployment.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;russ.cloud&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/russ.cloud-d7373bdd3491.jpg&quot; alt=&quot;Install n8n locally using Cloudflare&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h3&gt;MDX component system&lt;/h3&gt;
&lt;p&gt;Global MDX components are automatically available in all blog posts without imports. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Media embeds&lt;/strong&gt;: YouTube, Instagram, Giphy, Reddit&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audio&lt;/strong&gt;: MP3/OGG/WAV player, Apple Music&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content&lt;/strong&gt;: LinkPreview, ChatMessage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Callouts&lt;/strong&gt;: Note, Tip, Info, Important, Warning, Caution&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LightGallery&lt;/strong&gt;: Image galleries with a custom meta file plugin for captions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The LightGallery component maintains compatibility with Hugo’s &lt;code&gt;.meta&lt;/code&gt; file system, making the migration seamless.&lt;/p&gt;
&lt;h3&gt;Weekly music posts automation&lt;/h3&gt;
&lt;p&gt;One of the most exciting updates was to the automated “Weekly Tunes” generator. This system:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fetches listening stats from Last.fm for the previous week&lt;/li&gt;
&lt;li&gt;Retrieves album/artist metadata and images from &lt;span&gt;&lt;a href=&quot;https://russ.fm&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;russ.fm&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Uses AI (LangChain with OpenAI GPT-5) to research each album and write engaging content&lt;/li&gt;
&lt;li&gt;Generates a complete MDX post with galleries&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This was migrated from a Python/CrewAI implementation to JavaScript/LangChain.js, providing better integration with the Astro ecosystem, and also it gave me chance to roll my sleeves up and play with LangChain.&lt;/p&gt;
&lt;p&gt;For more information on the old implementation see the following post:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.russ.cloud/2024/05/06/updating-my-blog-using-crewai/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;russ.cloud&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;russ.cloud&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You can view the results of the new implementation at &lt;a href=&quot;/2025/10/06/exploring-the-echoes-of-melancholy-from-therapy-to-tears-for-fears/&quot;&gt;this post&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;SEO and performance&lt;/h3&gt;
&lt;p&gt;The site includes comprehensive SEO optimization via &lt;code&gt;astro-seo-plugin&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic sitemap generation (excluding draft posts)&lt;/li&gt;
&lt;li&gt;robots.txt generation&lt;/li&gt;
&lt;li&gt;RSS feed at &lt;code&gt;/rss.xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Canonical URLs and meta tags&lt;/li&gt;
&lt;li&gt;Twitter Card and OpenGraph support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Performance is excellent with nearly perfect Lighthouse scores, thanks to Astro’s island architecture and static site generation.&lt;/p&gt;
&lt;h3&gt;Dark mode and styling&lt;/h3&gt;
&lt;p&gt;Dark mode is implemented via a &lt;code&gt;.dark&lt;/code&gt; class on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element, with Tailwind CSS v4 providing theme-aware utilities. The theme switcher persists preferences and integrates with Expressive Code for syntax highlighting.&lt;/p&gt;
&lt;h3&gt;Page transitions&lt;/h3&gt;
&lt;p&gt;Swup provides smooth fade transitions between pages while maintaining performance. Careful cleanup hooks prevent DOM artifacts from libraries like LightGallery that append to &lt;code&gt;document.body&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Migration considerations&lt;/h2&gt;
&lt;p&gt;The migration involved several key decisions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hugo shortcodes → MDX components&lt;/strong&gt;: All custom shortcodes were reimplemented as Astro components&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL structure&lt;/strong&gt;: Maintained the date-based pattern &lt;code&gt;/[year]/[month]/[day]/[slug]&lt;/code&gt; for backward compatibility&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frontmatter&lt;/strong&gt;: Support for both Astro and Hugo-style fields ensures old posts work without modification&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Image handling&lt;/strong&gt;: Assets moved from &lt;code&gt;static/&lt;/code&gt; to &lt;code&gt;src/assets/&lt;/code&gt; for Astro’s image optimization&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build optimization&lt;/strong&gt;: &lt;code&gt;@playform/compress&lt;/code&gt; handles compression of all static assets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;While I am happy with the new setup, there are still a few things I want to improve - but I am holding off on those for now as I slowly bring my site score on &lt;span&gt;&lt;a href=&quot;https://pagespeed.web.dev/analysis/https-www-russ-cloud/kx1utv17io?form_factor=desktop&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Lighthouse up to 100&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and work through the issues which &lt;span&gt;&lt;a href=&quot;http://ahrefs.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ahrefs&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is flagging.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;After five years running on Hugo and the PaperMod theme, this blog has been completely rebuilt using a modern JavaScript-first stack centered around Astro. The migration brings significant improvements including type-safe content collections with Zod validation, automatically generated OpenGraph images for every post, and a global MDX component system that eliminates the need for imports.
Key features include seamless media embeds, a migrated weekly music automation system (now using JavaScript/LangChain.js instead of Python/CrewAI), comprehensive SEO optimization, and smooth page transitions via Swup. The new stack maintains backward compatibility with Hugo-style frontmatter and URL structures while leveraging Astro’s island architecture for excellent performance.
The migration required reimplementing all Hugo shortcodes as Astro components and reorganizing assets, but the result is a more maintainable codebase that aligns with modern web development practices and the author’s other projects.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-10-12-a-new-coat-of-paint.DRa_fU_d.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/10/12/a-new-coat-of-paint-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>blog</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Install n8n locally using Cloudflare</title><link>https://www.russ.cloud/2025/10/04/install-n8n-locally-using-cloudflare/</link><guid isPermaLink="true">https://www.russ.cloud//2025/10/04/install-n8n-locally-using-cloudflare/</guid><description>Learn to install n8n locally with Docker and Cloudflare Tunnel. Includes PostgreSQL setup and Zero Trust security for home lab deployment.</description><pubDate>Sat, 04 Oct 2025 17:52:55 GMT</pubDate><content:encoded>&lt;p&gt;I finally got around to getting a small AMD Ryzen 7 Mini PC with 32GB of RAM to use as part of my home lab to replace some aging Raspberry Pis.&lt;/p&gt;
&lt;p&gt;While it came with Windows 11 (which i am sure was fine and not dodgy in any way) I instead opted to run the server version of Ubuntu 24.04 LTS and immediately trashed the Windows install. Given everyone is making videos about running &lt;span&gt;&lt;a href=&quot;https://n8n.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;n8n&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; on VPS servers I decided to do the same and run it locally on my new machine.&lt;/p&gt;
&lt;h2&gt;Installing Docker&lt;/h2&gt;
&lt;p&gt;As I was running a fresh Ubuntu 24.04 install I needed to install Docker before I could run &lt;a href=&quot;/glossary/n8n/&quot;&gt;n8n&lt;/a&gt; locally, To do this I first needed to install the prerequisites and then setup the official Docker repository:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the prerequisites and setting up the Docker repository&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-transport-https&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ca-certificates&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;software-properties-common&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-fsSL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://download.docker.com/linux/ubuntu/gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dearmor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/share/keyrings/docker-archive-keyring.gpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;deb [arch=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;dpkg&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--print-architecture&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;lsb_release&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-cs&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; stable&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tee&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/apt/sources.list.d/docker.list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/dev/null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once the repository is setup I could install Docker itself and configure it to run as the current user:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Docker&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker-ce&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker-ce-cli&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;containerd.io&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker-compose-plugin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;usermod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-aG&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; $USER&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;newgrp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, I checked the Docker version to ensure it was installed correctly:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Checking the Docker version&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;28.5.0,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;887030f&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With that done I could start to install n8n locally.&lt;/p&gt;
&lt;h2&gt;Preparing the environment&lt;/h2&gt;
&lt;p&gt;As I will be running a few different services locally on the same machine I decided to use &lt;a href=&quot;/glossary/docker-compose/&quot;&gt;Docker Compose&lt;/a&gt; and keep all of the services needed for n8n in one file. We will be running the following containers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://docs.n8n.io/hosting/installation/docker/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;docker.n8n.io/n8nio/n8n&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: The main n8n service&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://hub.docker.com/_/postgres&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;postgres:16&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: The PostgreSQL database for n8n&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://hub.docker.com/r/cloudflare/cloudflared&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;cloudflare/cloudflared:latest&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: The &lt;a href=&quot;/glossary/cloudflare-tunnel/&quot;&gt;Cloudflare Tunnel&lt;/a&gt; for n8n&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Setting up Cloudflare Tunnel&lt;/h3&gt;
&lt;p&gt;I host all of my domains via Cloudflare (as well as this blog) so using their &lt;span&gt;&lt;a href=&quot;https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tunnel&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; service for routing the traffic to my local n8n instance was a no-brainer.&lt;/p&gt;
&lt;p&gt;To do this signup for their Zero Trust service, using the free tier is fine, once you have signed up you need to create a new tunnel, you can this by going &lt;strong&gt;Network&lt;/strong&gt; &amp;gt; &lt;strong&gt;Tunnels&lt;/strong&gt; &amp;gt; &lt;strong&gt;Create a Tunnel&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/cloudflare/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/cloudflare/01.png&quot; alt=&quot;Create a Tunnel&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2708&quot; height=&quot;1784&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On the Select your tunnel type screen select &lt;strong&gt;Cloudflare&lt;/strong&gt;. Next you will be asked to name the tunnel, in the screens below I have called mine &lt;strong&gt;n8nBlogPost&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/cloudflare/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/cloudflare/02.png&quot; alt=&quot;Name the Tunnel&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2708&quot; height=&quot;1784&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Next you will be taken to the &lt;strong&gt;Install and run connectors&lt;/strong&gt; screen, here select &lt;strong&gt;Docker&lt;/strong&gt;, copy and page the command you get given as this contains the &lt;code&gt;--token&lt;/code&gt; parameter you will need to use later the command will look something like this:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Copying the Cloudflare Tunnel token&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cloudflare/cloudflared:latest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tunnel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--no-autoupdate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--token&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;eyJhIjoiMzQwODksoTHingRealyLong5YzNotaR4alTok3N&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Store your token carefully. This is a sensitive token that allows the connector to run. Anyone with access to this token will be able to run the tunnel.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/cloudflare/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/cloudflare/03.png&quot; alt=&quot;Install and run connectors&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2708&quot; height=&quot;1784&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We won’t be bringing the tunnel up here, we will do that later when we setup the Docker Compose file so just move onto the next screen.&lt;/p&gt;
&lt;p&gt;Here, we will be setting up the domain and ports for the tunnel, in the screens below I have called mine &lt;strong&gt;n8n.domain.com&lt;/strong&gt; and have used the service we will be tunnelling to:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/cloudflare/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/cloudflare/04.png&quot; alt=&quot;Set the domain and Service&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2708&quot; height=&quot;1784&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the screenshot above I have set the Hostname as &lt;strong&gt;n8n.wibble.foo&lt;/strong&gt; (Yes &lt;span&gt;&lt;a href=&quot;https://wibble.foo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;wibble.foo&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is a real domain I own) and as we will be proxying to our local n8n instance on port 5678 I set the type to &lt;strong&gt;HTTP&lt;/strong&gt; and URL as &lt;strong&gt;n8n:5678&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once setup we can click on &lt;strong&gt;Complete setup&lt;/strong&gt; and you will be taken back to the &lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt; screen where you will see your new tunnel listed, as we haven’t brought up the connector you will see it as &lt;strong&gt;INACTIVE&lt;/strong&gt;, don’t worry about this yet.&lt;/p&gt;
&lt;h3&gt;Setting the folders and .env file&lt;/h3&gt;
&lt;p&gt;First we need to generate a secure random key for which will be used for encryption, you can do this by running the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Generating a secure random key for encryption&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;openssl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rand&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-hex&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Make a note of the random key and we can move on to creating the folders and setting the permissions:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting the folders&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/local-files&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-R&lt;/span&gt;&lt;span&gt; $USER&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$USER &lt;/span&gt;&lt;span&gt;/opt/n8n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-R&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000:1000&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/local-files&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;vi&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/.env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Inside the .env file we need to set the following variables (make sure you use your own values):&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Environment variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Domain Configuration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PUBLIC_DOMAIN=n8n.domain.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# PostgreSQL Configuration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;POSTGRES_USER=root&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;POSTGRES_PASSWORD=some-secure-password-here&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;POSTGRES_DB=n8n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;POSTGRES_NON_ROOT_USER=n8n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;POSTGRES_NON_ROOT_PASSWORD=some-secure-password-here&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Timezone&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GENERIC_TIMEZONE=Europe/London&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# n8n Configuration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# IMPORTANT: Generate a secure random key for encryption&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Use: openssl rand -hex 32&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;N8N_ENCRYPTION_KEY=random-key-here&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Cloudflare Tunnel Token (get from Cloudflare dashboard)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CLOUDFLARE_TUNNEL_TOKEN=your-cloudflare-tunnel-token&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Enter the values you generated for the encryption key, set some passwords for the PostgreSQL database and add the Cloudflare Tunnel Token we grabbed earlier (make sure the domain matches the one you set up in Cloudflare) and save the file.&lt;/p&gt;
&lt;h3&gt;Download the Database setup script&lt;/h3&gt;
&lt;p&gt;Next we need to create a script to setup the database:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating the database setup script&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://raw.githubusercontent.com/russmckendrick/local-n8n-with-cloudflare/refs/heads/main/init-data.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/init-data.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will download the script from my &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/local-n8n-with-cloudflare&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;local-n8n-with-cloudflare&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; repository.&lt;/p&gt;
&lt;h3&gt;Setup Docker Compose file&lt;/h3&gt;
&lt;p&gt;Next we need to create a Docker Compose file:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating the Docker Compose file&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://raw.githubusercontent.com/russmckendrick/local-n8n-with-cloudflare/refs/heads/main/docker-compose.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/docker-compose.yml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will download the following file:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Docker Compose file&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;volumes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;db_storage&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;driver&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;local&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;n8n_data&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;driver&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;local&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;networks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;driver&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bridge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cloudflare&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;driver&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bridge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;postgres&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;postgres:16&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unless-stopped&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;networks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;environment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;POSTGRES_USER&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${POSTGRES_USER}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${POSTGRES_PASSWORD}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;POSTGRES_DB&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${POSTGRES_DB}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;POSTGRES_NON_ROOT_USER&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${POSTGRES_NON_ROOT_USER}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;POSTGRES_NON_ROOT_PASSWORD&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${POSTGRES_NON_ROOT_PASSWORD}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;volumes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;db_storage:/var/lib/postgresql/data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;healthcheck&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;CMD-SHELL&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;interval&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;timeout&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;retries&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;start_period&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;docker.n8n.io/n8nio/n8n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;container_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unless-stopped&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;networks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;backend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cloudflare&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;environment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_TYPE&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;postgresdb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_POSTGRESDB_HOST&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;postgres&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_POSTGRESDB_PORT&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5432&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_POSTGRESDB_DATABASE&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${POSTGRES_DB}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_POSTGRESDB_USER&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${POSTGRES_NON_ROOT_USER}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_POSTGRESDB_PASSWORD&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${POSTGRES_NON_ROOT_PASSWORD}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;EXECUTIONS_MODE&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;regular&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;N8N_HOST&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${PUBLIC_DOMAIN}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;N8N_PROTOCOL&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;WEBHOOK_URL&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://${PUBLIC_DOMAIN}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;GENERIC_TIMEZONE&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${GENERIC_TIMEZONE}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;TZ&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${GENERIC_TIMEZONE}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;N8N_ENCRYPTION_KEY&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${N8N_ENCRYPTION_KEY}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;volumes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n_data:/home/node/.n8n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./local-files:/files&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;depends_on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;postgres&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;condition&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service_healthy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;healthcheck&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;CMD-SHELL&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;wget --spider -q http://localhost:5678/healthz || exit 1&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;interval&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;timeout&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;retries&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;start_period&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;60s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cloudflared&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cloudflare/cloudflared:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;container_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cloudflared&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unless-stopped&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;networks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cloudflare&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;depends_on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Bringing up the n8n containers&lt;/h2&gt;
&lt;p&gt;We now have everything in place to bring up the containers, to do this run the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Bringing up the containers&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/docker-compose.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will pull the images for the containers, next we can bring them up:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Bringing up the containers&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/docker-compose.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This should give you the following output:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Bringing up the containers&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; Running 5/5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;✔&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n_n8n_data&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;Created&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;0.1s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;✔&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n_db_storage&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;Created&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;0.0s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;✔&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n-postgres-1&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Healthy&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;5.8s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;✔&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;Started&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;5.9s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;✔&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cloudflared&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;Started&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;6.0s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once the containers are up and running we can check the logs to ensure everything is working correctly:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Checking the logs&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/docker-compose.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;logs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You should see something like the following:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Logs&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;license SDK&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Skipping&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renewal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;license&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initialized&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Data table&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;The&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;experimental&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subject&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Any&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;added&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;before&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;official&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;may&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;become&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;inaccessible&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;at&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;any&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;point.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;at&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;your&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;own&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;risk.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Version:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1.113.3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Editor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;accessible&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;via:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;n8n&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://n8n.wibble.foo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you open your browser and navigate your domain, for example &lt;strong&gt;&lt;span&gt;&lt;a href=&quot;https://n8n.wibble.foo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://n8n.wibble.foo&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt; you should see the n8n editor, if you click on the &lt;strong&gt;Sign in&lt;/strong&gt; button you should be taken to the login screen, enter the credentials you set up in the .env file and you should be logged in.&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/n8n/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-10-04-install-n8n-locally-using-cloudflare/n8n/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/n8n/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-10-04-install-n8n-locally-using-cloudflare/n8n/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/n8n/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-10-04-install-n8n-locally-using-cloudflare/n8n/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/n8n/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-10-04-install-n8n-locally-using-cloudflare/n8n/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That’s it, you now have a fully working n8n instance running locally and accessible via Cloudflare Tunnel. Install the free license, grab a coffee and get started with n8n - but thats another post.&lt;/p&gt;
&lt;h2&gt;Housekeeping&lt;/h2&gt;
&lt;p&gt;If you need to update the n8n instance you can do so by running the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Updating the n8n instance&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/docker-compose.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;down&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/docker-compose.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/docker-compose.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will stop the current instance, pull the latest images and bring the containers back up.&lt;/p&gt;
&lt;p&gt;If you need to remove the instance you can do so by running the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Removing the n8n instance&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/n8n/docker-compose.yml&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;down&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will remove the containers and networks, to remove the volumes you can run the following command:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Run the command below with caution, it will remove all of the data and configuration for the n8n instance.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Removing the volumes&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;volume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n_db_storage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;n8n_n8n_data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Making it a little more secure&lt;/h2&gt;
&lt;p&gt;Given that we are running the n8n instance on a local resource, and the tunnel is exposing it to the internet, we can make it a little more secure by fronting the site with a &lt;a href=&quot;/glossary/cloudflare-zero-trust/&quot;&gt;Cloudflare Zero Trust&lt;/a&gt; login. This will prevent anyone from accessing the n8n instance without being validated via Cloudflare.&lt;/p&gt;
&lt;p&gt;To do this go to the Cloudflare dashboard and navigate to &lt;strong&gt;Zero Trust&lt;/strong&gt; &amp;gt; &lt;strong&gt;Access&lt;/strong&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt; &amp;gt; &lt;strong&gt;Add an Application&lt;/strong&gt;; the first screen will ask you to select the application type, select &lt;strong&gt;Self hosted&lt;/strong&gt; and click on &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/01.png&quot; alt=&quot;Create Application&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2100&quot; height=&quot;1780&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Next give your application a name, in the screens below I have called mine &lt;strong&gt;n8nBlogPost&lt;/strong&gt;, then in the public hostname section enter the URL of your n8n instance, in the screens below I have entered &lt;strong&gt;n8n.wibble.foo&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/02.png&quot; alt=&quot;Configure Application&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2218&quot; height=&quot;1780&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now we need to add a policy to the application, click on &lt;strong&gt;+ Create new policy&lt;/strong&gt; where you will be taken to the &lt;strong&gt;Add policy&lt;/strong&gt; screen, here enter a name for the policy, I have called mine &lt;strong&gt;n8nBlogPostPolicy&lt;/strong&gt;. Then scroll down to the &lt;strong&gt;Add rules&lt;/strong&gt; section, by default there will an Include rule waiting to be added, in the &lt;strong&gt;Selector&lt;/strong&gt; select &lt;strong&gt;Emails ending in&lt;/strong&gt; and enter the domain name of your email provider, in my example I have entered &lt;strong&gt;wibble.email&lt;/strong&gt;. Now click on &lt;strong&gt;+ Add require&lt;/strong&gt;, in the &lt;strong&gt;Selector&lt;/strong&gt; this time select &lt;strong&gt;Country&lt;/strong&gt; and enter the country(s) you want to allow access from, in my example I have entered &lt;strong&gt;United Kingdom&lt;/strong&gt; and click on &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/03.png&quot; alt=&quot;Add a policy&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2218&quot; height=&quot;1780&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This will create a policy that will allow access to the n8n instance from the United Kingdom for users with an email ending in &lt;strong&gt;wibble.email&lt;/strong&gt;. Click on &lt;strong&gt;Save&lt;/strong&gt; and return to the tab where you are configuring the application, here click on &lt;strong&gt;Select existing policy&lt;/strong&gt; and select the policy you just created.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/04.png&quot; alt=&quot;Select a policy&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2218&quot; height=&quot;1780&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Under &lt;strong&gt;Login methods&lt;/strong&gt; select &lt;strong&gt;Accept all available identity providers&lt;/strong&gt; and click on &lt;strong&gt;Next&lt;/strong&gt;. The next two screens are both optional and can be left at their defaults. Finally, click on &lt;strong&gt;Save&lt;/strong&gt; and you will be taken to the &lt;strong&gt;Application&lt;/strong&gt; screen where you will see your new application listed.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/05.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/05.png&quot; alt=&quot;Applications&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2218&quot; height=&quot;1780&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once configured and goto your n8n domain you should be prompted to login with Cloudflare, once you have done this you will be taken to the n8n editor:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/06.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/06.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/07.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/07.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/08.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/08.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you were to try and access the n8n instance from outside the United Kingdom you would be denied access:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/09.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-10-04-install-n8n-locally-using-cloudflare/zerotrust/09.png&quot; alt=&quot;Denied !!!&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2148&quot; height=&quot;1760&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Same for not using the correct email ending in &lt;strong&gt;wibble.email&lt;/strong&gt;. The advantage of this is that unless you meet the criteria you will not be able to access the n8n instance; which means no traffic to your local machine from random people or bots on the internet.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this post, we explored how to install n8n on a local resource using Docker and route it via Cloudflare. We saw how easy it is to setup a local n8n instance and how to use Cloudflare Tunnel to route the traffic to it.&lt;/p&gt;
&lt;p&gt;Here are the links to n8n and the repository we downloaded the files from:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://n8n.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;n8n.io - AI workflow automation platform&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;n8n is a workflow automation platform that uniquely combines AI capabilities with business process automation, giving technical teams the flexibility of code with the speed of no-code.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;n8n.io&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/n8n.io-265cc2a18fbe.jpg&quot; alt=&quot;n8n.io - AI workflow automation platform&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.cloudflare.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;cloudflare.com&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;cloudflare.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.github.com/russmckendrick/local-n8n-with-cloudflare&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/local-n8n-with-cloudflare: n8n + Docker + Cloudflare&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;n8n + Docker + Cloudflare. Contribute to russmckendrick/local-n8n-with-cloudflare development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-8517b0b38a11.jpg&quot; alt=&quot;n8n + Docker + Cloudflare. Contribute to russmckendrick/local-n8n-with-cloudflare development by creating an account on GitHub.&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2025-10-04-install-n8n-locally-using-cloudflare/2025-10-04-install-n8n-locally-using-cloudflare.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-10-04-install-n8n-locally-using-cloudflare.CVq65nJ0.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/10/04/install-n8n-locally-using-cloudflare-og.png</blog:ogImage><blog:readingTime>11</blog:readingTime><category>AI</category><category>Automation</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Doom or Vibe Coding</title><link>https://www.russ.cloud/2025/05/26/doom-or-vibe-coding/</link><guid isPermaLink="true">https://www.russ.cloud//2025/05/26/doom-or-vibe-coding/</guid><description>A look at the exciting AI announcements from Google I/O, Microsoft Build, and Anthropic&apos;s Claude 4 launch, plus a new Doom game.</description><pubDate>Mon, 26 May 2025 10:29:35 GMT</pubDate><content:encoded>&lt;p&gt;This week was an interesting one in the world of AI - we had all of Google’s announcements at &lt;span&gt;&lt;a href=&quot;https://io.google/2025/explore/?focus_areas=AI&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google I/O&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, some announcements from Microsoft at their &lt;span&gt;&lt;a href=&quot;https://news.microsoft.com/build-2025-book-of-news/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Build&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; conference, and the most exciting of all - the launch of &lt;span&gt;&lt;a href=&quot;https://www.anthropic.com/news/claude-4&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Claude 4&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; from Anthropic; and to top it off, there is a new &lt;span&gt;&lt;a href=&quot;https://doom.bethesda.net/en-US/the-dark-ages&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Doom&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; game out!!!&lt;/p&gt;
&lt;h2&gt;Gaming or coding&lt;/h2&gt;
&lt;p&gt;While I am not much of a gamer, game releases like Doom prick up my ears - as most of you will know I am a macOS user, so Doom isn’t out for that. This is one of the reasons why I subscribe to Nvidia’s &lt;span&gt;&lt;a href=&quot;https://geforcenow.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GeForce Now&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; service, so I started playing Doom: The Dark Ages, but, I do like puzzles, so when Anthropic announced Claude 4 I decided to park Doom and give it a go.&lt;/p&gt;
&lt;div class=&quot;w-full aspect-video&quot;&gt;&lt;a href=&quot;https://youtube.com/watch?v=oqUclC3gqKs&quot;&gt;&lt;span&gt;Play&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;RussTools&lt;/h2&gt;
&lt;p&gt;A few weeks ago I used &lt;span&gt;&lt;a href=&quot;https://cursor.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cursor&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to create a single-page app to help with my day job. The first of the two tools was a &lt;span&gt;&lt;a href=&quot;https://www.russ.tools/network-designer/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;network design tool&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, the second was an &lt;span&gt;&lt;a href=&quot;https://www.russ.tools/azure-naming&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Azure Resource Naming tool&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. I decided to make use of one of the nearly fifty domains I’ve accumulated since 2001 and stuck them on &lt;span&gt;&lt;a href=&quot;https://russ.tools&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;russ.tools&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. To create these tools I have used a combination of Claude 3.5 Sonnet and Google’s Gemini 2.5 Pro, and while the two tools were functional, they were very obviously created by two different models - there wasn’t much consistency, both style-wise and with the code itself.&lt;/p&gt;
&lt;h3&gt;Finishing off some existing code&lt;/h3&gt;
&lt;p&gt;After the announcement of Claude 4 and after seeing a few posts about its code improvements, I decided to use it to finish off the third tool, which was a &lt;span&gt;&lt;a href=&quot;https://www.russ.tools/cron/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cron Job Expression Builder/Validator&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. While the code I already had in place was mostly non-functional and full of placeholders, this was enough for Claude 4 Sonnet to pick up where Gemini left off and finish the tool off with a single prompt; not only that, it rebuilt the interface to be more user-friendly and added a few extra features without me asking it to do anything!&lt;/p&gt;
&lt;h3&gt;More tools&lt;/h3&gt;
&lt;p&gt;Colour me impressed - so with a long weekend I decided to add some more tools: first an &lt;span&gt;&lt;a href=&quot;https://www.russ.tools/ssl-checker/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;SSL Certificate Checker&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, then a &lt;span&gt;&lt;a href=&quot;https://www.russ.tools/dns-lookup/www.russ.tools&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;DNS Lookup Tool&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and then a &lt;span&gt;&lt;a href=&quot;https://www.russ.tools/whois-lookup/104.21.112.1&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;WHOIS Lookup Tool&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h3&gt;What is missing&lt;/h3&gt;
&lt;p&gt;At this point, I had quite a good little collection of tools. I fed Claude the &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/russ-tools/blob/main/README.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;README&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; file and asked it what other tools would complement the existing ones, and we came up with …&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.tools/data-converter&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Data Format Converter&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;; Convert between &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt;, &lt;a href=&quot;/glossary/yaml/&quot;&gt;YAML&lt;/a&gt;, and TOML formats with validation and formatting&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.tools/base64/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Base64 Encoder/Decoder&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;; Encode and decode text and files using Base64 encoding with multiple variants&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.tools/jwt/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;JWT Decoder/Validator&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;; Decode JWT tokens and validate signatures without sending to external services&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Bringing it all together&lt;/h3&gt;
&lt;p&gt;While everything was pretty consistent, there were a few design issues. Not a problem, though - I asked Claude to review screenshots of each of the tools and &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/russ-tools/blob/main/src/components/tools/STYLE_GUIDE.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;write then implement a style guide&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; giving them a consistent look and feel across the site.&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/001.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/001.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/002.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/002.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/003.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/003.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/004.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/004.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/005.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/005.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/006.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/006.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/007.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/007.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/008.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/008.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-05-26-doom-or-vibe-coding/images/tools/009.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-05-26-doom-or-vibe-coding/images/tools/009.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Why?&lt;/h2&gt;
&lt;p&gt;While the new Doom game is fun, I actually find puzzles more interesting. Not being a developer by trade, the whole &lt;a href=&quot;/glossary/vibe-coding/&quot;&gt;vibe coding&lt;/a&gt; trend is quite a lot of fun, and I find it a lot more relaxing than Doom - errors are interesting to debug, and finding the right prompt is very satisfying when you get it right first time.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;While the new Doom game offers its own kind of thrill, I found myself drawn to the intellectual puzzles of “vibe coding” with these cutting-edge AI tools. I’ve been building out my ‘RussTools’ collection, a series of single-page apps for my day job, using a mix of AI models. It’s been an interesting journey, especially seeing the inconsistencies when different models were involved. However, Claude 4 truly impressed me, not only finishing off a tricky bit of code with a single prompt but also intuitively improving the UI and adding features I hadn’t even asked for!&lt;/p&gt;
&lt;p&gt;This success spurred me on to create even more tools, like an SSL Certificate Checker and &lt;a href=&quot;/glossary/dns/&quot;&gt;DNS&lt;/a&gt;/WHOIS lookup tools. The final touch was having Claude help implement a consistent style guide across the entire site, bringing everything together beautifully. Ultimately, for me, this ‘vibe coding’ isn’t just about building; it’s about the satisfying challenge of debugging errors and crafting the perfect prompt – a far more relaxing and engaging pursuit than the chaos of Doom!&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2025-05-26-doom-or-vibe-coding/2025-05-26-doom-or-vibe-coding.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-05-26-doom-or-vibe-coding.K8oZYQCv.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/05/26/doom-or-vibe-coding-og.png</blog:ogImage><blog:readingTime>5</blog:readingTime><category>AI</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Personal Project Updates and AI Editors</title><link>https://www.russ.cloud/2025/01/12/personal-project-updates-and-ai-editors/</link><guid isPermaLink="true">https://www.russ.cloud//2025/01/12/personal-project-updates-and-ai-editors/</guid><description>About that time I wrote and published an App to the Apple App Store without knowing how to code</description><pubDate>Sun, 12 Jan 2025 11:16:46 GMT</pubDate><content:encoded>&lt;p&gt;Over the holidays, I decided to revisit &lt;a href=&quot;/2024/10/28/vinyl-scrobbling-macos-app/&quot;&gt;a personal project I worked on in October&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Vinyl Scrobbler Recap&lt;/h2&gt;
&lt;p&gt;The project was a very basic &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; script prompted using AI. The application sits in the macOS menu bar and allows you to enter a &lt;span&gt;&lt;a href=&quot;http://discogs.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Discogs&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; release ID to scrobble the record you are listening to in real-time to &lt;span&gt;&lt;a href=&quot;http://last.fm&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;LastFM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The reason for the application was not only to dip my toes into the then newly released &lt;span&gt;&lt;a href=&quot;https://www.anthropic.com/news/3-5-models-and-computer-use&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Claude 3.5 Sonnet model&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; but also to have the artwork for the album I am listening to show up on my &lt;span&gt;&lt;a href=&quot;http://tuneshine.rocks&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tuneshine&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, as existing services didn’t quite have the functionality needed for it to work.&lt;/p&gt;
&lt;p&gt;The end of the post came with this warning:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Build and use this app at your own risk. It was developed by AI, and I am probably the only one who will ever want to use it. 😊&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;But that didn’t stop Tobias Butler, the creator of Tuneshine, from posting about it:&lt;/p&gt;
&lt;blockquote&gt;&lt;a href=&quot;https://www.instagram.com/reel/DCzAQBuSGUt/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;View this post on Instagram&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;I fixed a few annoying issues and tidied up the code a little over November and December. I was happy using it in the background as a personal project.&lt;/p&gt;
&lt;h2&gt;Enter AI-Powered Code Editors&lt;/h2&gt;
&lt;p&gt;I started talking with a colleague about AI code editors before the Christmas break. Since I had some free time, I decided to take a look at two of them.&lt;/p&gt;
&lt;h3&gt;Windsurf and a Test Project&lt;/h3&gt;
&lt;p&gt;The first editor I looked at was &lt;span&gt;&lt;a href=&quot;https://codeium.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Windsurf by Codeium&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. My initial idea, rather than touching the Vinyl Scrobbler app, was to create a Spotify Wrapped-style end-of-year report on the albums I had purchased. As I catalog everything I buy using my &lt;span&gt;&lt;a href=&quot;https://www.discogs.com/user/russmck/collection&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Discogs account&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, which is then indexed on &lt;span&gt;&lt;a href=&quot;http://russ.fm/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;my own website&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, I had all the data I needed to create something.&lt;/p&gt;
&lt;p&gt;I first ended up asking Windsurf to create a set of Python scripts, as I am getting used to working with and debugging Python code. While that was great for fetching data from Discogs, I quickly hit limitations when it came to creating the HTML for the reports. Rather than spending too much time trying to fix it, I scrapped the code and started again. This time, I used Python to generate &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; for each year and then asked Windsurf to create a single-page web app.&lt;/p&gt;
&lt;p&gt;It suggested creating a Vue 3-powered SPA that read the JSON files and created the site, which is hosted at:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.russ.fm/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Russ.fm - Personal Record Collection&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Explore my personal vinyl and music collection. Browse albums, artists, and genres from my curated record collection.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;russ.fm&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/russ.fm-303fece5864f.jpg&quot; alt=&quot;Russ.fm - Personal Record Collection&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;… As you can see, it eventually did a good job. I say “eventually” because while it worked, I got stuck in a few loops, which annoyingly ate into my prompt allowance (more on that later).&lt;/p&gt;
&lt;p&gt;The full code and commit history can be found at &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/unwrapped&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/russmckendrick/unwrapped&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;This gave me the confidence to try something a little more challenging.&lt;/p&gt;
&lt;h2&gt;Revisiting Vinyl Scrobbler&lt;/h2&gt;
&lt;p&gt;The code I have, which produces the website at &lt;span&gt;&lt;a href=&quot;https://www.russ.fm&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.russ.fm/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, interacts with the Apple Music &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; to get album covers and the links to embed the Apple Music player on the page.&lt;/p&gt;
&lt;p&gt;Because accessing the Apple Music API requires a developer key, I had to sign up to be an Apple Developer. This meant that if I wanted to, I could properly sign the Vinyl Scrobbler application so that it didn’t warn the user that the application is not trusted when they first opened it.&lt;/p&gt;
&lt;p&gt;This then got me thinking: maybe I could use Windsurf to port the Python code to Swift and make the application a fully native macOS app.&lt;/p&gt;
&lt;h3&gt;Windsurf + Vinyl Scrobbler&lt;/h3&gt;
&lt;p&gt;With Xcode installed and a new project created, I dropped a copy of the Python script into the project and asked Windsurf to reproduce the functionality in Swift. To my surprise, after a little back and forth about build errors, I had something that pretty much recreated the original Python script in Swift. It didn’t work initially because it appeared unable to reach the necessary APIs. However, it successfully built and launched an application that visually resembled the Python version.&lt;/p&gt;
&lt;p&gt;Now that I had a base to build on, I could start adding functionality back in. This went as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sorting out permissions&lt;/strong&gt;: By default, the application was being blocked from accessing the internet, which made interacting with the Discogs and LastFM APIs a little troublesome. :rofl:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clearing up some misunderstandings&lt;/strong&gt;: The original Python code was documented, but the documentation mostly dealt with what to do with each function rather than why it was there. This meant I had to fix some of the logic related to how it was scrobbling tracks to Last.FM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build errors&lt;/strong&gt;: With each change came build errors, most of which were simple to fix. However, I did find that some fixes broke previously working functionality, which resulted in quite a bit of back and forth.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now I had a working application, so I decided it was time to add some functionality. This was partly inspired by my experience using AI-powered tools like Windsurf for other projects and my curiosity about how far I could take this app with similar assistance.&lt;/p&gt;
&lt;p&gt;The first new functionality I tried to add was a player window, and this is where I became a little unstuck with Windsurf.&lt;/p&gt;
&lt;p&gt;While prompting the functionality, things started off great, but I soon found myself fighting Windsurf due to technical issues and limitations. Specifically, it started overcomplicating the code when making small changes and, worse, removed large chunks of existing working code for no apparent reason. as it started over complicating code when making small changes and worse, it removed large chunks of existing working code for no apparent reason. Normally, when an &lt;a href=&quot;/glossary/large-language-model/&quot;&gt;LLM&lt;/a&gt; starts to do that,t I start new chats with a detailed prompt; in this case, I used:&lt;/p&gt;
&lt;div class=&quot;chat-message flex gap-2.5 my-4 &quot;&gt;&lt;div class=&quot;avatar w-10 h-10 rounded-full flex-shrink-0 overflow-hidden flex items-center justify-center self-start&quot;&gt;&lt;img src=&quot;https://www.russ.social/sticker-clear.svg&quot; alt=&quot;Avatar&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;flex flex-col gap-1 max-w-[70%]&quot;&gt;&lt;div class=&quot;message-bubble relative px-3 py-2 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-tr-xl rounded-br-xl rounded-bl-xl&quot;&gt;&lt;div class=&quot;chat-tail absolute top-0 chat-tail-left -left-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;message-text text-xs&quot;&gt;&lt;p&gt;This Swift app is building and running great, but would like to add more functionality.&lt;/p&gt;&lt;p&gt;The files we are working with are as follows …&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;ConfigurationManager.swift = Loads in the configuration file from ~/.vinyl-scrobbler-config.json and sets everything up&lt;/li&gt;
&lt;li&gt;AppDelegate.swift = The bulk of the main code&lt;/li&gt;
&lt;li&gt;DiscogsService.swift = The Discogs part of the application&lt;/li&gt;
&lt;li&gt;LastFMService.swift = The LastFM parts of the application&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Please concentrate on only adding this functionality, which is detailed below …&lt;/p&gt;&lt;p&gt;&amp;lt; rest of the prompt here &amp;gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While this approach normally works when dealing with Claude directly I was still having problems with Windsurf - at this stage I decided to look at one of Windsurf’s competitors, Cursor, as I had used pretty much all of the tokens I had paid for and felt like I was going around in circles.&lt;/p&gt;
&lt;h3&gt;A new challenger enters the ring, Cursor.&lt;/h3&gt;
&lt;p&gt;I had used &lt;span&gt;&lt;a href=&quot;https://www.cursor.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cursor&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; when it was first launched, and I am not entirely sure why, but I didn’t get on with the interface. It felt like having a whole separate IDE when VS Code was fine seemed like overkill. Given my experience of Windsurf I decided to sign-up for the trial and give it a second chance to see if it could pick up from where Windsurf left off.&lt;/p&gt;
&lt;p&gt;Now given that both tools use Claude 3.5 Sonnet by default, the difference was night and day. The quality of the interactions felt a lot better, and within no time, I had a player window, with artwork and track listing within the application.&lt;/p&gt;

&lt;p&gt;Sure, it had problems when adding code that caused build errors-but these were nowhere near as dramatic as the ones Windsurf caused in that it was a problem with the new code that was added rather than the tool’s decision to remove entire functions without explanation or reason.&lt;/p&gt;
&lt;p&gt;99% of the time, these errors were resolved by passing the line number and the error itself, and if Xcode suggested a fix, I passed that along, too. This gave me the confidence to add more functionality.&lt;/p&gt;
&lt;h4&gt;Last.FM authentication&lt;/h4&gt;
&lt;p&gt;One of the things which I had been doing, since it was only really me using the application was putting my Last.FM credentials in a text file, it was very hacky, but functional. If I was to make a little more user friendly I had to get the end users to properly authenticate against using their own Last.FM credentials. After a little looking around I found ScrobbleKit, it would handle both the authentication as well as scrobbling the tracks to Last.FM:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/tfmart/ScrobbleKit&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;&lt;img src=&quot;/assets/link-previews/github.com-0297ec1b7f39-favicon.svg&quot; alt=&quot;&quot; width=&quot;20&quot; height=&quot;20&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;GitHub - tfmart/ScrobbleKit: A Swift library for interacting with the Last.fm API on Apple platforms and Linux&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A Swift library for interacting with the Last.fm API on Apple platforms and Linux - tfmart/ScrobbleKit&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;With a few prompts and a little back and forth with Cursor and a little Googling I had removed my method of handling the authentication and was using ScrobbleKit to sign into Last.FM and saving the credntials to the Key Chain so I could use them each time the application launched. I had also switched over to using ScrobbleKit to send the actual scrobbles.&lt;/p&gt;

&lt;h4&gt;Searching Discogs&lt;/h4&gt;
&lt;p&gt;Up until now I was passing in the Discogs release ID to search for an album and load its details, this was good for me as I index my collection on &lt;span&gt;&lt;a href=&quot;https://www.russ.fm&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Russ.FM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and one of the features I added a while ago was a button which copies the Discogs ID to the clipboard, which you can see [here]&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/metropolis-pt-2-scenes-from-a-memory-32745087/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;(&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;Again, with a few prompts I had the application searching Discogs and presenting the results back to the user so that can select which release to Scrobble.&lt;/p&gt;

&lt;h4&gt;Time to submit to Apple&lt;/h4&gt;
&lt;p&gt;After a little bit of tidying up and removing some of the leftover debug code, I was ready to submit the application to Apple. The application was rejected a few times because I had not removed some of the &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; entitlements that had allowed the application to access the configuration file in the user’s folder, which was a good catch. There was also a redundant entitlement file that had been created at some point and was doubling up on some information.&lt;/p&gt;
&lt;p&gt;Once those issues were resolved, the application was accepted, and I published it; you can find it on the Mac App Store by clicking the banner below:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;a href=&quot;https://apps.apple.com/gb/app/vinyl-scrobbler/id6740146205?mt=12&amp;amp;itscg=30200&amp;amp;itsct=apps_box_badge&amp;amp;mttnsubad=6740146205&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2025-01-12-personal-project-updates-and-ai-editors/images/LandscapeBannerAd.png&quot; alt=&quot;Download on the App Store [noExternalIcon]&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1280&quot; height=&quot;720&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;So, with that, I have what was a simple app that I would only use, converted to native Swift, and published on the Mac Store. That’s not bad, considering I am not a developer by trade, nor had I ever used Swift or Xcode before starting this project.&lt;/p&gt;
&lt;h2&gt;Things I wish I knew before starting&lt;/h2&gt;
&lt;p&gt;There are a few things I should have been doing, but I didn’t and regretted not doing so.&lt;/p&gt;
&lt;h3&gt;Branch and commit often&lt;/h3&gt;
&lt;p&gt;Create a “feature” branch in &lt;a href=&quot;/glossary/git/&quot;&gt;Git&lt;/a&gt;, commit often and don’t be afraid to roll back.&lt;/p&gt;
&lt;h3&gt;Plan and feedback&lt;/h3&gt;
&lt;p&gt;Take baby steps, it is tempting to ask the LLM to just make one big feature change in one go. Don’t do this, instead explain your plan and ask the LLM to break it down into steps, here is the initial prompt I have used to implement an upcoming feature.&lt;/p&gt;
&lt;div class=&quot;chat-message flex gap-2.5 my-4 &quot;&gt;&lt;div class=&quot;avatar w-10 h-10 rounded-full flex-shrink-0 overflow-hidden flex items-center justify-center self-start&quot;&gt;&lt;img src=&quot;https://www.russ.social/sticker-clear.svg&quot; alt=&quot;Avatar&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;flex flex-col gap-1 max-w-[70%]&quot;&gt;&lt;div class=&quot;message-bubble relative px-3 py-2 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-tr-xl rounded-br-xl rounded-bl-xl&quot;&gt;&lt;div class=&quot;chat-tail absolute top-0 chat-tail-left -left-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;message-text text-xs&quot;&gt;&lt;p&gt;We are working on a feature branch. I want to add the option to use ShazamKit to listen to what the user is playing and then use the results to search Discogs. As ShazamKit only returns the Artist and track title, we need to think carefully about how to pass this information onto Discogs. Let’s use the LastFM API to get more information on the track using track.getInfo and then the album information from there to pass onto the Discogs search we already have using the Album details there.&lt;/p&gt;&lt;p&gt;The interface should be a new slider activated from a new “Listen” menu item below “Load Album.” As per the rest of the application, this new slider should be modern and animated, providing user feedback on what is happening. It should also, as always, be written and implemented using pure SwiftUI and Apple’s best practices regarding creating new Views, models, and, if requested, Services.&lt;/p&gt;&lt;p&gt;Please do not change any existing functionality.&lt;/p&gt;&lt;p&gt;How does this sound?&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;chat-message flex gap-2.5 my-4 flex-row-reverse&quot;&gt;&lt;div class=&quot;avatar w-10 h-10 rounded-full flex-shrink-0 overflow-hidden flex items-center justify-center self-start&quot;&gt;&lt;img src=&quot;/assets/2025-01-12-personal-project-updates-and-ai-editors/images/cursor.png&quot; alt=&quot;Avatar&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;flex flex-col gap-1 max-w-[70%]&quot;&gt;&lt;div class=&quot;message-bubble relative px-3 py-2 bg-blue-100 dark:bg-blue-900 text-gray-900 dark:text-gray-100 rounded-tl-xl rounded-bl-xl rounded-br-xl&quot;&gt;&lt;div class=&quot;chat-tail absolute top-0 chat-tail-right -right-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;message-text text-xs&quot;&gt;&lt;p&gt;Your approach sounds well thought out and aligns perfectly with macOS design patterns and SwiftUI best practices. Let me outline the plan and then we can implement it step by step:&lt;/p&gt;&lt;p&gt;Architecture Plan:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Create a ShazamService to handle audio recognition&lt;/li&gt;
&lt;li&gt;Add a new ListenView for the sliding panel&lt;/li&gt;
&lt;li&gt;Create a ListenViewModel to manage the state and business logic
4, Integrate with existing LastFMService and DiscogsService&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;The flow would be:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;User activates “Listen” from menu&lt;/li&gt;
&lt;li&gt;Sliding panel appears with animation&lt;/li&gt;
&lt;li&gt;ShazamKit starts listening&lt;/li&gt;
&lt;li&gt;On match:
&lt;ul&gt;
&lt;li&gt;Get track/artist from Shazam&lt;/li&gt;
&lt;li&gt;Use LastFM API to get album info&lt;/li&gt;
&lt;li&gt;Pass album info to Discogs search&lt;/li&gt;
&lt;li&gt;Show results in existing Discogs view&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Let me first check the existing codebase structure to ensure we maintain consistency.&lt;/p&gt;&lt;p&gt;&amp;lt; &lt;a href=&quot;/glossary/agent/&quot;&gt;agent&lt;/a&gt; action taken here &amp;gt;&lt;/p&gt;&lt;p&gt;Perfect. The project follows a clean architecture. Here’s what we’ll need to create:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;New Service:
&lt;ul&gt;
&lt;li&gt;ShazamService.swift - Handles ShazamKit integration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;New Views:
&lt;ul&gt;
&lt;li&gt;ListenView.swift - Main sliding panel view&lt;/li&gt;
&lt;li&gt;ListenButtonView.swift - Reusable button component for listening state&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;New ViewModel:
&lt;ul&gt;
&lt;li&gt;ListenViewModel.swift - Manages listening state and coordinates services&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Model updates:
&lt;ul&gt;
&lt;li&gt;ShazamMatch.swift - Model for Shazam results&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Before we start implementing, I should check the entitlements file to ensure we have microphone access:&lt;/p&gt;&lt;p&gt;&amp;lt; agent action taken here &amp;gt;&lt;/p&gt;&lt;p&gt;We’ll need to add microphone access entitlements. Would you like me to proceed with:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Adding the necessary entitlements for microphone access and ShazamKit&lt;/li&gt;
&lt;li&gt;Creating the ShazamService with basic recognition functionality&lt;/li&gt;
&lt;li&gt;Implementing the ListenView with a sliding panel design&lt;/li&gt;
&lt;li&gt;Integrating with existing LastFM and Discogs services&lt;/li&gt;
&lt;li&gt;The UI will follow macOS design patterns with:&lt;/li&gt;
&lt;li&gt;A subtle sliding panel from the right&lt;/li&gt;
&lt;li&gt;Clear visual feedback during listening&lt;/li&gt;
&lt;li&gt;SF Symbols for status indicators&lt;/li&gt;
&lt;li&gt;Smooth animations for state transitions&lt;/li&gt;
&lt;li&gt;Proper error handling and user feedback&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Does this align with your vision? I can start with any of these components first, but I recommend beginning with the entitlements and ShazamService as they’re foundational to the feature.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A preview of this (and the new GUI) can been seen below:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-01-12-personal-project-updates-and-ai-editors/images/shazam/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-01-12-personal-project-updates-and-ai-editors/images/shazam/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-01-12-personal-project-updates-and-ai-editors/images/shazam/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-01-12-personal-project-updates-and-ai-editors/images/shazam/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2025-01-12-personal-project-updates-and-ai-editors/images/shazam/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2025-01-12-personal-project-updates-and-ai-editors/images/shazam/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Going around in circles&lt;/h3&gt;
&lt;p&gt;Sometimes, you will find yourself going around in a loop. You will ask to fix an error, and the LLM will try to cause another problem. You manage to prompt a fix for the new issue, but all that does is recreate the original problem, and your prompt to fix that recreates the new problem you just fixed.&lt;/p&gt;
&lt;p&gt;Ask the LLM this one simple thing:&lt;/p&gt;
&lt;div class=&quot;chat-message flex gap-2.5 my-4 &quot;&gt;&lt;div class=&quot;avatar w-10 h-10 rounded-full flex-shrink-0 overflow-hidden flex items-center justify-center self-start&quot;&gt;&lt;img src=&quot;https://www.russ.social/sticker-clear.svg&quot; alt=&quot;Avatar&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;flex flex-col gap-1 max-w-[70%]&quot;&gt;&lt;div class=&quot;message-bubble relative px-3 py-2 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-tr-xl rounded-br-xl rounded-bl-xl&quot;&gt;&lt;div class=&quot;chat-tail absolute top-0 chat-tail-left -left-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;message-text text-xs&quot;&gt;&lt;p&gt;Are we using the simplest approach and best practices for the scale of our project structure?&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A lot of LLMs like Clade 3.5 and GPT-4o love to overcomplicate their solutions and sometimes need to be told to calm things down and take a simpler approach.&lt;/p&gt;
&lt;h3&gt;Forgetfulness&lt;/h3&gt;
&lt;p&gt;At some point, the LLM will behave like it has forgotten what it is doing. At the first sign of this, start a new session and give the LLM a prompt to refresh its context of where you are in the task. To do this, you can use the plan it originally gave. Just let us know what steps it has done and which step you would like it to work on next. Ensure that you tell it to do it one step at a time.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This blog post chronicles my journey of revisiting the Vinyl Scrobbler app, initially written in Python, and transforming it into a fully native macOS app using Swift. Using AI-assisted development tools like Windsurf and Cursor, I explored new functionality, tackled technical challenges, and learned valuable lessons about working with AI-driven tools. Finally, the project culminated in the successful publication of Vinyl Scrobbler on the Mac App Store, which was fun considering my lack of prior experience with Swift or Xcode. The lessons learned during this process apply to anyone venturing into AI-assisted app development or embarking on similar projects.&lt;/p&gt;
&lt;h2&gt;Audio Summary&lt;/h2&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2025-01-12-personal-project-updates-and-ai-editors/2025-01-12-personal-project-updates-and-ai-editors.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2025-01-12-personal-project-updates-and-ai-editors.-L2yfTCr.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2025/01/12/personal-project-updates-and-ai-editors-og.png</blog:ogImage><blog:readingTime>14</blog:readingTime><category>macos</category><category>ai</category><category>code</category><category>vinyl</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>My 2024 Top 20(ish)</title><link>https://www.russ.cloud/2024/12/27/my-2024-top-20ish/</link><guid isPermaLink="true">https://www.russ.cloud//2024/12/27/my-2024-top-20ish/</guid><description>A curated collection of my favorite album releases from 2024, featuring everything from progressive rock to electronic music, showcasing both veteran artists and exciting new releases.</description><pubDate>Fri, 27 Dec 2024 10:03:22 GMT</pubDate><content:encoded>&lt;p&gt;This year has been particularly notable for the long-awaited return of Tears For Fears with “Songs For A Nervous Planet” to The Cure’s “Songs Of A Lost World,” we’ve seen legendary artists continuing to push boundaries.&lt;/p&gt;
&lt;p&gt;The progressive rock scene has been especially vibrant with strong releases from Frost*, Pure Reason Revolution, and The Pineapple Thief. What’s particularly striking about 2024’s releases is the diversity of sounds - from Underworld’s electronic landscapes in “Strawberry Hotel” to Orville Peck’s unique take on country music with “Stampede.” The year has demonstrated that established artists can still innovate while staying true to their core sound.&lt;/p&gt;
&lt;p&gt;If you’re interested in exploring my complete vinyl collection and purchases throughout 2024, don’t forget to check out my detailed tracking at this link &lt;span&gt;&lt;a href=&quot;https://www.russ.fm/wrapped/2024&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.russ.fm/wrapped/2024&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;In no particular order, here are what I dug in 2024 …&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-01/01.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-01/01.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-01/02.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-01/02.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-01/03.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-01/03.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-02/04.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-02/04.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-02/05.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-02/05.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-02/06.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-02/06.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/songs-for-a-nervous-planet-32086020/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tears For Fears - Songs For A Nervous Planet&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/life-in-the-wires-32056107/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Frost* - Life In The Wires&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/gravity-stairs-30830307/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Crowded House - Gravity Stairs&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/the-wolf-changes-its-fur-but-not-its-nature-horrific-honorifics-number-two-32439303/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Crippled Black Phoenix - The Wolf Changes Its Fur But Not Its Nature&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/songs-of-a-lost-world-32149125/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Cure - Songs Of A Lost World&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/coming-up-to-consciousness-31683545/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Pure Reason Revolution - Coming Up To Consciousness&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-03/07.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-03/07.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-03/08.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-03/08.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-03/09.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-03/09.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-04/09.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-04/09.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-04/10.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-04/10.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-04/11.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-04/11.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/ensoulment-31668308/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The The - Ensoulment&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/luck-and-strange-31657538/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;David Gilmour - Luck And Strange&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/it-leads-to-this-29741239/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Pineapple Thief - It Leads To This&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/strawberry-hotel-32093847/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Underworld - Strawberry Hotel&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/as-it-ever-was-so-it-will-be-again-30976879/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Decemberists - As It Ever Was, So It Will Be Again&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/all-now-30163826/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Staves - All Now&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-05/12.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-05/12.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-05/13.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-05/13.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-05/14.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-05/14.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-06/15.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-06/15.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-06/16.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-06/16.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-06/17.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-06/17.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/ocean-without-a-shore-31896559/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Weather Systems - Ocean Without A Shore&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/stampede-31383548/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Orville Peck - Stampede&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/album/im-totally-fine-with-it-dont-give-a-fuck-anymore-30624874/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Arab Strap - I’m Totally Fine With It 👍 Don’t Give A Fuck Anymore 👍&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/my-light-my-destroyer-31205095/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cassandra Jenkins - My Light, My Destroyer&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/small-changes-32384205/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Michael Kiwanuka - Small Changes&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/yummy-30375872/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;James - Yummy&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-07/18.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-07/18.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-07/19.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-07/19.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-07/20.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-07/20.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-08/21.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-08/21.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-08/22.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-08/22.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-12-27-top-20-2024/images/section-08/23.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-12-27-top-20-2024/images/section-08/23.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/all-born-screaming-30547978/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;St. Vincent - All Born Screaming&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/the-art-of-the-lie-30947866/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;John Grant - The Art Of The Lie&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/leon-31909705/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Leon Bridges - Leon&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/seti-29917387/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sedibus - Seti&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/blu-wav-29808619/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Grandaddy - Blu Wav&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/interplay-30233693/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ride - Interplay&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-12-27-top-20-2024.COKuyoXB.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/12/27/my-2024-top-20ish-og.png</blog:ogImage><blog:readingTime>3</blog:readingTime><category>Vinyl</category><category>Life</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Running Flux on macOS</title><link>https://www.russ.cloud/2024/11/24/running-flux-on-macos/</link><guid isPermaLink="true">https://www.russ.cloud//2024/11/24/running-flux-on-macos/</guid><description>“Learn how to install and use MFLUX to run FLUX.1 models on macOS. This guide explores generating high-quality AI images, comparing [Schnell] and [Dev] models, and enhancing outputs with LoRAs for custom styles.”</description><pubDate>Sun, 24 Nov 2024 14:16:40 GMT</pubDate><content:encoded>&lt;p&gt;When I last wrote about &lt;a href=&quot;/2024/08/11/unlocking-image-creation-with-flux-and-gpt-4o/&quot;&gt;FLUX.1 from Black Forest Labs&lt;/a&gt;, I mentioned, “however, it quickly became clear that my M3 MacBook Pro with its 36GB of RAM wasn’t going to cut it.” Since then, I’ve upgraded to an M4 MacBook Pro with 48GB of RAM. Alongside this, substantial improvements have been made to run &lt;a href=&quot;/glossary/flux/&quot;&gt;FLUX&lt;/a&gt;.1 smoothly on macOS machines via the &lt;span&gt;&lt;a href=&quot;https://github.com/filipstrand/mflux&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;MFLUX project&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;In this quick post, I’ll walk you through installing and using MFLUX. Let’s dive in!&lt;/p&gt;
&lt;h2&gt;Installing MFLUX&lt;/h2&gt;
&lt;p&gt;As I’ve shared over the past few months, I use &lt;a href=&quot;/2024/04/06/conda-for-python-environment-management-on-macos/&quot;&gt;Conda&lt;/a&gt; to &lt;a href=&quot;/2024/11/17/zsh-conda-environment-selector-function/&quot;&gt;manage&lt;/a&gt; &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; environments on my macOS machine. The first step is to create a new environment, switch to it, and install MFLUX:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing MFLUX&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda create -n mflux python=3.11&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda activate mflux&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip install mflux&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With that, MFLUX is installed, and we’re almost ready to generate our first image.&lt;/p&gt;
&lt;h2&gt;Generating images with FLUX.1 [schnell]&lt;/h2&gt;
&lt;p&gt;With MFLUX installed, the &lt;code&gt;mflux-generate&lt;/code&gt; command becomes available. On its first run, it will download the models and necessary files-over 30GB in total, so be prepared for a bit of a wait:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Something here&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mflux-generate \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--prompt &quot;Luxury food photograph&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--model schnell \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--steps 2 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--seed 2 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--height 1024 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--width 1024&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once done, you’ll receive an image like this:&lt;/p&gt;

&lt;h3&gt;Sample FLUX.1 [schnell] images&lt;/h3&gt;
&lt;p&gt;Here are more samples using FLUX.1 [Schnell], generated by altering only the prompts:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-24-running-flux-on-macos/images/schnell-samples/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-24-running-flux-on-macos/images/schnell-samples/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-24-running-flux-on-macos/images/schnell-samples/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-24-running-flux-on-macos/images/schnell-samples/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-24-running-flux-on-macos/images/schnell-samples/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-24-running-flux-on-macos/images/schnell-samples/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-24-running-flux-on-macos/images/schnell-samples/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-24-running-flux-on-macos/images/schnell-samples/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The prompts for each of the images are below:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Prompt #1&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Detailed cinematic dof render of an old detailed CRT monitor on a wooden desk in a bright room with items around, messy dirty room. On the screen are the letters “RUSS.CLOUD” glowing softly. High detail hard surface render.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Prompt #2&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Create a hyperrealistic image of a person sitting in a cozy coffee shop, working on a sleek laptop. The individual should be wearing casual but stylish attire, with a focused expression. Surround them with the warm ambiance of the cafe: a mix of modern and rustic decor, soft lighting from hanging Edison bulbs, and steam rising from a freshly brewed coffee cup beside their laptop. Through the window behind them, show a bustling city street with pedestrians and blurred car lights, suggesting an early evening setting. The laptop screen should display faint reflections of text or code, adding to the sense of productivity.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Prompt #3&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Create a highly detailed and dramatic image of a 1950s movie scene. The setting should be a dimly lit diner at night, with neon signs glowing faintly through rain-streaked windows. Center two characters: a woman in a vintage dress with perfectly styled hair, clutching a handkerchief as she looks distraught, and a man in a sharp suit with a loosened tie, leaning against the counter with a cigarette in hand, his expression a mix of regret and defiance. The tension between them should be palpable. Surround them with details like a jukebox softly glowing in the corner, a half-empty coffee cup on the counter, and a lone waitress in the background, watching the scene unfold with concern. Use dramatic chiaroscuro lighting to heighten the emotion and give the scene a cinematic noir feel.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Prompt #4&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Design a sleek, modern promotional image for the tech blog “russ.cloud.” Center the blog’s name in bold, futuristic typography, glowing with a soft neon effect. The background should feature a dynamic blend of cloud patterns and abstract digital elements, such as circuits, floating data streams, and holographic grids, to symbolize cutting-edge technology and cloud computing. Include subtle icons of tech concepts like servers, code snippets, and analytics charts seamlessly integrated into the background. Use a color palette of deep blues, vibrant purples, and glowing whites to convey innovation and sophistication.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Each image adhered closely to its prompt and rendered text beautifully, taking about 30 seconds to generate.&lt;/p&gt;
&lt;h2&gt;Generating images with FLUX.1 [dev]&lt;/h2&gt;
&lt;p&gt;Switching to FLUX.1 [Dev] only requires changing the &lt;code&gt;--model&lt;/code&gt; flag to &lt;code&gt;dev&lt;/code&gt;. However, this model requires login credentials for Hugging Face. Install the &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; and log in:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install the Hugging Face CLI and login &lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew install hugging face-cli&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;huggingface-cli login&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;After logging in, download the FLUX.1 [Dev] model (another 30GB). Running the same command with adjusted steps yields improved results.&lt;/p&gt;

&lt;p&gt;Oh, well that didn’t work as expected, what happened? Well, FLUX.1 [schnell] is optimized for speed and is capable of generating images in just a small number of steps, like the 2 we used. This makes it significantly faster than FLUX.1 [dev], which typically requires 20 steps or more for image generation. The trade-off for this speed is a somewhat lower image quality compared to FLUX.1 dev.&lt;/p&gt;
&lt;p&gt;Lets add some more steps:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Running with more steps&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mflux-generate \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--prompt &quot;Luxury food photograph&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--model dev \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--steps 20 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--seed 20 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--height 1024 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--width 1024&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;

&lt;p&gt;What difference did increase the number of steps to 20 have? Well it now took just over 5 minutes to generate then image, but as you can see the quality is a lot higher.&lt;/p&gt;
&lt;h3&gt;Sample FLUX.1 [dev] images&lt;/h3&gt;
&lt;p&gt;Here are some samples of the same prompts we used for FLUX.1 [schnell], but this time the number of steps was updated to 20 and also the so was the seed.&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-24-running-flux-on-macos/images/dev-samples/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-24-running-flux-on-macos/images/dev-samples/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-24-running-flux-on-macos/images/dev-samples/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-24-running-flux-on-macos/images/dev-samples/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-24-running-flux-on-macos/images/dev-samples/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-24-running-flux-on-macos/images/dev-samples/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-24-running-flux-on-macos/images/dev-samples/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-24-running-flux-on-macos/images/dev-samples/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I think this is one of the only times I have taxed my M4 (so far) that the fans have kicked in while running the command. I think that FLUX.1 [dev] did a way better job on the first three images than FLUX.1 [schnell], what do you think?&lt;/p&gt;
&lt;h2&gt;Adding LoRAs&lt;/h2&gt;
&lt;p&gt;Now we have FLUX.1 [dev] running we can also add LoRAs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LoRAs (Low-Rank Adaptations) for Flux are specialized fine-tuning modules that enhance the capabilities of the Flux image generation model. These adaptations allow users to customize Flux’s output for specific styles, concepts, or characters without retraining the entire model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the example below I am using to the following LoRA:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://huggingface.co/Purz/choose-your-own-adventure&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Purz/choose-your-own-adventure · Hugging Face&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;We’re on a journey to advance and democratize artificial intelligence through open source and open science.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;huggingface.co&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/huggingface.co-17d41308de93.jpg&quot; alt=&quot;Purz/choose-your-own-adventure · Hugging Face&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;All you need to do it download &lt;code&gt;purz-cy04-choose_your_own_adventure.safetensors&lt;/code&gt; file and then add the path to your command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Something here&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mflux-generate \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--prompt &apos;cy04, a book titled &quot;Paths of the Vale&quot;. A breathtaking fantasy scene unfolds on the illustrated cover. A lone adventurer clad in a flowing cloak stands at a crossroads surrounded by glowing ethereal pathways that twist and ascend into a star-streaked sky. Each path is lined with unique details: one shimmering with golden light and blooming flowers, another cloaked in shadow with ominous ruins, and a third sparkling with icy frost and towering crystalline spires. In the distance, a mysterious, floating vale hovers, its surface glowing with pulsating, magical runes. The adventurer holds an ancient, glowing compass, its needle spinning wildly, reflecting the uncertainty of the journey ahead.&apos; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--model dev \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--steps 30 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--seed 20 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--height 1024 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--width 720 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--lora-paths purz-cy04-choose_your_own_adventure.safetensors&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;In this case the LoRA is referenced using &lt;code&gt;cy04&lt;/code&gt; in your prompt, this gave me the following:&lt;/p&gt;

&lt;p&gt;As you can see from the prompt, I used a few more steps for this one and adjusted the size to make it more book shaped - the other over you may have noticed is that image above is a little larger than 1024x720. To upscale the image is used &lt;span&gt;&lt;a href=&quot;https://upscayl.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Upscayl&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this post, I’ve explored installing and running FLUX.1 with MFLUX on macOS, demonstrated the differences between the FLUX.1 [Schnell] and [Dev] models, and showcased how LoRAs enhance customization. Whether you’re focused on speed or quality, these tools deliver impressive results on macOS.&lt;/p&gt;
&lt;p&gt;For more information on MFLUX see the following &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; repo:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/filipstrand/mflux&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;&lt;img src=&quot;/assets/link-previews/github.com-a9502466a163-favicon.svg&quot; alt=&quot;&quot; width=&quot;20&quot; height=&quot;20&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;GitHub - filipstrand/mflux: MLX native implementations of state-of-the-art generative image models&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;MLX native implementations of state-of-the-art generative image models - filipstrand/mflux&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-11-24-running-flux-on-macos.3K5vPSWS.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/11/24/running-flux-on-macos-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>macos</category><category>ai</category><category>python</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Zsh Conda Environment Selector Function</title><link>https://www.russ.cloud/2024/11/17/zsh-conda-environment-selector-function/</link><guid isPermaLink="true">https://www.russ.cloud//2024/11/17/zsh-conda-environment-selector-function/</guid><description>Streamline your Python workflow on macOS with a custom Zsh function for quickly selecting and activating Conda environments. Simplify environment management with this interactive and efficient solution!</description><pubDate>Sun, 17 Nov 2024 12:43:53 GMT</pubDate><content:encoded>&lt;p&gt;Managing &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; environments can sometimes feel like navigating a jungle 🌴🐍, especially when dealing with dependency conflicts. To streamline this process, I created a handy &lt;a href=&quot;/glossary/zsh/&quot;&gt;Zsh&lt;/a&gt; function to add to your &lt;a href=&quot;/2024/04/02/updating-my-dotfiles/&quot;&gt;dotfile&lt;/a&gt; for switching &lt;a href=&quot;/glossary/conda/&quot;&gt;Conda&lt;/a&gt; environments quickly and efficiently. 🚀 Let me walk you through it!&lt;/p&gt;
&lt;h2&gt;Why I Needed This Function 🛠️&lt;/h2&gt;
&lt;p&gt;After months of &lt;a href=&quot;/2024/04/06/conda-for-python-environment-management-on-macos/&quot;&gt;using Conda to manage Python environments&lt;/a&gt; on my macOS machines, I realized I had built up quite a collection. Switching between them manually became tedious. So, I decided to create a quick, interactive function to simplify the process. 🎯&lt;/p&gt;
&lt;h2&gt;How It Works 🔧&lt;/h2&gt;
&lt;p&gt;Here’s the magic in action ✨:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Something here (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Conda Select function with colors and styling&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function cs() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Colors and formatting&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;local BLUE=&apos;\033[0;34m&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;local GREEN=&apos;\033[0;32m&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;local YELLOW=&apos;\033[1;33m&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;local CYAN=&apos;\033[0;36m&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;local BOLD=&apos;\033[1m&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;local NC=&apos;\033[0m&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# No Color&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Get list of conda environments&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;local environments=($(conda env list | grep -v &apos;^#&apos; | awk &apos;{print $1}&apos; | grep -v &apos;^$&apos;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Print header with styling&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo &quot;\n${BOLD}${BLUE}🐍 Available Conda Environments:${NC}\n&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Print environments with numbers and colors&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;for i in {1..${#environments[@]}}; do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if [ &quot;${environments[$i]}&quot; = &quot;base&quot; ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;echo &quot;  ${YELLOW}$i)${NC} ${CYAN}${environments[$i]}${NC} ${GREEN}(base)${NC}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;echo &quot;  ${YELLOW}$i)${NC} ${CYAN}${environments[$i]}${NC}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Get user selection with styled prompt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo &quot;\n${BOLD}${BLUE}Enter environment number (${GREEN}1-${#environments[@]}${BLUE}):${NC} &quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;read selection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Validate input&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if [[ &quot;$selection&quot; =~ ^[0-9]+$ ]] &amp;amp;&amp;amp; [ &quot;$selection&quot; -ge 1 ] &amp;amp;&amp;amp; [ &quot;$selection&quot; -le &quot;${#environments[@]}&quot; ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo &quot;${GREEN}✓ Activating ${CYAN}${environments[$selection]}${GREEN} environment...${NC}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;conda activate &quot;${environments[$selection]}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;echo &quot;${YELLOW}⚠️  Invalid selection${NC}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Using the Function ⚡&lt;/h2&gt;
&lt;p&gt;Running the &lt;code&gt;cs&lt;/code&gt; command brings up a list of your Conda environments with some stylish colors and formatting. 🎨 All you have to do is pick a number, and the script takes care of the rest!&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-11-17-zsh-conda-environment-selector/images/cs-command.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-11-17-zsh-conda-environment-selector/images/cs-command.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And there you have it! A quick and stylish way to manage your Python environments in Zsh 🐍. Have fun coding and keep your workflows clean and efficient! 🚀✨&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-11-17-zsh-conda-environment-selector.D4OQttgZ.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/11/17/zsh-conda-environment-selector-function-og.png</blog:ogImage><blog:readingTime>2</blog:readingTime><category>python</category><category>macos</category><category>code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Vinyl Scrobbling macOS App</title><link>https://www.russ.cloud/2024/10/28/vinyl-scrobbling-macos-app/</link><guid isPermaLink="true">https://www.russ.cloud//2024/10/28/vinyl-scrobbling-macos-app/</guid><pubDate>Mon, 28 Oct 2024 13:47:28 GMT</pubDate><content:encoded>&lt;p&gt;Like many techies on their day off, I completely abandoned my planned tasks and embarked on an entirely unplanned project: creating a macOS application. Well, to be more precise, I decided to prompt one using &lt;span&gt;&lt;a href=&quot;https://www.anthropic.com/news/3-5-models-and-computer-use&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Claude 3.5 Sonnet&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;With Anthropic’s recent updates to their AI model, I figured it was the perfect opportunity to test its capabilities while solving a personal need.&lt;/p&gt;
&lt;h2&gt;The Catalyst: My New Tuneshine Device&lt;/h2&gt;
&lt;p&gt;Recently, I acquired a &lt;span&gt;&lt;a href=&quot;https://www.tuneshine.rocks&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tuneshine&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; - a clever device that arrived last week. It works wonderfully with my streaming services of choice, including &lt;span&gt;&lt;a href=&quot;https://music.apple.com/profile/russmckendrick&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apple Music&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://open.spotify.com/user/russmckendrick&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Spotify&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and offers integration with &lt;span&gt;&lt;a href=&quot;https://www.last.fm/user/RussMckendrick&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;LastFM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;For those unfamiliar with the device, Techmoan provides an excellent overview in their review:&lt;/p&gt;
&lt;div class=&quot;w-full aspect-video&quot;&gt;&lt;a href=&quot;https://youtube.com/watch?v=6Y_AfSzjjL0&quot;&gt;&lt;span&gt;Play&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;For those that know me, you will know that the Tuneshine ticks a lot of geeky boxes ✅.&lt;/p&gt;
&lt;h3&gt;The Challenge&lt;/h3&gt;
&lt;p&gt;Given the Tuneshine’s LastFM support, I assumed I could continue using &lt;span&gt;&lt;a href=&quot;https://openscrobbler.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenScrobbler&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to log my vinyl listening sessions and have the album artwork display on the device. I was mistaken - but not due to any fault in either the Tuneshine or OpenScrobbler.&lt;/p&gt;
&lt;p&gt;The issue lies in how these services interact with LastFM. The Tuneshine is designed for online players and monitors tracks that are “Scrobbling now” through LastFM’s &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt;. In contrast, OpenScrobbler manually logs tracks after they’ve been played, which means they don’t trigger the “now playing” status that the Tuneshine looks for.&lt;/p&gt;
&lt;p&gt;After searching for an alternative - specifically, an application that would run in the background and send real-time “now playing” requests to the &lt;span&gt;&lt;a href=&quot;https://www.last.fm/api/scrobbling&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;LastFM API&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; - I came up empty-handed. That’s when I decided to prompt my own solution.&lt;/p&gt;
&lt;h3&gt;Where to start?&lt;/h3&gt;
&lt;p&gt;The place to start was prompting a quick MVP, could I get Clade to give me something that&lt;/p&gt;
&lt;div class=&quot;chat-message flex gap-2.5 my-4 &quot;&gt;&lt;div class=&quot;avatar w-10 h-10 rounded-full flex-shrink-0 overflow-hidden flex items-center justify-center self-start&quot;&gt;&lt;img src=&quot;https://www.russ.social/sticker-clear.svg&quot; alt=&quot;Avatar&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;flex flex-col gap-1 max-w-[70%]&quot;&gt;&lt;div class=&quot;message-bubble relative px-3 py-2 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-tr-xl rounded-br-xl rounded-bl-xl&quot;&gt;&lt;div class=&quot;chat-tail absolute top-0 chat-tail-left -left-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;message-text text-xs&quot;&gt;&lt;p&gt;I would like to have &lt;a href=&quot;/glossary/python/&quot;&gt;python&lt;/a&gt; script that scrobles to Last.FM in real-time so it shows on now playing - can you help?&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;chat-message flex gap-2.5 my-4 flex-row-reverse&quot;&gt;&lt;div class=&quot;avatar w-10 h-10 rounded-full flex-shrink-0 overflow-hidden flex items-center justify-center self-start&quot;&gt;&lt;img src=&quot;/assets/2024-10-28-vinyl-scrobbling-app/images/claude.svg&quot; alt=&quot;Avatar&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;flex flex-col gap-1 max-w-[70%]&quot;&gt;&lt;div class=&quot;message-bubble relative px-3 py-2 bg-blue-100 dark:bg-blue-900 text-gray-900 dark:text-gray-100 rounded-tl-xl rounded-bl-xl rounded-br-xl&quot;&gt;&lt;div class=&quot;chat-tail absolute top-0 chat-tail-right -right-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;message-text text-xs&quot;&gt;&lt;p&gt;Let me help you create a script for Last.FM scrobbling. I’ll create a Python script that can handle real-time scrobbling.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Claude returned the following code (I filled in the track details at the bottom):&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Something here (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import pylast&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import time&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;from datetime import datetime&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import logging&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import os&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class LastFMScrobbler&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def __init__(self, config_path=&apos;config.json&apos;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;self.load_config(config_path)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;self.setup_logging()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;self.initialize_network()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def load_config(self, config_path)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;Load configuration from JSON file&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;with open(config_path, &apos;r&apos;) as f&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;config = json.load(f)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;self.api_key = config[&apos;api_key&apos;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;self.api_secret = config[&apos;api_secret&apos;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;self.username = config[&apos;username&apos;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;self.password_hash = config[&apos;password_hash&apos;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;except FileNotFoundError&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;raise Exception(&quot;Config file not found. Please create a config.json file.&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def setup_logging(self)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;Setup logging configuration&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;logging.basicConfig(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;level=logging.INFO,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;format=&apos;%(asctime)s - %(levelname)s - %(message)s&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;handlers=[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;logging.FileHandler(&apos;scrobbler.log&apos;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;logging.StreamHandler()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;self.logger = logging.getLogger(__name__)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def initialize_network(self)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;Initialize Last.FM network connection&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self.network = pylast.LastFMNetwork(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;api_key=self.api_key,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;api_secret=self.api_secret,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;username=self.username,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;password_hash=self.password_hash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self.logger.info(&quot;Successfully connected to Last.FM network&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;except Exception as e&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self.logger.error(f&quot;Failed to initialize Last.FM network&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;str(e)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;raise&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def scrobble_track(self, artist, title, album=&quot;&quot;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;Scrobble a track to Last.FM&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;timestamp = int(time.time())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self.network.scrobble(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;artist=artist,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;title=title,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;timestamp=timestamp,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;album=album&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self.logger.info(f&quot;Scrobbled&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;artist&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return True&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;except Exception as e:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;self.logger.error(f&quot;&lt;/span&gt;&lt;span&gt;Failed to scrobble track&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;str(e)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return False&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def update_now_playing(self, artist, title, album=&quot;&quot;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;Update now playing status&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self.network.update_now_playing(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;artist=artist,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;title=title,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;album=album&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self.logger.info(f&quot;Updated now playing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;artist&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return True&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;except Exception as e:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;self.logger.error(f&quot;&lt;/span&gt;&lt;span&gt;Failed to update now playing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;str(e)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return False&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def main():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Example configuration file format (config.json):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;#     &quot;&lt;/span&gt;&lt;span&gt;api_key&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;your_api_key&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#     &quot;api_secret&quot;: &quot;your_api_secret&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#     &quot;username&quot;: &quot;your_username&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#     &quot;password_hash&quot;: &quot;your_password_hash&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scrobbler = LastFMScrobbler()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Example usage:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;artist = &quot;Tori Amos&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;title = &quot;God&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;album = &quot;Under The Pink&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Update now playing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scrobbler.update_now_playing(artist, title, album)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Scrobble the track&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scrobbler.scrobble_track(artist, title, album)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if __name__ == &quot;__main__&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;main()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;After installing the &lt;span&gt;&lt;a href=&quot;https://github.com/pylast/pylast&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;pylast&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; module using the command below:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Something here&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip install pylast&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I ran the code and saw that “God” by Tori Amos from the album Under The Pink had appeared in LastFM and glancing over at the Tuneshine I saw the following …&lt;/p&gt;

&lt;p&gt;Result !!! Now that I proved it could work I could start prompting away.&lt;/p&gt;
&lt;h2&gt;Prompting the full application&lt;/h2&gt;
&lt;p&gt;I won’t fill this post with every prompt, because there were a lot of them, but after a few hours I had a Python powered stand-alone macOS application. The key fetaures I prompted were …&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Search albums using their Discogs ID&lt;/li&gt;
&lt;li&gt;Automatic track duration detection (with fallback options)&lt;/li&gt;
&lt;li&gt;Real-time “Now Playing” status with a timer&lt;/li&gt;
&lt;li&gt;Automatic track progression&lt;/li&gt;
&lt;li&gt;Clean configuration management as I didn’t want to hard code any of my keys or credentials into the application&lt;/li&gt;
&lt;li&gt;Detailed logging system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Its uses the &lt;span&gt;&lt;a href=&quot;https://rumps.readthedocs.io/en/latest/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;rumps&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to do the bulk of heavy lifting around being a status bar application and then &lt;span&gt;&lt;a href=&quot;https://py2app.readthedocs.io/en/latest/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;py2app&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to take the rumps powered status bar application and make it standalone,&lt;/p&gt;
&lt;h3&gt;Seeing it in action&lt;/h3&gt;
&lt;p&gt;It is a really basic application so lets dive straight in scrobble &lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/damned-damned-damned-24589202/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Damned Damned Damned&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; by &lt;span&gt;&lt;a href=&quot;https://www.russ.fm/artist/the-damned/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Damned&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;- first off we need the Discogs ID which is “24589202”. Now we have that we can open the application and search for it:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-10-28-vinyl-scrobbling-app/images/damned/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-10-28-vinyl-scrobbling-app/images/damned/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-10-28-vinyl-scrobbling-app/images/damned/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-10-28-vinyl-scrobbling-app/images/damned/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-10-28-vinyl-scrobbling-app/images/damned/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-10-28-vinyl-scrobbling-app/images/damned/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-10-28-vinyl-scrobbling-app/images/damned/04.jpeg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-10-28-vinyl-scrobbling-app/images/damned/04.jpeg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-10-28-vinyl-scrobbling-app/images/damned/05.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-10-28-vinyl-scrobbling-app/images/damned/05.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-10-28-vinyl-scrobbling-app/images/damned/06.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-10-28-vinyl-scrobbling-app/images/damned/06.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Checking the logs at &lt;code&gt;~/.vinyl-scrobbler/vinyl_scrobbler.log&lt;/code&gt; I can see the following:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Something here&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tail -f ~/.vinyl-scrobbler/vinyl_scrobbler.log&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:45:46,156 - INFO - No Discogs duration for I Feel Alright, trying Last.fm...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:45:46,156 - INFO - Fetching duration from Last.fm for: The Damned - I Feel Alright&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:45:46,156 - INFO - track.getInfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:45:46,570 - INFO - HTTP Request: POST https://ws.audioscrobbler.com/2.0/ &quot;HTTP/1.1 200 OK&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:45:46,573 - INFO - Found duration on Last.fm: 4:27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:45:46,573 - INFO - Menu updated successfully&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:46:04,573 - INFO - Loaded album: Damned Damned Damned&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:46:54,528 - INFO - track.updateNowPlaying&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:46:54,773 - INFO - HTTP Request: POST https://ws.audioscrobbler.com/2.0/ &quot;HTTP/1.1 200 OK&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:46:54,774 - INFO - Updated now playing: The Damned - Neat Neat Neat&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:49:36,780 - INFO - track.scrobble&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:49:36,941 - INFO - HTTP Request: POST https://ws.audioscrobbler.com/2.0/ &quot;HTTP/1.1 200 OK&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:49:36,943 - INFO - Scrobbled: The Damned - Neat Neat Neat&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:49:36,944 - INFO - track.updateNowPlaying&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:49:37,093 - INFO - HTTP Request: POST https://ws.audioscrobbler.com/2.0/ &quot;HTTP/1.1 200 OK&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2024-10-28 16:49:37,095 - INFO - Updated now playing: The Damned - Fan Club&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, checking the Tuneshine we can see:&lt;/p&gt;

&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;What started as an off the cuff idea after waking up turned into a successful experiment in AI-assisted development using Claude 3.5 Sonnet. The challenge was to create a solution that would enable real-time LastFM scrobbling for vinyl records to work with the Tuneshine device. Through iterative prompting and development, I was were able to create a functional macOS status bar application that not only solves the original problem but includes several useful features.&lt;/p&gt;
&lt;p&gt;The project demonstrates the potential of modern AI tools in practical application development. By leveraging Claude 3.5 Sonnet’s capabilities alongside libraries like rumps and py2app, I were was to transform a simple Python script into a fully-functional macOS application with next to no knowlege of either developing Python or native macOS applications.&lt;/p&gt;
&lt;p&gt;For those interested in trying it out or building upon this project, the complete source code is available on &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; at:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/vinyl-scrobbler/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;&lt;img src=&quot;/assets/link-previews/github.com-cb1aa54b6529-favicon.svg&quot; alt=&quot;&quot; width=&quot;20&quot; height=&quot;20&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;GitHub - russmckendrick/vinyl-scrobbler: A very small and very basic status bar app to scrobble records. See the linked blog post for more details.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A very small and very basic status bar app to scrobble records. See the linked blog post for more details. - russmckendrick/vinyl-scrobbler&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Now its not perfect, so I should probably add the following:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Build and use this app at your own risk, it was developed by AI and I am on the only one who will probably ever want to use it 😊&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;But this adhoc project served as an example of how AI can be effectively used to rapidly prototype and develop solutions for specific use cases, even when working with multiple APIs and system-level integration requirements.&lt;/p&gt;
&lt;h2&gt;Audio Summary&lt;/h2&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2024-10-28-vinyl-scrobbling-app/2024-10-28-vinyl-scrobbling-app.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-10-28-vinyl-scrobbling-app.OFQPEtI9.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/10/28/vinyl-scrobbling-macos-app-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>ai</category><category>macos</category><category>vinyl</category><category>python</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Dynamic Network Config in Ansible: Local to Azure VNet</title><link>https://www.russ.cloud/2024/09/21/dynamic-network-config-in-ansible-local-to-azure-vnet/</link><guid isPermaLink="true">https://www.russ.cloud//2024/09/21/dynamic-network-config-in-ansible-local-to-azure-vnet/</guid><description>Learn to use Ansible&apos;s network utilities for dynamic subnet configuration and Azure VNet deployment. Boost your infrastructure-as-code flexibility with practical examples.</description><pubDate>Sat, 21 Sep 2024 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If like me you have been guilty of hard coding all of your network configuration in &lt;a href=&quot;/glossary/ansible/&quot;&gt;Ansible&lt;/a&gt; variables like this …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Hard Code Values (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;vars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;network&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;full_range&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;10.0.0.0/24&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;subnets&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;address_range&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;10.0.0.0/27&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet2&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;address_range&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;10.0.0.32/27&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet3&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;address_range&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;10.0.0.64/27&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet4&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;address_range&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;10.0.0.96/27&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;… then this post is for you. Not that I am saying that is wrong, but it does mean if you want to the network range you are using them you also have to update it in four other places.&lt;/p&gt;
&lt;h2&gt;Working with network ranges dynamically in Ansible&lt;/h2&gt;
&lt;p&gt;Instead of hardcoding subnet addresses, we can leverage Ansible’s powerful filters and plugins to calculate network and subnet information dynamically. This approach enhances the flexibility and maintainability of your playbooks.&lt;/p&gt;
&lt;h3&gt;A Basic Playbook&lt;/h3&gt;
&lt;p&gt;Let’s start by creating a playbook that calculates subnet addresses based on a given network range and subnet sizes.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;site.yml (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Network and Subnet Information Playbook&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;hosts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;localhost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;gather_facts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;vars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;cidr_range&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;10.0.0.0/24&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;subnets&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cidr_size&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet2&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cidr_size&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet3&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cidr_size&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet4&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cidr_size&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Generate network information&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;network_info&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;cidr&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ cidr_range }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;available_ips&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ (cidr_range | ansible.utils.ipaddr(&apos;size&apos;)) - 2 }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Generate subnet information&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;subnet_info&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnet_info | default([]) + [{&apos;name&apos;: item.0.name, &apos;cidr&apos;: cidr_range | ansible.utils.ipsubnet(item.0.cidr_size, item.1)}] }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;loop&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnets | zip_longest(range(subnets|length), fillvalue=&apos;&apos;)|list }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Calculate detailed subnet information&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;subnet_details&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnet_details | default([]) + [subnet | combine({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;network&apos;: (subnet.cidr | ansible.utils.ipaddr(&apos;network&apos;)),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;netmask&apos;: (subnet.cidr | ansible.utils.ipaddr(&apos;netmask&apos;)),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;broadcast&apos;: (subnet.cidr | ansible.utils.ipaddr(&apos;broadcast&apos;)),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;first_ip&apos;: (subnet.cidr | ansible.utils.ipaddr(&apos;1&apos;) | ansible.utils.ipaddr(&apos;address&apos;)),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;last_ip&apos;: (subnet.cidr | ansible.utils.ipaddr(&apos;-2&apos;) | ansible.utils.ipaddr(&apos;address&apos;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})] }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;loop&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnet_info }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;loop_control&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;loop_var&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subnet&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Display network information&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;vars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;network_info_output&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===============================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Network Information&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===============================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CIDR Range: {{ network_info.cidr }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Available IP Addresses: {{ network_info.available_ips }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Subnet Overview:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% for subnet in subnet_info %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- {{ subnet.name }}: {{ subnet.cidr }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% endfor %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ansible.builtin.debug&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ network_info_output.split(&apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&apos;) }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Display detailed subnet information&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;vars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;detailed_info&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===============================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Detailed Subnet Information&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===============================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% for subnet in subnet_details %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{{ subnet.name }}:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CIDR: {{ subnet.cidr }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Network: {{ subnet.network }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Netmask: {{ subnet.netmask }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Broadcast: {{ subnet.broadcast }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;First Usable IP: {{ subnet.first_ip }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Last Usable IP: {{ subnet.last_ip }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% if not loop.last %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% endif %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% endfor %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ansible.builtin.debug&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ detailed_info.split(&apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&apos;) }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Breaking Down the Playbook&lt;/h3&gt;
&lt;p&gt;The playbook is designed to generate and display network and subnet information based on a given CIDR range. Let’s break it down:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Playbook Header:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name: “Network and Subnet Information Playbook”&lt;/li&gt;
&lt;li&gt;Hosts: localhost (runs on the local machine)&lt;/li&gt;
&lt;li&gt;gather_facts: true (collects system information)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cidr_range: Defines the main network CIDR (10.0.0.0/24)&lt;/li&gt;
&lt;li&gt;subnets: A list of 4 subnets, each with a name and CIDR size (27 bits)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tasks:
The playbook contains several tasks that generate and display network information.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Tasks&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Generate network information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creates a ‘network_info’ fact containing the CIDR range and available IP addresses&lt;/li&gt;
&lt;li&gt;Uses the ‘ansible.utils.ipaddr’ filter to calculate the number of available IPs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generate subnet information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creates a ‘subnet_info’ list containing each subnet’s name and CIDR&lt;/li&gt;
&lt;li&gt;Uses ‘ansible.utils.ipsubnet’ to calculate subnet CIDRs&lt;/li&gt;
&lt;li&gt;Utilizes a loop with zip_longest to iterate over subnets&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Calculate detailed subnet information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Expands on ‘subnet_info’ to create ‘subnet_details’&lt;/li&gt;
&lt;li&gt;Adds network, netmask, broadcast, first IP, and last IP for each subnet&lt;/li&gt;
&lt;li&gt;Uses various ‘ansible.utils.ipaddr’ filters to calculate these values&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Display network information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creates a formatted output string with network overview&lt;/li&gt;
&lt;li&gt;Uses ansible.builtin.debug to display the information&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Display detailed subnet information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creates a formatted output string with detailed subnet information&lt;/li&gt;
&lt;li&gt;Uses ansible.builtin.debug to display the information&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Understanding the ansible.utils.ipsubnet function&lt;/h3&gt;
&lt;p&gt;The ‘ansible.utils.ipsubnet’ function is used to calculate subnet information based on a given network and subnet size. In this playbook, it’s used as follows:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Dynamic Network Configuration (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cidr_range | ansible.utils.ipsubnet(item.0.cidr_size, item.1)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;cidr_range: The base network (10.0.0.0/24)&lt;/li&gt;
&lt;li&gt;item.0.cidr_size: The size of the subnet (27 in this case)&lt;/li&gt;
&lt;li&gt;item.1: The index of the current subnet (0, 1, 2, 3)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This function calculates the CIDR for each subnet within the main network. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Subnet 1: 10.0.0.0/27&lt;/li&gt;
&lt;li&gt;Subnet 2: 10.0.0.32/27&lt;/li&gt;
&lt;li&gt;Subnet 3: 10.0.0.64/27&lt;/li&gt;
&lt;li&gt;Subnet 4: 10.0.0.96/27&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Understanding the ansible.utils.ipaddr function&lt;/h3&gt;
&lt;p&gt;The ‘ansible.utils.ipaddr’ function is a versatile filter for IP address manipulation. In this playbook, it’s used in several ways, as you can see from the examples below the ‘ansible.utils.ipaddr’ function accepts various options to perform different operations on IP addresses and networks, making it a powerful tool for network-related tasks in Ansible playbooks.&lt;/p&gt;
&lt;h4&gt;Calculating available IPs&lt;/h4&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Dynamic Network Configuration (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cidr_range | ansible.utils.ipaddr(&apos;size&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This calculates the total number of IP addresses in the CIDR range.&lt;/p&gt;
&lt;h4&gt;Getting network address&lt;/h4&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Dynamic Network Configuration (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt;yaml {linenos=true}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subnet.cidr | ansible.utils.ipaddr(&apos;network&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This extracts the network address from a CIDR.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#### Getting netmask&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```yaml frame=&quot;code&quot; title=&quot;Dynamic Network Configuration (YAML)&quot; showLineNumbers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;subnet.cidr | ansible.utils.ipaddr(&apos;netmask&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This calculates the subnet mask for a given CIDR.&lt;/p&gt;
&lt;h4&gt;Getting broadcast address&lt;/h4&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Dynamic Network Configuration (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;subnet.cidr | ansible.utils.ipaddr(&apos;broadcast&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This calculates the broadcast address for a subnet.&lt;/p&gt;
&lt;h4&gt;Calculating first and last usable IPs&lt;/h4&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Dynamic Network Configuration (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;subnet.cidr | ansible.utils.ipaddr(&apos;1&apos;) | ansible.utils.ipaddr(&apos;address&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;subnet.cidr | ansible.utils.ipaddr(&apos;-2&apos;) | ansible.utils.ipaddr(&apos;address&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;These calculate the first and last usable IP addresses in a subnet.&lt;/p&gt;
&lt;h3&gt;Running the Playbook&lt;/h3&gt;
&lt;p&gt;Running the &lt;code&gt;site.yml&lt;/code&gt; playbook using the following:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Running the playbook&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible-playbook site.yml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Gives the following output:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Full Output&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[WARNING]: No inventory was parsed, only implicit localhost is available&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;localhost does not match &apos;all&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PLAY [Network and Subnet Information Playbook] *************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Gathering Facts] *************************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Generate network information] ************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Generate subnet information] *************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item=[{&apos;name&apos;: &apos;subnet1&apos;, &apos;cidr_size&apos;: 27}, 0])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item=[{&apos;name&apos;: &apos;subnet2&apos;, &apos;cidr_size&apos;: 27}, 1])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item=[{&apos;name&apos;: &apos;subnet3&apos;, &apos;cidr_size&apos;: 27}, 2])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item=[{&apos;name&apos;: &apos;subnet4&apos;, &apos;cidr_size&apos;: 27}, 3])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Calculate detailed subnet information] ***************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item={&apos;name&apos;: &apos;subnet1&apos;, &apos;cidr&apos;: &apos;10.0.0.0/27&apos;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item={&apos;name&apos;: &apos;subnet2&apos;, &apos;cidr&apos;: &apos;10.0.0.32/27&apos;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item={&apos;name&apos;: &apos;subnet3&apos;, &apos;cidr&apos;: &apos;10.0.0.64/27&apos;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item={&apos;name&apos;: &apos;subnet4&apos;, &apos;cidr&apos;: &apos;10.0.0.96/27&apos;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Display network information] *************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;msg&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;===============================&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Network Information&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;===============================&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;CIDR Range: 10.0.0.0/24&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Available IP Addresses: 254&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Subnet Overview:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;- subnet1: 10.0.0.0/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;- subnet2: 10.0.0.32/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;- subnet3: 10.0.0.64/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;- subnet4: 10.0.0.96/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Display detailed subnet information] ****************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;msg&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;===============================&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Detailed Subnet Information&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;===============================&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;subnet1:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  CIDR: 10.0.0.0/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Network: 10.0.0.0&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Netmask: 255.255.255.224&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Broadcast: 10.0.0.31&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  First Usable IP: 10.0.0.1&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Last Usable IP: 10.0.0.30&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;subnet2:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  CIDR: 10.0.0.32/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Network: 10.0.0.32&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Netmask: 255.255.255.224&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Broadcast: 10.0.0.63&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  First Usable IP: 10.0.0.33&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Last Usable IP: 10.0.0.62&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;subnet3:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  CIDR: 10.0.0.64/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Network: 10.0.0.64&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Netmask: 255.255.255.224&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Broadcast: 10.0.0.95&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  First Usable IP: 10.0.0.65&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Last Usable IP: 10.0.0.94&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;subnet4:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  CIDR: 10.0.0.96/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Network: 10.0.0.96&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Netmask: 255.255.255.224&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Broadcast: 10.0.0.127&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  First Usable IP: 10.0.0.97&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Last Usable IP: 10.0.0.126&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PLAY RECAP ************************************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;localhost: ok=6  changed=0 unreachable=0  failed=0  skipped=0  rescued=0  ignored=0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, we are getting quite alot of information about the network with only telling Ansible the CIDR range to use and the number of subnets and their size.&lt;/p&gt;
&lt;h2&gt;Deploying an Azure Virtual Network&lt;/h2&gt;
&lt;p&gt;This Ansible playbook automates the creation of an &lt;a href=&quot;/glossary/vnet/&quot;&gt;Azure Virtual Network&lt;/a&gt; (VNet) and multiple subnets. It leverages the same network calculation concepts as our previous example, applying them to real &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; resources. The playbook creates a VNet with a specified CIDR range and dynamically generates subnets based on given parameters. It uses Azure Resource Manager modules to interact with Azure, and concludes by displaying detailed information about the created network infrastructure - which you probably would never include in your own Playbooks - I have included it here so we can see the output.&lt;/p&gt;
&lt;p&gt;This &lt;a href=&quot;/glossary/automation/&quot;&gt;automation&lt;/a&gt; streamlines the process of setting up complex network configurations in Azure, ensuring consistency and reducing manual errors.&lt;/p&gt;
&lt;p&gt;The Playbook itself looks like the following:&lt;/p&gt;
&lt;h3&gt;The Azure Vnet Playbook&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;azure.yml (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Create Azure Virtual Network and Subnets&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;hosts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;localhost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;connection&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;local&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;gather_facts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;vars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;resource_group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;rg-ansible-demo-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;vnet_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vnet-ansible-demo-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;cidr_range&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;10.0.0.0/24&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;subnets&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cidr_size&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet2&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cidr_size&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet3&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cidr_size&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;subnet4&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cidr_size&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Ensure resource group exists&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;azure.azcollection.azure_rm_resourcegroup&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ resource_group }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ location }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Calculate subnet CIDRs&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;subnet_info&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnet_info | default([]) + [{&apos;name&apos;: item.0.name, &apos;cidr&apos;: cidr_range | ansible.utils.ipsubnet(item.0.cidr_size, item.1)}] }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;loop&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnets | zip_longest(range(subnets|length), fillvalue=&apos;&apos;)|list }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Create virtual network&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;azure.azcollection.azure_rm_virtualnetwork&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;resource_group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ resource_group }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ vnet_name }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;address_prefixes_cidr&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ cidr_range }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Create subnets&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;azure.azcollection.azure_rm_subnet&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;resource_group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ resource_group }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ item.name }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;address_prefix_cidr&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ item.cidr }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;virtual_network_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ vnet_name }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;loop&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnet_info }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Gather VNet facts&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;azure.azcollection.azure_rm_virtualnetwork_info&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;resource_group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ resource_group }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ vnet_name }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vnet_facts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Gather VNet facts&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;azure.azcollection.azure_rm_virtualnetwork_info&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;resource_group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ resource_group }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ vnet_name }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vnet_facts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Calculate detailed subnet information&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;subnet_details&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnet_details | default([]) + [subnet | combine({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;network&apos;: (subnet.address_prefix | ansible.utils.ipaddr(&apos;network&apos;)),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;netmask&apos;: (subnet.address_prefix | ansible.utils.ipaddr(&apos;netmask&apos;)),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;broadcast&apos;: (subnet.address_prefix | ansible.utils.ipaddr(&apos;broadcast&apos;)),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;first_ip&apos;: (subnet.address_prefix | ansible.utils.ipaddr(&apos;1&apos;) | ansible.utils.ipaddr(&apos;address&apos;)),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;last_ip&apos;: (subnet.address_prefix | ansible.utils.ipaddr(&apos;-2&apos;) | ansible.utils.ipaddr(&apos;address&apos;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})] }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;loop&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ vnet_facts.virtualnetworks[0].subnets }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;loop_control&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;loop_var&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subnet&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Display detailed VNet and Subnet information&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;vars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;detailed_info&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===============================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Virtual Network Information&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===============================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Name: {{ vnet_name }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Resource Group: {{ resource_group }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Location: {{ location }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Address Space: {{ vnet_facts.virtualnetworks[0].address_prefixes[0] }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===============================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Detailed Subnet Information&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===============================&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% for subnet in subnet_details %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{{ subnet.name }}:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CIDR: {{ subnet.address_prefix }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Network: {{ subnet.network }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Netmask: {{ subnet.netmask }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Broadcast: {{ subnet.broadcast }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;First Usable IP: {{ subnet.first_ip }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Last Usable IP: {{ subnet.last_ip }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% if not loop.last %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% endif %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{% endfor %}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ansible.builtin.debug&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ detailed_info.split(&apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&apos;) }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Running the Playbook&lt;/h3&gt;
&lt;p&gt;Running the &lt;code&gt;azure.yml&lt;/code&gt; playbook using the following:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Running the playbook&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible-playbook site.yml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Gives the following output:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Full Output&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PLAY [Create Azure Virtual Network and Subnets] ************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Ensure resource group exists] ************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;changed: [localhost]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Calculate subnet CIDRs] ******************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item=[{&apos;name&apos;: &apos;subnet1&apos;, &apos;cidr_size&apos;: 27}, 0])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item=[{&apos;name&apos;: &apos;subnet2&apos;, &apos;cidr_size&apos;: 27}, 1])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item=[{&apos;name&apos;: &apos;subnet3&apos;, &apos;cidr_size&apos;: 27}, 2])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item=[{&apos;name&apos;: &apos;subnet4&apos;, &apos;cidr_size&apos;: 27}, 3])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Create virtual network] ******************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;changed: [localhost]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Create subnets] ***************************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;changed: [localhost] =&amp;gt; (item={&apos;name&apos;: &apos;subnet1&apos;, &apos;cidr&apos;: &apos;10.0.0.0/27&apos;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;changed: [localhost] =&amp;gt; (item={&apos;name&apos;: &apos;subnet2&apos;, &apos;cidr&apos;: &apos;10.0.0.32/27&apos;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;changed: [localhost] =&amp;gt; (item={&apos;name&apos;: &apos;subnet3&apos;, &apos;cidr&apos;: &apos;10.0.0.64/27&apos;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;changed: [localhost] =&amp;gt; (item={&apos;name&apos;: &apos;subnet4&apos;, &apos;cidr&apos;: &apos;10.0.0.96/27&apos;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Gather VNet facts] ************************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Gather VNet facts] *************************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Calculate detailed subnet information] *****************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item={&apos;id&apos;: &apos;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-ansible-demo-uks/providers/Microsoft.Network/virtualNetworks/vnet-ansible-demo-uks/subnets/subnet1&apos;, &apos;name&apos;: &apos;subnet1&apos;, &apos;provisioning_state&apos;: &apos;Succeeded&apos;, &apos;address_prefix&apos;: &apos;10.0.0.0/27&apos;, &apos;address_prefixes&apos;: None, &apos;network_security_group&apos;: None, &apos;route_table&apos;: None})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item={&apos;id&apos;: &apos;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-ansible-demo-uks/providers/Microsoft.Network/virtualNetworks/vnet-ansible-demo-uks/subnets/subnet2&apos;, &apos;name&apos;: &apos;subnet2&apos;, &apos;provisioning_state&apos;: &apos;Succeeded&apos;, &apos;address_prefix&apos;: &apos;10.0.0.32/27&apos;, &apos;address_prefixes&apos;: None, &apos;network_security_group&apos;: None, &apos;route_table&apos;: None})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item={&apos;id&apos;: &apos;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-ansible-demo-uks/providers/Microsoft.Network/virtualNetworks/vnet-ansible-demo-uks/subnets/subnet3&apos;, &apos;name&apos;: &apos;subnet3&apos;, &apos;provisioning_state&apos;: &apos;Succeeded&apos;, &apos;address_prefix&apos;: &apos;10.0.0.64/27&apos;, &apos;address_prefixes&apos;: None, &apos;network_security_group&apos;: None, &apos;route_table&apos;: None})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; (item={&apos;id&apos;: &apos;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-ansible-demo-uks/providers/Microsoft.Network/virtualNetworks/vnet-ansible-demo-uks/subnets/subnet4&apos;, &apos;name&apos;: &apos;subnet4&apos;, &apos;provisioning_state&apos;: &apos;Succeeded&apos;, &apos;address_prefix&apos;: &apos;10.0.0.96/27&apos;, &apos;address_prefixes&apos;: None, &apos;network_security_group&apos;: None, &apos;route_table&apos;: None})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TASK [Display detailed VNet and Subnet information] **********************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ok: [localhost] =&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;msg&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;===============================&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Virtual Network Information&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;===============================&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Name: vnet-ansible-demo-uks&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Resource Group: rg-ansible-demo-uks&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Location: uksouth&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Address Space: 10.0.0.0/24&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;===============================&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Detailed Subnet Information&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;===============================&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;subnet1:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  CIDR: 10.0.0.0/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Network: 10.0.0.0&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Netmask: 255.255.255.224&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Broadcast: 10.0.0.31&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  First Usable IP: 10.0.0.1&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Last Usable IP: 10.0.0.30&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;subnet2:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  CIDR: 10.0.0.32/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Network: 10.0.0.32&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Netmask: 255.255.255.224&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Broadcast: 10.0.0.63&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  First Usable IP: 10.0.0.33&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Last Usable IP: 10.0.0.62&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;subnet3:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  CIDR: 10.0.0.64/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Network: 10.0.0.64&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Netmask: 255.255.255.224&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Broadcast: 10.0.0.95&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  First Usable IP: 10.0.0.65&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Last Usable IP: 10.0.0.94&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;subnet4:&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  CIDR: 10.0.0.96/27&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Network: 10.0.0.96&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Netmask: 255.255.255.224&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Broadcast: 10.0.0.127&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  First Usable IP: 10.0.0.97&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;  Last Usable IP: 10.0.0.126&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PLAY RECAP ***************************************************************************&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;localhost:   ok=8  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, we have the VNet and subnets all created as well.&lt;/p&gt;
&lt;h3&gt;Extending the Playbook further&lt;/h3&gt;
&lt;p&gt;We could extend the playbook further, for example we could deploy an Azure Application Gateway into one of the subnets and get its static internal IP address by figuring out the forth IP address in &lt;code&gt;subnet1&lt;/code&gt; by using:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Adding an Azure Application Gateway (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Calculate 4th IP address of subnet1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;ansible.builtin.set_fact&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;appgw_ip&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ subnet_details | selectattr(&apos;name&apos;, &apos;equalto&apos;, &apos;subnet1&apos;) | map(attribute=&apos;cidr&apos;) | first | ansible.utils.ipaddr(&apos;4&apos;) | ansible.utils.ipaddr(&apos;address&apos;) }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Create public IP address for Application Gateway&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;azure.azcollection.azure_rm_publicipaddress&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;resource_group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ resource_group }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pip-agw-ansible-demo-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;allocation_method&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Static&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;sku&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standard&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Create Application Gateway&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;azure.azcollection.azure_rm_applicationgateway&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;resource_group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ resource_group }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pip-agw-ansible-demo-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;sku&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standard_v2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;tier&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standard_v2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gateway_ip_configurations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subnet&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ vnet_facts.virtualnetworks[0].subnets | selectattr(&apos;name&apos;, &apos;equalto&apos;, &apos;subnet1&apos;) | map(attribute=&apos;id&apos;) | first }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayIpConfig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;frontend_ip_configurations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public_ip_address&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pip-agw-ansible-demo-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayFrontendIP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;frontend_ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayFrontendPort&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;backend_address_pools&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayBackendPool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;backend_http_settings_collection&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;protocol&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Http&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;cookie_based_affinity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Disabled&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayBackendHttpSettings&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;http_listeners&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;frontend_ip_configuration&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayFrontendIP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;frontend_port&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayFrontendPort&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayHttpListener&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;request_routing_rules&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rule_type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Basic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;http_listener&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayHttpListener&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;backend_address_pool&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayBackendPool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;backend_http_settings&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appGatewayBackendHttpSettings&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rule1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ip_configurations&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subnet&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ vnet_facts.virtualnetworks[0].subnets | selectattr(&apos;name&apos;, &apos;equalto&apos;, &apos;subnet1&apos;) | map(attribute=&apos;id&apos;) | first }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;private_ip_address&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;{{ appgw_ip }}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;private_ip_allocation_method&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Static&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this post, we’ve explored the power of dynamic network configuration in Ansible, moving beyond hardcoded values to create more flexible and maintainable playbooks. We started with a local playbook that calculates subnet information based on a given CIDR range, utilizing Ansible’s powerful &lt;code&gt;ansible.utils.ipsubnet&lt;/code&gt; and &lt;code&gt;ansible.utils.ipaddr&lt;/code&gt; functions.&lt;/p&gt;
&lt;p&gt;We then applied these concepts to a real-world scenario, creating an Azure Virtual Network with multiple subnets. This approach not only simplifies our code but also makes it more adaptable to changes in network requirements.&lt;/p&gt;
&lt;p&gt;Key takeaways include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using &lt;code&gt;ansible.utils.ipsubnet&lt;/code&gt; to dynamically calculate subnet CIDRs&lt;/li&gt;
&lt;li&gt;Leveraging &lt;code&gt;ansible.utils.ipaddr&lt;/code&gt; for detailed IP address manipulation&lt;/li&gt;
&lt;li&gt;Applying these techniques to Azure VNet and subnet creation&lt;/li&gt;
&lt;li&gt;Extending the playbook to include advanced resources like Azure Application Gateway&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By adopting these practices, you can create more robust, flexible, and error-resistant network configurations in your Ansible playbooks. Whether you’re working with local environments or cloud providers like Azure, these techniques will help you manage network infrastructure more effectively. I would encourage you to experiment with these methods in your own projects. As you become more comfortable with dynamic network calculations, you’ll find that your infrastructure-as-code becomes more powerful and adaptable to changing requirements.&lt;/p&gt;
&lt;h2&gt;Audio Summary&lt;/h2&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2024-09-21-dynamic-network-config-in-ansible-local-to-azure-vnet/2024-09-21-dynamic-network-config-in-ansible-local-to-azure-vnet.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-09-21-dynamic-network-config-in-ansible-local-to-azure-vnet.lbRH_P5-.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/09/21/dynamic-network-config-in-ansible-local-to-azure-vnet-og.png</blog:ogImage><blog:readingTime>13</blog:readingTime><category>Ansible</category><category>Cloud</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>How to Install Ansible on a Mac: A Modern Approach</title><link>https://www.russ.cloud/2024/09/15/how-to-install-ansible-on-a-mac-a-modern-approach/</link><guid isPermaLink="true">https://www.russ.cloud//2024/09/15/how-to-install-ansible-on-a-mac-a-modern-approach/</guid><description>A guide to installing and managing Ansible on macOS using Conda, with tips for handling collections and dependencies.</description><pubDate>Sun, 15 Sep 2024 10:15:29 GMT</pubDate><content:encoded>&lt;p&gt;As the &lt;a href=&quot;/glossary/devops/&quot;&gt;DevOps&lt;/a&gt; landscape evolves, so do our methods for managing tools like &lt;a href=&quot;/glossary/ansible/&quot;&gt;Ansible&lt;/a&gt;. This post outlines my current preferred approach to installing and managing Ansible on macOS.&lt;/p&gt;
&lt;h2&gt;The Traditional Homebrew Approach&lt;/h2&gt;
&lt;p&gt;For years, many macOS users, including myself, relied on Homebrew to install and maintain Ansible. The process was straightforward:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Ansible using Homebrew&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew install ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;While this method effectively managed the core Ansible installation, it became problematic as Ansible’s ecosystem grew more complex.&lt;/p&gt;
&lt;h3&gt;The Dependency Dilemma&lt;/h3&gt;
&lt;p&gt;As more collections moved away from the core Ansible distribution, managing prerequisites for various collections became increasingly challenging. Some collections had specific dependency requirements, leading to potential conflicts and a maintenance headache - especially when doing other &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; development which have the same dependency requirements but you need to use other versions than those defined by the &lt;code&gt;requirements.txt&lt;/code&gt; which ships with a collection.&lt;/p&gt;
&lt;h2&gt;A Modern Solution: Conda for Ansible Management&lt;/h2&gt;
&lt;p&gt;Having experienced the pitfalls of Python dependency management, I’ve transitioned to using &lt;a href=&quot;/glossary/conda/&quot;&gt;Conda&lt;/a&gt; for Ansible. This approach allows for a dedicated, isolated Ansible environment, mitigating the risks of “dependency hell.”&lt;/p&gt;
&lt;h3&gt;Step-by-Step Installation Process&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create and activate a Conda environment for Ansible:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting up Ansible environment with Conda&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda create -n ansible python=3.12&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda activate ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Install Ansible within the environment:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Ansible in Conda environment&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;python -m pip install ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Managing Ansible Collections&lt;/h3&gt;
&lt;p&gt;Once Ansible is installed, you can add collections and their dependencies using Ansible Galaxy and pip.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Tip&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Always ensure you’re in your Ansible Conda environment by running &lt;code&gt;conda activate ansible&lt;/code&gt; before executing the following commands.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;For example, to install the &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; Collection and its dependencies:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Adding the Azure collection&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible-galaxy collection install azure.azcollection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;python -m pip install -r ~/.ansible/collections/ansible_collections/azure/azcollection/requirements.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Upgrading Ansible and Collections&lt;/h3&gt;
&lt;p&gt;Keeping Ansible and its collections up-to-date is crucial. Here’s how to do it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Upgrade Ansible:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Upgrading Ansible&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;python -m pip install ansible --upgrade&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Upgrade a specific collection (e.g., Azure):&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Upgrading Azure Collection&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible-galaxy collection install azure.azcollection --upgrade&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;python -m pip install -r ~/.ansible/collections/ansible_collections/azure/azcollection/requirements.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Summary and Best Practices&lt;/h2&gt;
&lt;p&gt;Adopting Conda for Ansible management on macOS offers several advantages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Isolation&lt;/strong&gt;: Your Ansible setup exists in its own environment, preventing conflicts with other Python applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: Easily manage different versions of Ansible for various projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency Control&lt;/strong&gt;: Better handle complex dependencies required by different Ansible collections.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: Ensure a consistent environment across different machines or team members.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To make the most of this setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Regularly update both Ansible and its collections to benefit from the latest features and &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; patches.&lt;/li&gt;
&lt;li&gt;Explore &lt;span&gt;&lt;a href=&quot;https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#exporting-the-environment-yml-file&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Conda’s environment export&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; features to share your exact setup with team members.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By embracing this modern approach to Ansible management on macOS, you’ll find yourself with a more maintainable, flexible, and robust Ansible environment. Happy automating!&lt;/p&gt;
&lt;h2&gt;Audio Summary&lt;/h2&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2024-09-15-how-to-install-ansible-on-a-mac-a-modern-approach/2024-09-15-how-to-install-ansible-on-a-mac-a-modern-approach.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-09-15-how-to-install-ansible-on-a-mac-a-modern-approach.CaHKQJte.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/09/15/how-to-install-ansible-on-a-mac-a-modern-approach-og.png</blog:ogImage><blog:readingTime>3</blog:readingTime><category>ansible</category><category>macos</category><category>devops</category><category>python</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Unlocking Image Creation with Flux and GPT-4o</title><link>https://www.russ.cloud/2024/08/11/unlocking-image-creation-with-flux-and-gpt-4o/</link><guid isPermaLink="true">https://www.russ.cloud//2024/08/11/unlocking-image-creation-with-flux-and-gpt-4o/</guid><description>Explore the world of AI image generation using Flux, fal.ai, and OpenAI. Learn how to build a Streamlit app that leverages GPT-4o for prompt tuning and Flux models for creating stunning visuals. Compare outputs with Midjourney and discover the potential of these cutting-edge tools.</description><pubDate>Sun, 11 Aug 2024 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This week I decided to see how many buzzwords I could get into one post, given the recent release of &lt;a href=&quot;/glossary/flux/&quot;&gt;Flux&lt;/a&gt; by Black Forest Labs I had originally intended on trying to get it up and running locally - however it quickly became clear that my M3 MacBook Pro with its 36GB of RAM wasn’t going to cut it. Because of this, I decided to look at one of the many online services that offer access to the various Flux models via their APIs which led me to Fal.AI which in turn gave me the idea for this post.&lt;/p&gt;
&lt;h2&gt;The Tools&lt;/h2&gt;
&lt;p&gt;Before we dive into the idea I had, let’s quickly get ourselves up to speed with the tools we’ll be using in this post. Each of these technologies plays a crucial role in our AI-powered image generation project.&lt;/p&gt;
&lt;h3&gt;Flux.1: The new kid on the block&lt;/h3&gt;
&lt;p&gt;A team of AI researchers and engineers, renowned for their work on foundational generative AI models like VQGAN and Latent Diffusion across academic, industrial, and open-source platforms, surprised everyone with the announcement of &lt;span&gt;&lt;a href=&quot;https://blackforestlabs.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Black Forest Labs&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Alongside their Series Seed funding round of $31 million, they also revealed the immediate availability of three variations of their Flux.1 model:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FLUX.1 [pro]:&lt;/strong&gt; The premier version of the FLUX.1 model, offering state-of-the-art image generation with exceptional prompt following, visual quality, image detail, and output diversity. FLUX.1 [pro] is accessible via &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt;, and is also available through platforms such as Replicate and fal.ai. Additionally, it supports dedicated and customized enterprise solutions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FLUX.1 [dev]:&lt;/strong&gt; An open-weight, guidance-distilled model intended for non-commercial applications. Derived directly from FLUX.1 [pro], FLUX.1 [dev] provides similar quality and prompt adherence capabilities while being more efficient than standard models of the same size. The model’s weights are available top download from &lt;span&gt;&lt;a href=&quot;https://huggingface.co/black-forest-labs/FLUX.1-dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Hugging Face&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and it can be executed on &lt;span&gt;&lt;a href=&quot;https://replicate.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Replicate&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://fal.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;fal.ai&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FLUX.1 [schnell]:&lt;/strong&gt; Designed as the fastest model in the lineup, FLUX.1 [schnell] is optimized for local development and personal use. It is freely available under an Apache 2.0 license, with weights hosted on Hugging Face. Inference code can be found on &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; and in Hugging Face’s Diffusers, and the model is integrated with ComfyUI from day one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For an idea of what you can produce have a look at the example images below, which were all generated using the tools we are going to be covering in this post:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/samples/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/samples/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/samples/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/samples/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/samples/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/samples/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/samples/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/samples/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see, it can not only handle text extremely well, but it also produces high-quality photo-realistic images as well as some abstract ones.&lt;/p&gt;
&lt;h3&gt;fal.ai: Doing the heavy lifting&lt;/h3&gt;
&lt;p&gt;Also already mentioned, running even the small model locally was out of the question so I decided to look at one of the two original partners providing all three models in the Flux.1 family, I chose to focus on &lt;span&gt;&lt;a href=&quot;https://fal.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;fal.ai&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; as I had used them previously to test another model earlier in the year.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating an image using cURL&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export FAL_KEY=&quot;YOUR_API_KEY&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl --request POST \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--url https://fal.run/fal-ai/flux-pro \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--header &quot;Authorization: Key $FAL_KEY&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--header &quot;Content-Type: application/json&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--data &apos;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;prompt&quot;: &quot;Extreme close-up of a single tiger eye, direct frontal view. Detailed iris and pupil. Sharp focus on eye texture and color. Natural lighting to capture authentic eye shine and depth. The word \&quot;FLUX\&quot; is painted over it in big, white brush strokes with visible texture.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This returned the following &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; response:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The results of the image (JSON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;images&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://fal.media/files/panda/oGSuf_FrMn7im_0DGnSdg_4378270c342f4170bb0f55e14cbb0636.jpg&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1024&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;768&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;content_type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;image/jpeg&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;timings&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;seed&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;932194184&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;has_nsfw_concepts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;prompt&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Extreme close-up of a single tiger eye, direct frontal view. Detailed iris and pupil. Sharp focus on eye texture and color. Natural lighting to capture authentic eye shine and depth. The word &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;FLUX&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; is painted over it in big, white brush strokes with visible texture.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Opening the image in the response gave us the following:&lt;/p&gt;

&lt;p&gt;This got me thinking - there is a &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; library for fal.ai so I could use that, also what other tools have Python libraries I can use alongside fai.ai?&lt;/p&gt;
&lt;h3&gt;OpenAI: Building a Better Prompt&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://openai.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenAI&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; shouldn’t need an introduction, we will be using the &lt;span&gt;&lt;a href=&quot;https://platform.openai.com/docs/models/gpt-4o&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GPT4o models&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to help with prompting.&lt;/p&gt;
&lt;h3&gt;Streamlit: Bring it all together&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://streamlit.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Streamlit&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is a Python framework from &lt;span&gt;&lt;a href=&quot;https://www.snowflake.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Snowflake&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; that quickly lets you build data and AI-driven web applications with a really low entry point, see the following for a quick overview:&lt;/p&gt;
&lt;div class=&quot;w-full aspect-video&quot;&gt;&lt;a href=&quot;https://youtube.com/watch?v=c9k8K1eII4g&quot;&gt;&lt;span&gt;Play&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;There was just one problem with this, I am not a developer - so how could I do this?&lt;/p&gt;
&lt;h3&gt;Claude: Your personal development team&lt;/h3&gt;
&lt;p&gt;The final tool used in this post is &lt;span&gt;&lt;a href=&quot;https://claude.ai/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Claude&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; from &lt;span&gt;&lt;a href=&quot;https://www.anthropic.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Anthropic&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, while I may not be a developer - I can prompt and debug code and since the release of &lt;span&gt;&lt;a href=&quot;https://www.anthropic.com/news/claude-3-5-sonnet&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Claude 3.5 Sonnet&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; I have been using it more and more for development tasks.&lt;/p&gt;
&lt;p&gt;So let’s give it a go !!!&lt;/p&gt;
&lt;h2&gt;The Idea&lt;/h2&gt;
&lt;p&gt;The idea I had was to use Streamlit to build an interface to interact with the fal.ai API and the Flux family of models from my local machine - as part of this it should also use GPT4o to help tune the prompt to give us the best shot at getting a good result from Flux.&lt;/p&gt;
&lt;h2&gt;Running the application&lt;/h2&gt;
&lt;p&gt;Before we dive into the code, let’s just download and run the app.&lt;/p&gt;
&lt;h3&gt;Getting the keys&lt;/h3&gt;
&lt;p&gt;To run the application you will need two API keys, one for fal.ai and the other for the OpenAI API, the links below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://fal.ai/docs/authentication/key-based&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Key-based Authentication on fal.ai&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://platform.openai.com/docs/api-reference/authentication&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenAI Authentication documentation&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make a note of the keys and keep them very safe.&lt;/p&gt;
&lt;h3&gt;Creating the environment&lt;/h3&gt;
&lt;p&gt;Next up we need to create an environment to run the application in and grab the code from &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/flux-fal-openai-streamlit/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;I use &lt;a href=&quot;/glossary/conda/&quot;&gt;Conda&lt;/a&gt; to run Python on my local machine, you can find &lt;a href=&quot;/2024/04/06/conda-for-python-environment-management-on-macos/&quot;&gt;my blog post about installing and using Conda here&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Run the commands below to create the conda environment, switch to it, check the code out from &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/flux-fal-openai-streamlit/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and install the required Python packages:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Preparing the environment&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda create -n streamlit python=3.12&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda activate streamlit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git clone https://github.com/russmckendrick/flux-fal-openai-streamlit.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd flux-fal-openai-streamlit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip install -r requirements.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Launching the application&lt;/h3&gt;
&lt;p&gt;We are nearly ready to launch the application, just one more thing to do and that is the expose our API keys as an environment variable for our application to read:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Exporting the API keys&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export FAL_KEY=&quot;&amp;lt;put-your-fal.ai-key-here&amp;gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export OPENAI_API_KEY=&apos;&amp;lt;put-your-openai-key-here&amp;gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the keys in place we can launch the application by running the command below:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Exporting the API keys&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;streamlit run app.py&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will open the application window, which should look something like the following:&lt;/p&gt;

&lt;h3&gt;Using the application&lt;/h3&gt;
&lt;p&gt;Now we have the application open, let’s generate an image, first let’s write a basic prompt:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Initial Prompt:&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;A magical cityscape&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Just clicking “☁️ Generate image” at this point will give you an impressive-looking image without any tuning, below is the image which was generated using the basic prompt above:&lt;/p&gt;

&lt;p&gt;Lets now get GPT-4o to expand on our basic prompt. First, click on “Use OpenAI for prompt tuning” and then select your chosen GPT-4o model from the drop-down, in this example, I will use the default of gpt-4o rather than gpt-4o-mini, then click on the “✏️ Tune Prompt” button.&lt;/p&gt;
&lt;p&gt;This will send your original prompt to the model and not only return a new prompt, which in this run was:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Tuned Prompt:&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;A sprawling cityscape filled with fantastical architecture, where towering crystal spires and floating gardens intertwine with ancient, ivy-covered stone buildings. The streets are aglow with bioluminescent plants and bustling with mythical creatures such as griffins and fairies. In the sky, airships float alongside dragons, under a sky painted with auroras and dotted with twinkling stars and moons.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You will also get if you click on “See explanation and suggestions” more details on why the prompt has been updated:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Explanation of changes:&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The original prompt was very general and lacked specificity. By adding details such as “towering crystal spires,” “floating gardens,” “ancient, ivy-covered stone buildings,” and “bioluminescent plants,” the image becomes more vivid and specific. Including mythical creatures like griffins and fairies adds an element of magic and life to the city. The mention of airships, dragons, auroras, and multiple moons helps to set a more fantastical and immersive atmosphere.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;As well as more suggestions on how you could further improve the prompt:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Additional suggestions:&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;To further enhance the prompt, consider specifying the time of day or the overall mood you want to convey (e.g., twilight, festive, serene). You could also indicate the cultural influences or architectural styles you envision (e.g., “influenced by ancient Greek and futuristic aesthetics”).&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you are happy with the new prompt, click on ”✅ Accept Tuned Prompt” and then click the “☁️ Generate image” button, after a minute you will get your image back:&lt;/p&gt;

&lt;p&gt;The screens below show how the process above looks within the application:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/05.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/05.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/06.jpeg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o/images/app/prompt-tune/06.jpeg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the last picture, a copy of the image and a &lt;a href=&quot;/glossary/markdown/&quot;&gt;markdown&lt;/a&gt; summary have been saved to your local machine.&lt;/p&gt;
&lt;h2&gt;Code Highlights&lt;/h2&gt;
&lt;p&gt;Let’s take a closer look at some key parts of our application’s code. These snippets highlight the core functionality and demonstrate how we’re integrating the various tools we’ve discussed. By examining these, you’ll get a better understanding of how the different components work together to create our AI-powered image generation app.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;tune_prompt_with_openai (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tune_prompt_with_openai&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prompt&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;openai_api_key &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getenv&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;OPENAI_API_KEY&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; openai_api_key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;raise&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ValueError&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;OPENAI_API_KEY environment variable is not set&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;client &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; openai&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OpenAI&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;api_key&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;openai_api_key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; client&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;chat&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;completions&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;messages&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;role&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;system&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;content&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;You are an advanced AI assistant specialized in refining and enhancing image generation prompts. Your goal is to help users create more effective, detailed, and creative prompts for high-quality images. Respond with: 1) An improved prompt (prefix with &apos;PROMPT:&apos;), 2) Explanation of changes (prefix with &apos;EXPLANATION:&apos;), and 3) Additional suggestions (prefix with &apos;SUGGESTIONS:&apos;). Each section should be on a new line.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;role&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;user&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;content&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Improve this image generation prompt: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;prompt&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; response&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;choices&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;strip&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the system prompt below, we are instructing the model to not only generate the prompt but also everything else and how it should format its response:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;The system prompt&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;You are an advanced AI assistant specialized in refining and enhancing image generation prompts. Your goal is to help users create more effective, detailed, and creative prompts for high-quality images.&lt;/p&gt;&lt;p&gt;Respond with:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;An improved prompt (prefix with ‘PROMPT:’),&lt;/li&gt;
&lt;li&gt;Explanation of changes (prefix with ‘EXPLANATION:’),&lt;/li&gt;
&lt;li&gt;Additional suggestions (prefix with ‘SUGGESTIONS:’).&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Each section should be on a new line.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This means we only make one call to the model and also we have sections we can identify when we receive the response.&lt;/p&gt;
&lt;p&gt;As the images may take a little while to generate I had Claude add some messages to cycle through rather than us just looking at a boring spinner:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;cycle_spinner_messages (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cycle_spinner_messages&lt;/span&gt;&lt;span&gt;():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;messages &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🎨 Mixing colors...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;✨ Sprinkling creativity dust...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🖌️ Applying artistic strokes...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🌈 Infusing with vibrant hues...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🔍 Focusing on details...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🖼️ Framing the masterpiece...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🌟 Adding that special touch...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🎭 Bringing characters to life...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🏙️ Building the scene...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;🌅 Setting the perfect mood...&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; itertools&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cycle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;messages&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When I said Streamlit made creating web applications really easy, I wasn’t kidding. The code below generates the bulk of what you see on the initial screen:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;main() (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;🤖 Image Generation with fal.ai &amp;amp; Flux&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Check for environment variables&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getenv&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FAL_KEY&quot;&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FAL_KEY environment variable is not set. Please set it before running the app.&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Model selection dropdown&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;model_options &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Flux Pro&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/flux-pro&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Flux Dev&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/flux/dev&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Flux Schnell&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/flux/schnell&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Flux Realism&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fal-ai/flux-realism&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;selected_model &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;selectbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Select Model:&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;model_options&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;keys&lt;/span&gt;&lt;span&gt;()),&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Basic parameters&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;image_size &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;selectbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Image Size:&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;square_hd&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;square&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;portrait_4_3&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;portrait_16_9&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;landscape_4_3&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;landscape_16_9&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;num_inference_steps &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;slider&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Number of Inference Steps:&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;min_value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max_value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;28&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Advanced configuration in an expander&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;expander&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Advanced Configuration&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expanded&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;False&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;guidance_scale &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;slider&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Guidance Scale:&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;min_value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max_value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;20.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;3.5&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;safety_tolerance &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;selectbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Safety Tolerance:&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;3&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;4&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Initialize session state&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;user_prompt&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;user_prompt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;tuned_prompt&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tuned_prompt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;prompt_accepted&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prompt_accepted &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;False&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# User input for the prompt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;user_prompt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;text_input&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Enter your image prompt:&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;user_prompt&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Update session state when user types in the input field&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; user_prompt &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;user_prompt&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;user_prompt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; user_prompt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prompt_accepted &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;False&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# OpenAI prompt tuning options&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;use_openai_tuning &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;checkbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Use OpenAI for prompt tuning&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;False&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;openai_model_options &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;gpt-4o&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;gpt-4o-mini&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;selected_openai_model &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;selectbox&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Select OpenAI Model:&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; openai_model_options&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;disabled&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; use_openai_tuning&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The drop-downs, text input boxes, sliders and the hidden advanced settings are all native Streamlit components which “just work”, also being all native it gives the application a nice consistent look when other things happen like:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Generating image (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# Display the prompt being used&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;subheader&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;☁️ Generating image with the following prompt:&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;st&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;info&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user_prompt&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The rest of the code can be found on the application’s &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/flux-fal-openai-streamlit/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub repo&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Some more images&lt;/h2&gt;
&lt;p&gt;Before wrapping up this post, let’s take some of the prompts I have used to generate previous blog post covers in Midjouney and run them through Flux - I will leave you to make your own mind up on which is better 😊.&lt;/p&gt;
&lt;h3&gt;Generating an Azure Storage Account SAS token using Azure Logic and Function apps&lt;/h3&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Original Prompt&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;A high-resolution digital photograph featuring an &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt;-colored traditional combination safe in a large open-plan office. The vintage metal safe, painted in shades of Azure blue, stands prominently next to a modern desk with high-tech tools. The setting blends modern and vintage elements, with natural light streaming through large windows and focused artificial lighting highlighting the safe and the workspace. Soft clouds surround the safe, symbolizing cloud storage. The image conveys the concept of secure access to information, blending old-world &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; with modern cloud technology. Shot on Kodachrome film stock for a rich, professional look.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Midjourney&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flux.1 [pro]&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;/2024/07/27/generating-an-azure-storage-account-sas-token-using-azure-logic-and-function-apps/&quot;&gt;Link to post&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Azure Firewall KQL Query&lt;/h3&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Original Prompt&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Retro computing magazine cover, &lt;a href=&quot;/glossary/kql/&quot;&gt;KQL&lt;/a&gt; queries theme vibrant illustrated with futuristic elements, early 1980s style, computer terminals, bright color palette with yellows, blues, and reds, hand-drawn illustration style ::1 1980s computing magazine aesthetic, firewall, flames, fire, log, logs, no photorealism ::1&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Midjourney&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flux.1 [pro]&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;/2024/07/20/azure-firewall-kql-query/&quot;&gt;Link to post&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Azure Virtual Desktop KQL Queries&lt;/h3&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Original Prompt&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Retro computing magazine cover, KQL queries theme vibrant illustrated landscape with futuristic elements, early 1980s style, computer terminals integrated into nature scenes, power lines connecting devices, cartoon-style buildings in background, vintage computer hardware in foreground, bright color palette with yellows, blues, and greens, hand-drawn illustration style ::1 1980s computing magazine aesthetic, no photorealism ::1&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Midjourney&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flux.1 [pro]&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;/2024/07/07/azure-virtual-desktop-kql-queries/&quot;&gt;Link to post&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Azure DevOps Ansible Pipeline; Boosting Efficiency with Caching&lt;/h3&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Original Prompt&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Photorealistic image of a 1950s workshop bench. Two side-by-side conveyor belts with gears and cogs visible. On the left belt: a vintage tin robot toy moving slowly. On the right belt: a shiny die-cast 50s sports car toy speeding ahead. Warm lighting, slight workshop clutter. Muted color palette with focus on metallic silvers, cherry reds, and pastel blues.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Midjourney&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flux.1 [pro]&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;/2024/06/28/azure-devops-ansible-pipeline-boosting-efficiency-with-caching/&quot;&gt;Link to post&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Day to Day Tools, the 2024 edition&lt;/h3&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Original Prompt&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Create a photorealistic image of a well-organized tool wall in an indoor rustic workshop setting, showcasing a variety of tools neatly arranged, highlighting the tools used for everyday tasks - tools should represent different macOS tools like VS Code, Sublime Text, Docker, Apple Music to name but a few. Also, in the foreground there is an early 80s Beige IBM PC with a green screen CRT monitor. The lighting is soft and bright, ensuring all details of the tools and workbench are clearly visible. The image should look clean and professional, with a focus on the organization and variety of tools.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Midjourney&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flux.1 [pro]&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;/2024/06/16/day-to-day-tools-the-2024-edition/&quot;&gt;Link to post&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Conda for Python environment management on macOS&lt;/h3&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Original Prompt&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;An extremely photo-realistic image of a developer with pronounced frustration on his face, his thick glasses slipping down the bridge of his nose, shirt sleeves rolled up. He’s surrounded by a chaotic landscape of tangled wires, scattered papers, and multiple monitors flashing code on his very cluttered desk. In contrast, the modern office around him is orderly, with colleagues focused on their tasks, highlighting his isolation in frustration. Emphasize the contrast between his messy area and the clean, organized workspace of the office. Capture this in a wider shot to show both his immediate environment and the office backdrop, providing a full context of his challenging situation.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Midjourney&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flux.1 [pro]&lt;/strong&gt; …&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;/2024/04/06/conda-for-python-environment-management-on-macos/&quot;&gt;Link to post&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now that you’ve seen the potential of combining Flux, fal.ai, OpenAI, and Streamlit, why not give it a try yourself? Clone the repository, set up your environment, and start experimenting with your own prompts. Whether you’re a developer looking to build on this framework or a creative professional curious about AI-assisted image generation, there’s plenty of room for exploration and innovation. Don’t forget to share your experiences or any cool images you generate – I’d love to see what you come up with!&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-08-11-unlocking-image-creation-with-flux-and-gpt-4o.bgsSvZxQ.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/08/11/unlocking-image-creation-with-flux-and-gpt-4o-og.png</blog:ogImage><blog:readingTime>16</blog:readingTime><category>ai</category><category>python</category><category>code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Behind the Scenes; More than just a background</title><link>https://www.russ.cloud/2024/08/04/behind-the-scenes-more-than-just-a-background/</link><guid isPermaLink="true">https://www.russ.cloud//2024/08/04/behind-the-scenes-more-than-just-a-background/</guid><description>Dive into the world of a passionate vinyl collector, exploring an extensive record collection, my audio setup, and the intersection of music and technology.</description><pubDate>Sun, 04 Aug 2024 10:47:42 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve ever joined me on a Teams call from my home office, you’ve likely noticed the wall of vinyl records behind me. This post aims to answer the most common questions I get on calls, offering a glimpse into my music collection and the technology used to play it.&lt;/p&gt;
&lt;h2&gt;The Collection&lt;/h2&gt;
&lt;p&gt;My vinyl collection is more than just a backdrop; let me give you a tour:&lt;/p&gt;
&lt;h3&gt;The Main Attraction&lt;/h3&gt;
&lt;p&gt;The wall you see behind me during video calls is just the beginning:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-04-i-like-your-background/images/01/01.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-04-i-like-your-background/images/01/01.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;What you don’t see on calls&lt;/h3&gt;
&lt;p&gt;Adjacent to my desk, a second wall of records adds to the collection:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-04-i-like-your-background/images/02/02.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-04-i-like-your-background/images/02/02.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-04-i-like-your-background/images/02/03.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-04-i-like-your-background/images/02/03.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Box Set Bonanza&lt;/h3&gt;
&lt;p&gt;For those special editions and compilations, my bookshelves house an array of box sets:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-04-i-like-your-background/images/03/04.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-04-i-like-your-background/images/03/04.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-04-i-like-your-background/images/03/05.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-04-i-like-your-background/images/03/05.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Exploring the Collection Digitally&lt;/h2&gt;
&lt;p&gt;With over 2,827 items cataloged on &lt;span&gt;&lt;a href=&quot;https://www.discogs.com/user/russmck/collection?page=1&amp;amp;limit=250&amp;amp;header=1&amp;amp;layout=big&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Discogs&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;; the collection caters to every mood and taste and I don’t find myself with nothing to listen to. A while ago I got bored during a winter weekend and using ChatGPT created a custom website that brings my collection to life digitally. Using data from Discogs, Wikipedia, Apple Music, and Spotify, &lt;span&gt;&lt;a href=&quot;https://www.russ.fm/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.russ.fm/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; offers an interactive exploration of my vinyl collection.&lt;/p&gt;
&lt;h3&gt;Discover a Random Album&lt;/h3&gt;
&lt;p&gt;If I ever get stuck and can’t decide what to listen I added a feature that allows you virtually randomly grab an album from the shelves until you find something you want to listen to:&lt;/p&gt;

&lt;p&gt;Click “Album Details” to learn more, or “Grab Another Album” to explore further. Hover over the cover for more information on the album if you can’t recognise from the sleeve cover!&lt;/p&gt;
&lt;h3&gt;The Tech Behind the Tunes&lt;/h3&gt;
&lt;p&gt;For those interested in the development side, here are links to the code that does the scrapping and serves the website:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/discogs-scraper&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;&lt;img src=&quot;/assets/link-previews/github.com-0b2163121930-favicon.svg&quot; alt=&quot;&quot; width=&quot;20&quot; height=&quot;20&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;GitHub - russmckendrick/discogs-scraper: A basic scraper for generating files for my website 🎸.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;A basic scraper for generating files for my website 🎸. - russmckendrick/discogs-scraper&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/records/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;&lt;img src=&quot;/assets/link-previews/github.com-fbc7b475ae43-favicon.svg&quot; alt=&quot;&quot; width=&quot;20&quot; height=&quot;20&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;GitHub - russmckendrick/records: This is the source code for russFM, a site dedicated to showcasing my personal record collection. 🎼🔥&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;This is the source code for russFM, a site dedicated to showcasing my personal record collection. 🎼🔥 - russmckendrick/records&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Don’t forget to check out my weekly listening habits in the &lt;a href=&quot;/tunes/&quot;&gt;“What have I been listening to?”&lt;/a&gt; section of this website.&lt;/p&gt;
&lt;h2&gt;The Audio Setup&lt;/h2&gt;
&lt;p&gt;To truly appreciate these records, a quality audio setup is crucial. Here’s what brings my vinyl to life:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Turntable: &lt;span&gt;&lt;a href=&quot;https://www.denon.com/en-gb/product/turntables/dp-400/DP400.html?dwvar_DP400_color=White&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Denon DP-400&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Cartridge: &lt;span&gt;&lt;a href=&quot;https://www.nagaoka.co.jp/item/mp-110.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nagaoka MP-110&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Pre-amp: &lt;span&gt;&lt;a href=&quot;https://www.cambridgeaudio.com/gbr/en/products/hi-fi/alva/solo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cambridge Audio Alva Solo&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Speakers: &lt;span&gt;&lt;a href=&quot;https://www.sonos.com/en-gb/shop/five&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sonos Five (Stereo pair)&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; &amp;amp; &lt;span&gt;&lt;a href=&quot;https://www.sonos.com/en-gb/shop/sub&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sonos Sub Gen3&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Record Cleaner: &lt;span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=fG1HjkdYsrQ&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Pro-ject VC-S MKII&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This setup is complemented by additional Sonos speakers throughout the house, a system I’ve been building for about &lt;a href=&quot;/2015/04/05/adventures-in-hi-fi/&quot;&gt;a decade&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-08-04-i-like-your-background/images/04/06.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-08-04-i-like-your-background/images/04/06.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The result of all this? It can get loud!!! 🔊&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-08-04-i-like-your-background.ClfAnWqo.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/08/04/behind-the-scenes-more-than-just-a-background-og.png</blog:ogImage><blog:readingTime>3</blog:readingTime><category>life</category><category>vinyl</category><category>blog</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Generating an Azure Storage Account SAS token using Azure Logic and Function apps</title><link>https://www.russ.cloud/2024/07/27/generating-an-azure-storage-account-sas-token-using-azure-logic-and-function-apps/</link><guid isPermaLink="true">https://www.russ.cloud//2024/07/27/generating-an-azure-storage-account-sas-token-using-azure-logic-and-function-apps/</guid><description>Learn how to generate an Azure Storage Account SAS token from a Logic App using an Azure Function. This step-by-step guide covers deploying a PowerShell-based Function App, creating the SAS token generation function, and integrating it into your Logic App workflow. Overcome common challenges and enhance your Azure development skills with this practical tutorial.</description><pubDate>Sat, 27 Jul 2024 17:03:45 GMT</pubDate><content:encoded>&lt;p&gt;Don’t you just love it when you open your laptop first thing on a Monday morning, a fresh cup of coffee at the ready and you think to yourself …&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This should be a straight-forward five-minute job to start the week with&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;… and it turns out to be anything but?&lt;/p&gt;
&lt;p&gt;That is exactly what happened to me this week, I won’t go into too much detail, but I was working on an &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; Logic App that had to interact with an external service and pass it a a fully signed &lt;a href=&quot;/glossary/sas-token/&quot;&gt;SAS&lt;/a&gt; URL for an Azure Storage Account so that it could send some quite large files there. You would assume that the &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/connectors/azureblob/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Azure Blob connector&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; would be able to do this, and you would be correct as there is a “Create SAS URI by path (V2)” action:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/connectors/azureblob/#create-sas-uri-by-path-(v2)&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Azure Blob Storage - Connectors&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;learn.microsoft.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/learn.microsoft.com-fdec7a2bd7a3.jpg&quot; alt=&quot;Microsoft Learn&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This worked great in generating a SAS URL but when I tried to use it the service I was targeting returned the following message:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;LocalFunctionProj/getSAS/run.ps1 (JSON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ExportFailed&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;The SAS token requires access to the &apos;Blob&apos; service (ss=b)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;After a few hours of making tweaks, searching forums and community posts during and between calls I could not get it to work. After a few aborted Plan B’s which included trying to make all of the REST requests needed to generate the SAS token, that failed as I could not encode the connection string correctly using the built-in Logic App functions. I eventually settled on deploying an Azure Function App to run some Powershell which generates the SAS token and passes it back to the Logic App.&lt;/p&gt;
&lt;h2&gt;Launching the Function App&lt;/h2&gt;
&lt;p&gt;First of all, we need a Function App, for this, we will be deploying a consumption plan-based App with the Powershell runtime. You can use the commands below to deploy this using the &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/cli/azure/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Azure CLI&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;First, we need to set some variables;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Set some variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-generate-sas-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export REGION=&quot;uksouth&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export FUNCATION_APP_NAME=&quot;func-generate-sas-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export FUNCATION_APP_STORAGE_ACCOUNT=&quot;safusasuks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now that we have some variables set we can add the Resource Group;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Add the resource group using the Azure CLI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With somewhere to place the resources next up we need to add the storage account which will be used by the Function App to store its files;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Deploy the Storage Account using the Azure CLI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az storage account create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $FUNCATION_APP_STORAGE_ACCOUNT \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--kind StorageV2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, we can launch the Azure Function App;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Deploy the Function App using the Azure CLI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az functionapp create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name &quot;$FUNCATION_APP_NAME&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--consumption-plan-location &quot;$REGION&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--storage-account &quot;$FUNCATION_APP_STORAGE_ACCOUNT&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--functions-version &quot;4&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--runtime &quot;powershell&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--runtime-version &quot;7.4&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Deploying the Function&lt;/h2&gt;
&lt;p&gt;Now we have the Azure Function App deployed, our next task is to create and deploy our actual function. Being a &lt;a href=&quot;/tags/macos/&quot;&gt;macOS&lt;/a&gt; user I use &lt;code&gt;func&lt;/code&gt; to create the function locally and then publish it, you can &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;download the tool from here&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, once installed, run the following commands to create the local function:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Deploy to the Function App using the Azure CLI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;func init LocalFunctionProj --worker-runtime powershell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd LocalFunctionProj&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;func new --name getSAS --template &quot;HTTP trigger&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will create all of the files needed for the function, using your IDE of choice replace the contents of &lt;code&gt;LocalFunctionProj/requirements.psd1&lt;/code&gt; with the code below:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;LocalFunctionProj/requirements.psd1 (POWERSHELL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;Az&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;12.*&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then the contents of &lt;code&gt;LocalFunctionProj/getSAS/run.ps1&lt;/code&gt; with:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;LocalFunctionProj/getSAS/run.ps1 (POWESHELL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Import required namespaces&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System.Net&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft.WindowsAzure.Storage.Blob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Input bindings are passed in via param block.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# $Request: Contains the request data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# $TriggerMetadata: Contains metadata about the trigger&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$Request&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; $TriggerMetadata&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Log the request body for debugging purposes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Write-Host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Request body type: &lt;/span&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;$Request.Body.GetType&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.Name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Write-Host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Request body content: &lt;/span&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;$Request.Body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ConvertTo-Json&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Depth &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# The body is already deserialized, so we can use it directly&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$requestBody &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; $Request.Body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Check if we received the expected data (StorageAccountName and StorageAccountKey)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$requestBody &lt;/span&gt;&lt;span&gt;-and&lt;/span&gt;&lt;span&gt; $requestBody.ContainsKey&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;StorageAccountName&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-and&lt;/span&gt;&lt;span&gt; $requestBody.ContainsKey&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;StorageAccountKey&apos;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Extract the storage account details from the request body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$storageAccountName &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; $requestBody.StorageAccountName&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$storageAccountKey &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; $requestBody.StorageAccountKey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Generate the SAS token&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# Create a new Azure Storage context using the provided credentials&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$ctx &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;New-AzStorageContext&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;StorageAccountName $storageAccountName &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;StorageAccountKey $storageAccountKey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# Generate a new SAS token with the following parameters:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# - Service: Blob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# - ResourceType: Service, Container, Object&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# - Permission: Create, Read, Write, Delete, List&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# - ExpiryTime: 2 days from now&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$sasToken &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;New-AzStorageAccountSASToken&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Service Blob &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;ResourceType Service&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;Container&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;Object &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Permission &lt;/span&gt;&lt;span&gt;&quot;crwdl&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;ExpiryTime &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Get-Date&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.AddDays&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Context $ctx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# Prepare the success response&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$responseBody &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;message&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;SAS Token generated successfully&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;sasToken&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; $sasToken&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ConvertTo-Json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Write-Host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;SAS Token generated successfully&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# If an error occurs, prepare an error response&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$responseBody &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;error&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Failed to generate SAS Token&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;details&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$_&lt;/span&gt;&lt;span&gt;.Exception.Message&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ConvertTo-Json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Write-Host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Failed to generate SAS Token: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# If the required data is missing or invalid, prepare an error response&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$responseBody &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;error&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Invalid or missing JSON data&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;receivedData&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; $requestBody&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ConvertTo-Json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Write-Host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Invalid or missing JSON data&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Associate values to output bindings by calling &apos;Push-OutputBinding&apos;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# This sends the HTTP response back to the client.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Push-OutputBinding&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Name Response &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Value &lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;HttpResponseContext&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;StatusCode &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;HttpStatusCode&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;::OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Body &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; $responseBody&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Headers &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;Content-Type&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;application/json&apos;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, the code above is commented which should give you a good gist of what happening but basically, we post a &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; file, the body of which looks like the following:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The JSON body (JSON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;StorageAccountName&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&amp;lt;name-of-your-target-storage-account&amp;gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;StorageAccountKey&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&amp;lt;the-primary-key-of-the-storage-account-above&amp;gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It then uses the &lt;code&gt;New-AzStorageAccountSASToken&lt;/code&gt; cmdlet to generate the SAS token. With the updates to the files in place, we can publish the local function to Azure by running the following code:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Deploy to the Function App using the Azure CLI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;func azure functionapp publish $FUNCATION_APP_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once deployed, you can test if it is working as expected using &lt;span&gt;&lt;a href=&quot;https://curl.se/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;cURL&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; locally.&lt;/p&gt;
&lt;h2&gt;Testing the Azure Function App&lt;/h2&gt;
&lt;p&gt;First, let’s re-export our variables adding one that contains the name of the storage account you want to target:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;These commands assume that your user has access to the target storage account.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Set some variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-generate-sas-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export REGION=&quot;uksouth&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export FUNCATION_APP_NAME=&quot;func-generate-sas-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export FUNCATION_APP_STORAGE_ACCOUNT=&quot;safusasuks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export TARGRET_STORAGE_ACCOUNT_NAME=&quot;sabackupuks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We can now use the Azure &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; to grab the primary storage key and use that to populate our JSON payload by running the command below:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Set some variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;json_data=$(cat &amp;lt;&amp;lt;EOF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;StorageAccountName&quot;: &quot;$TARGRET_STORAGE_ACCOUNT_NAME&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;StorageAccountKey&quot;: &quot;$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $TARGRET_STORAGE_ACCOUNT_NAME --query &apos;[0].value&apos; -o tsv)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;EOF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With our JSON body populated, we can now use the Azure CLI to grab the details on the function we have just deployed (i.e. the URL and access key) and then post to JSON body to it by running:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Set some variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl -X POST \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-H &quot;Content-Type: application/json&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-d &quot;$json_data&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;$(az functionapp function show --resource-group $RESOURCE_GROUP_NAME --name &quot;$FUNCATION_APP_NAME&quot; --function-name getSAS --query &quot;invokeUrlTemplate&quot; --output tsv)?code=$(az functionapp function keys list --resource-group $RESOURCE_GROUP_NAME --name &quot;$FUNCATION_APP_NAME&quot; --function-name getSAS --query &quot;default&quot; --output tsv)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It will take a few seconds to return something, but if everything goes as planned you should see something like the following response:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The JSON result (JSON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;SAS Token generated successfully&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sasToken&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;sv=2023-08-03&amp;amp;ss=b&amp;amp;srt=sco&amp;amp;se=2024-07-30T08%3A12%3A06Z&amp;amp;sp=rwdlc&amp;amp;sig=7RsOYC6nEHCT9NaHm4t34RNqSLrt4GRiefZclzkHWgc%3D&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, this worked as expected and returned the signed token which is valid for 48 hours.&lt;/p&gt;
&lt;h2&gt;Adding the Function to your Logic App&lt;/h2&gt;
&lt;p&gt;I am not going to into details of the Logic App here, suffice to say that it needed to be integrated with an Azure Key Vault to store the primary Storage Account key and several other secrets for the service I was interacting with via HTTP requests. The Azure Function App was called using the Function workflow &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/logic-apps/call-azure-functions-from-workflows?tabs=consumption&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;which is detailed here&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. There was a little bit of messing about with the results and getting the right information to construct the URL so here those details:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Getting the information into the LogicApp (JSON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;Parse_JSON_(SAS)&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@outputs(&apos;Generate_SAS_Token&apos;)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;schema&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sasToken&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;object&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Content-Length&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Content-Type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Request-Context&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Transfer-Encoding&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Vary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;object&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;statusCode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;integer&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;object&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Generate_SAS_Token&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ParseJson&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Here is the string I used to generate the fully signed URL:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Calling the third-part API (JSON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;Trigger_the_external_API&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;blob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{variables(&apos;dateTime&apos;)}.somefiletype&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{parameters(&apos;saBlobContainer&apos;)}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;storageAccountSasUri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://@&amp;amp;#123;parameters(&apos;saAccountName&apos;)&amp;amp;#125;.blob.core.windows.net/?@&amp;amp;#123;body(&apos;Parse_JSON_(SAS)&apos;)?[&apos;body&apos;]?[&apos;sasToken&apos;]&amp;amp;#125;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Authorization&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Bearer @{body(&apos;Parse_JSON_(AuthToken)&apos;)?[&apos;access_token&apos;]}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Content-Type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;application/json&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;POST&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&amp;lt;some URL for an external endpoint&amp;gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Parse_JSON_(SAS)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;What started as a “straightforward five-minute job” turned into an insightful journey through Azure’s intricacies. While the Azure Blob connector’s built-in “Create SAS URI by path (V2)” action seemed promising initially, it fell short of providing the specific SAS token required by the external service. This challenge led us to explore alternative solutions, ultimately landing on a robust and flexible approach using an Azure Function App.&lt;/p&gt;
&lt;p&gt;By leveraging PowerShell within an Azure Function, we created a custom solution that generates the exact SAS token needed, complete with the required ‘Blob’ service access. This approach not only solved our immediate problem but also provided several advantages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Flexibility: The function can be easily modified to accommodate different SAS token requirements in the future.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/glossary/security/&quot;&gt;Security&lt;/a&gt;: By using Azure Key Vault in conjunction with the Logic App, we maintained secure storage and access to sensitive information like storage account keys.&lt;/li&gt;
&lt;li&gt;Reusability: The function can be called from various Logic Apps or other Azure services, making it a versatile tool in your Azure toolkit.&lt;/li&gt;
&lt;li&gt;Scalability: Being &lt;a href=&quot;/glossary/serverless/&quot;&gt;serverless&lt;/a&gt;, the Function App can handle varying loads without manual intervention.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This solution demonstrates the power of combining different Azure services to overcome limitations and create tailored solutions.  Remember, when working with Azure or any complex system, what seems like a simple task can often lead to valuable learning experiences and innovative solutions. Embrace these challenges as opportunities to deepen your understanding and expand your Azure expertise.&lt;/p&gt;
&lt;p&gt;As you implement this solution in your projects, consider documenting any additional hurdles you encounter and the solutions you devise. I hope you found this useful and also that appears high in your search results so don’t have to spend lots of frustrating hours scratching your head like I did.&lt;/p&gt;
&lt;h2&gt;Audio Summary&lt;/h2&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2024-07-27-creating-an-azure-storage-account-sas-token-from-a-logic-app/2024-07-27-creating-an-azure-storage-account-sas-token-from-a-logic-app.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-07-27-creating-an-azure-storage-account-sas-token-from-a-logic-app.CtkDIwh6.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/07/27/generating-an-azure-storage-account-sas-token-using-azure-logic-and-function-apps-og.png</blog:ogImage><blog:readingTime>10</blog:readingTime><category>Azure</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Azure Firewall KQL Query</title><link>https://www.russ.cloud/2024/07/20/azure-firewall-kql-query/</link><guid isPermaLink="true">https://www.russ.cloud//2024/07/20/azure-firewall-kql-query/</guid><description>Explore a powerful KQL query for Azure Firewall logs. Learn how to analyze network traffic, filter by source and destination IP, and gain insights into your Azure Firewall&apos;s performance and security.</description><pubDate>Sat, 20 Jul 2024 12:42:01 GMT</pubDate><content:encoded>&lt;p&gt;This is a quick post to share a useful &lt;a href=&quot;/glossary/kql/&quot;&gt;KQL&lt;/a&gt; query for &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; Firewalls that I’ve been using. It’s based on the default Firewall query from Microsoft, but with some added variables to allow for a more focused analysis of source and destination traffic. I’m putting it here for &lt;a href=&quot;/about&quot;&gt;my&lt;/a&gt; future reference and in case it’s helpful for anyone else.&lt;/p&gt;
&lt;h2&gt;Azure Firewall Query with Variables&lt;/h2&gt;
&lt;p&gt;This query allows you to analyze &lt;a href=&quot;/glossary/azure-firewall/&quot;&gt;Azure Firewall&lt;/a&gt; logs, focusing on specific source and destination IP addresses within a defined time range. It’s particularly useful for troubleshooting and &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; analysis.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Query (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;timeAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;15m&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// how far back to look in the logs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sourceIP &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;192.168.0.1&quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// the source IP address&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;targetIP &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;10.0.0.1&quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// the desination IP address&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;theLimit &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// number of results to return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;AzureDiagnostics&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; Category == &lt;/span&gt;&lt;span&gt;&quot;AzureFirewallNetworkRule&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;or&lt;/span&gt;&lt;span&gt; Category == &lt;/span&gt;&lt;span&gt;&quot;AzureFirewallApplicationRule&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt; msg_original = msg_s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&apos;. Action: Deny. Reason: SNI TLS extension was missing.&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;@&apos; to no_data:no_data. Action: Deny. Rule Collection: default behavior. Rule: SNI TLS extension missing&apos;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&apos;No rule matched. Proceeding with default action&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;@&apos;Rule Collection: default behavior. Rule: no rule matched&apos;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt; msg_s &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;&quot; Web Category: &quot;&lt;/span&gt;&lt;span&gt; WebCategory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&apos;(. Web Category:).*&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt; msg_s &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;&quot;. Rule Collection: &quot;&lt;/span&gt;&lt;span&gt; RuleCollection &lt;/span&gt;&lt;span&gt;&quot;. Rule: &quot;&lt;/span&gt;&lt;span&gt; Rule&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&apos;(. Rule Collection:).*&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt; msg_s &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;&quot;. Rule Collection Group: &quot;&lt;/span&gt;&lt;span&gt; RuleCollectionGroup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&apos;(. Rule Collection Group:).*&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt; msg_s &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;&quot;. Policy: &quot;&lt;/span&gt;&lt;span&gt; Policy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&apos;(. Policy:).*&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt; msg_s &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;&quot;. Signature: &quot;&lt;/span&gt;&lt;span&gt; IDSSignatureIDInt &lt;/span&gt;&lt;span&gt;&quot;. IDS: &quot;&lt;/span&gt;&lt;span&gt; IDSSignatureDescription &lt;/span&gt;&lt;span&gt;&quot;. Priority: &quot;&lt;/span&gt;&lt;span&gt; IDSPriorityInt &lt;/span&gt;&lt;span&gt;&quot;. Classification: &quot;&lt;/span&gt;&lt;span&gt; IDSClassification&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&apos;(. Signature:).*&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt; msg_s &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;&quot; was DNAT&apos;ed to &quot;&lt;/span&gt;&lt;span&gt; NatDestination&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&quot;( was DNAT&apos;ed to ).*&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;. Action: DNAT&quot;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt; msg_s &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;&quot;. ThreatIntel: &quot;&lt;/span&gt;&lt;span&gt; ThreatIntel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s = &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&apos;(. ThreatIntel:).*&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt; URL = &lt;/span&gt;&lt;span&gt;extract&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@&quot;(Url: )(.*)(\. Action)&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; msg_s=&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&quot;(Url: .*)(Action)&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;@&quot;\2&quot;&lt;/span&gt;&lt;span&gt;, msg_s)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt; msg_s &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; Protocol &lt;/span&gt;&lt;span&gt;&quot; request from &quot;&lt;/span&gt;&lt;span&gt; SourceIP &lt;/span&gt;&lt;span&gt;&quot; to &quot;&lt;/span&gt;&lt;span&gt; Target &lt;/span&gt;&lt;span&gt;&quot;. Action: &quot;&lt;/span&gt;&lt;span&gt; Action&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SourceIP = &lt;/span&gt;&lt;span&gt;iif&lt;/span&gt;&lt;span&gt;(SourceIP &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;strcat_array&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(SourceIP, &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;), SourceIP),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SourcePort = &lt;/span&gt;&lt;span&gt;iif&lt;/span&gt;&lt;span&gt;(SourceIP &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;strcat_array&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(SourceIP, &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Target = &lt;/span&gt;&lt;span&gt;iif&lt;/span&gt;&lt;span&gt;(Target &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;strcat_array&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Target, &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;), Target),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TargetPort = &lt;/span&gt;&lt;span&gt;iif&lt;/span&gt;&lt;span&gt;(SourceIP &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;strcat_array&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Target, &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Action = &lt;/span&gt;&lt;span&gt;iif&lt;/span&gt;&lt;span&gt;(Action &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;.&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;strcat_array&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Action, &lt;/span&gt;&lt;span&gt;&quot;.&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;), Action),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Policy = &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt;(RuleCollection &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(RuleCollection, &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;], Policy),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;RuleCollectionGroup = &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt;(RuleCollection &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(RuleCollection, &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;], RuleCollectionGroup),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;RuleCollection = &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt;(RuleCollection &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(RuleCollection, &lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;], RuleCollection),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IDSSignatureID = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(IDSSignatureIDInt),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IDSPriority = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(IDSPriorityInt)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;msg_original,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TimeGenerated,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Protocol,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SourceIP,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SourcePort,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Target,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TargetPort,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;URL,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Action,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;NatDestination,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;OperationName,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ThreatIntel,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IDSSignatureID,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IDSSignatureDescription,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IDSPriority,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IDSClassification,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Policy,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;RuleCollectionGroup,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;RuleCollection,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Rule,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;WebCategory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; TimeGenerated&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(timeAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; Target == targetIP &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; SourceIP == sourceIP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| limit theLimit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Key Features of the Query&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Variable Declarations&lt;/strong&gt;: The query starts with variables for time range, source IP, target IP, and result limit. This makes it easy to customize without changing the main query logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log Filtering&lt;/strong&gt;: It focuses on Azure Firewall Network and Application rules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Parsing&lt;/strong&gt;: The query uses a series of &lt;code&gt;parse&lt;/code&gt; and &lt;code&gt;extend&lt;/code&gt; operations to extract structured information from the log messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Field Extraction&lt;/strong&gt;: Important fields like Protocol, Source/Target IP and Port, URL, Action, and various rule details are extracted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Filtering&lt;/strong&gt;: The query filters results based on the specified source and target IP addresses.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;How to Use This Query&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Adjust the &lt;code&gt;timeAgo&lt;/code&gt;, &lt;code&gt;sourceIP&lt;/code&gt;, &lt;code&gt;targetIP&lt;/code&gt;, and &lt;code&gt;theLimit&lt;/code&gt; variables at the beginning of the query to suit your needs.&lt;/li&gt;
&lt;li&gt;Run the query in your Azure Log Analytics workspace.&lt;/li&gt;
&lt;li&gt;The results will show you detailed information about firewall actions, including source and destination details, protocols used, and the specific rules applied.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;This query has been a handy tool for me when working with Azure Firewall logs. It’s particularly useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Troubleshooting connectivity issues between specific hosts&lt;/li&gt;
&lt;li&gt;Investigating potential security incidents&lt;/li&gt;
&lt;li&gt;Auditing Firewall Rule Effectiveness&lt;/li&gt;
&lt;li&gt;Monitoring traffic patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember, this is just a starting point. Feel free to modify the query to better fit your specific needs. As you work more with Azure Firewall and KQL, you’ll probably come up with your own variations that work even better for your environment.&lt;/p&gt;
&lt;p&gt;A few final thoughts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep an eye on performance, especially if you’re looking at a large time range or have a busy environment.&lt;/li&gt;
&lt;li&gt;Always test your queries, especially after making modifications.&lt;/li&gt;
&lt;li&gt;Don’t forget to update the placeholders (like &lt;code&gt;sourceIP&lt;/code&gt; and &lt;code&gt;targetIP&lt;/code&gt;) with actual values when you use this query.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hopefully, this query will save future me, (👋 Hello again future &lt;a href=&quot;/about&quot;&gt;Russ&lt;/a&gt;, and maybe you sometime when digging into Azure Firewall logs.&lt;/p&gt;
&lt;p&gt;Happy querying!&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-07-20-azure-firewall-kql-query.CA8M4Xjg.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/07/20/azure-firewall-kql-query-og.png</blog:ogImage><blog:readingTime>5</blog:readingTime><category>azure</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Azure Virtual Desktop KQL Queries</title><link>https://www.russ.cloud/2024/07/07/azure-virtual-desktop-kql-queries/</link><guid isPermaLink="true">https://www.russ.cloud//2024/07/07/azure-virtual-desktop-kql-queries/</guid><description>Discover powerful KQL queries to enhance your Azure Virtual Desktop (AVD) management. Learn how to track user connections, analyze session times, monitor errors, and gain insights into your AVD environment&apos;s performance and security.</description><pubDate>Sun, 07 Jul 2024 14:52:12 GMT</pubDate><content:encoded>&lt;p&gt;This is a quick post collating some of the notes for &lt;a href=&quot;/glossary/kql/&quot;&gt;KQL&lt;/a&gt; queries for &lt;a href=&quot;/glossary/azure-virtual-desktop/&quot;&gt;Azure Virtual Desktop&lt;/a&gt; I have in various documents into a single place, just in case they are helpful for anyone and also for my reference, 👋 hello future &lt;a href=&quot;/about&quot;&gt;Russ&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Find out all the IP addresses in the last 30 days&lt;/h2&gt;
&lt;p&gt;This query looks at the past 30 days, counting connections by username and client IP address, then sorts these counts in descending order, adds geolocation data for each IP, and finally outputs the client IP, connection count, and location details (country, state, and city) to provide insights into connection patterns and frequencies across different users and locations in the Windows Virtual Desktop environment.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Find out all the IP addresses in the last 30 days (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;30d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WVDConnections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt; NumberOfConnections = &lt;/span&gt;&lt;span&gt;count()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; UserName, ClientIPAddress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; NumberOfConnections &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; ip_location = &lt;/span&gt;&lt;span&gt;parse_json&lt;/span&gt;&lt;span&gt;(geo_info_from_ip_address(ClientIPAddress))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Country = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.country),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;State = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.state),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;City = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.city)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; ClientIPAddress, NumberOfConnections, Country, State, City&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Find out all Users and their IP addresses in the last 30 days&lt;/h2&gt;
&lt;p&gt;This query extends the previous query and analyses AVD connections over the last 30 days, counting connections by username and client IP address, sorting these counts in descending order, enriching the data with geo-location information for each IP, and finally outputting the username, client IP, connection count, and location details (country, state, and city). This provides a comprehensive view of the Azure Virtual Desktop environment’s connection patterns, frequencies, and geographical distribution for each user.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Find out all Users and their IP addresses in the last 30 days (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;30d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WVDConnections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt; NumberOfConnections = &lt;/span&gt;&lt;span&gt;count()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; UserName, ClientIPAddress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; NumberOfConnections &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; ip_location = &lt;/span&gt;&lt;span&gt;parse_json&lt;/span&gt;&lt;span&gt;(geo_info_from_ip_address(ClientIPAddress))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Country = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.country),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;State = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.state),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;City = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.city)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; UserName, ClientIPAddress, NumberOfConnections, Country, State, City&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Find out the IP addresses of where a user is connecting from&lt;/h2&gt;
&lt;p&gt;This KQL query is similar to the previous ones but introduces a new variable and focuses on filtering by username. Here’s a summary:
The query analyses WVDConnections over the past 30 days, filters for usernames containing a specified search string (currently empty), and counts connections by client IP address. It orders these counts in descending order, adds geolocation data for each IP, and outputs the client IP, connection count, and location details (country, state, and city).&lt;/p&gt;
&lt;p&gt;This query allows for targeted analysis of connection patterns for specific users or user groups, providing insights into the geographical distribution of their connections to the Windows Virtual Desktop environment.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Find out the IP addresses of where a user is connecting from (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;userSearch &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&amp;lt;replace with the UPN of a user&amp;gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;30d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WVDConnections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; UserName &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; userSearch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt; NumberOfConnections = &lt;/span&gt;&lt;span&gt;count()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; ClientIPAddress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; NumberOfConnections &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt;&lt;span&gt; ip_location = &lt;/span&gt;&lt;span&gt;parse_json&lt;/span&gt;&lt;span&gt;(geo_info_from_ip_address(ClientIPAddress))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Country = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.country),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;State = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.state),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;City = &lt;/span&gt;&lt;span&gt;tostring&lt;/span&gt;&lt;span&gt;(ip_location.city)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; ClientIPAddress, NumberOfConnections, Country, State, City&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Get all errors for all users from a single IP address&lt;/h2&gt;
&lt;p&gt;The query first identifies users connected from a specified IP address (currently set to an empty string) within the last 30 days using the WVDConnections table. It then uses this list of users to filter the WVDErrors table, summarising the count of errors for each user and the error code. The results are ordered by error count in descending order. This query allows for targeted error analysis, linking connection data with error occurrences to help identify potential issues affecting users connecting from specific IP addresses in the Windows Virtual Desktop environment.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Get all errors for all users from a single IP address (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ipAddress &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&amp;lt;replace with the IP address you are interested in&amp;gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;30d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;users &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;WVDConnections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; ClientSideIPAddress &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; ipAddress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; UserName;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WVDErrors&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; UserName &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; (users)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt; ErrorCount = &lt;/span&gt;&lt;span&gt;count()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; UserName, CodeSymbolic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; ErrorCount &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Total session time&lt;/h2&gt;
&lt;p&gt;This KQL query analyses Azure Virtual Desktop connections over the past month, matching ‘Connected’ and ‘Completed’ session states to calculate total session durations for each user and connection type, then presents the results in both hours and days, sorted to show the most active users first, thereby providing insights into user engagement patterns and system usage within the WVD environment.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Total session time (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;31d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WVDConnections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; State == &lt;/span&gt;&lt;span&gt;&quot;Connected&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; CorrelationId, UserName, ConnectionType, StartTime=TimeGenerated&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt; (WVDConnections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; State == &lt;/span&gt;&lt;span&gt;&quot;Completed&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; EndTime=TimeGenerated, CorrelationId)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt; CorrelationId&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt; SessionDuration = EndTime - StartTime&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt; TotalDuration = &lt;/span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt;(SessionDuration) &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; UserName, ConnectionType&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DurationHours = &lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;(TotalDuration / &lt;/span&gt;&lt;span&gt;1h&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DurationDays = &lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;(TotalDuration / &lt;/span&gt;&lt;span&gt;1d&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; UserName, ConnectionType, DurationHours, DurationDays&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; DurationHours &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Total number of sessions per host pool&lt;/h2&gt;
&lt;p&gt;This KQL query analyses Windows Virtual Desktop connections from the past 31 days, focusing on successful connections to count distinct users per host pool, then formats the host pool names by combining specific segments of the resource ID, ultimately providing a concise summary of user diversity across different WVD host pools.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Total number of sessions per host pool (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;31d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WVDConnections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; State == &lt;/span&gt;&lt;span&gt;&quot;Connected&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; _ResourceId, UserName&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project-rename&lt;/span&gt;&lt;span&gt; Hostpool = _ResourceId&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt; DistinctUsers= &lt;/span&gt;&lt;span&gt;dcount&lt;/span&gt;&lt;span&gt;(UserName) &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Hostpool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt; HostPool=&lt;/span&gt;&lt;span&gt;toupper&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;strcat&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Hostpool, &lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;&quot;.&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Hostpool, &lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;]))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; HostPool, DistinctUsers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Total Sessions&lt;/h2&gt;
&lt;p&gt;This KQL query analyses Windows Virtual Desktop connections from the past day, focusing on completed sessions, and extracts key session information including timestamp, user, client details, connection type, and a reformatted host pool identifier, providing a detailed snapshot of recent WVD activity across different host pools with an emphasis on successfully concluded sessions.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Total Sessions (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WVDConnections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo) &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; State &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Completed&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project-rename&lt;/span&gt;&lt;span&gt; Hostpool = _ResourceId&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt; HostPool=&lt;/span&gt;&lt;span&gt;toupper&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;strcat&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Hostpool, &lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;&quot;.&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Hostpool, &lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;]))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; TimeGenerated, UserName, ClientOS, ClientType, ConnectionType, HostPool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Errors per host pool&lt;/h2&gt;
&lt;p&gt;This KQL query examines Azure Virtual Desktop errors from the past day, extracting the error code and reformatting the host pool identifier from the resource ID, then summarises the data by counting the occurrences of each error code per host pool, providing a concise overview of recent error patterns across different AVD environments.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Errors per host pool (KQL)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daysAgo &lt;/span&gt;&lt;span&gt;= &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WVDErrors&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; TimeGenerated &amp;gt; &lt;/span&gt;&lt;span&gt;ago&lt;/span&gt;&lt;span&gt;(daysAgo)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project&lt;/span&gt;&lt;span&gt; _ResourceId, CodeSymbolic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;project-rename&lt;/span&gt;&lt;span&gt; Hostpool = _ResourceId&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt; HostPool=&lt;/span&gt;&lt;span&gt;toupper&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;strcat&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Hostpool, &lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;&quot;.&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(Hostpool, &lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;]))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt; Count=&lt;/span&gt;&lt;span&gt;count()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; CodeSymbolic, HostPool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;So there you have it - a handful of KQL queries I’ve found useful when working with Azure Virtual Desktop. These aren’t exhaustive by any means, just a collection of queries I’ve used and wanted to keep handy.&lt;/p&gt;
&lt;p&gt;Remember, these queries are starting points. Feel free to tweak them to fit your specific needs. As you work more with AVD and KQL, you’ll probably come up with your own variations that work even better for your environment.&lt;/p&gt;
&lt;p&gt;A few final thoughts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep an eye on performance. Some of these queries look at 30 days of data, which might be heavy if you have a large environment.&lt;/li&gt;
&lt;li&gt;Always test your queries, especially if you modify them.&lt;/li&gt;
&lt;li&gt;Don’t forget to update the placeholders (like &lt;code&gt;&amp;lt;replace with the IP address you are interested in&amp;gt;&lt;/code&gt;) with actual values when you use these queries.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hopefully, these notes will save future me (and maybe you) time when digging into AVD logs. If you’ve got any cool AVD queries of your own, I’d love to hear about them in the comments.&lt;/p&gt;
&lt;p&gt;Happy querying!&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-07-07-avd-kql-query-notes.DnjD5uxX.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/07/07/azure-virtual-desktop-kql-queries-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>azure</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Azure DevOps Ansible Pipeline; Boosting Efficiency with Caching</title><link>https://www.russ.cloud/2024/06/28/azure-devops-ansible-pipeline-boosting-efficiency-with-caching/</link><guid isPermaLink="true">https://www.russ.cloud//2024/06/28/azure-devops-ansible-pipeline-boosting-efficiency-with-caching/</guid><description>Discover how to optimize your Azure DevOps Ansible pipeline with caching techniques. Learn to reduce execution time, improve efficiency, and maintain security checks in your infrastructure as code deployments.</description><pubDate>Fri, 28 Jun 2024 13:24:15 GMT</pubDate><content:encoded>&lt;p&gt;Back in May 2020 I published what would be one of the more popular posts on this blog &lt;a href=&quot;/2020/05/10/azure-devops-ansible-pipeline/&quot;&gt;Azure DevOps Ansible Pipeline&lt;/a&gt;, it covered the &lt;a href=&quot;/glossary/azure-devops/&quot;&gt;Azure DevOps&lt;/a&gt; pipeline I had settled upon for running my &lt;a href=&quot;/glossary/ansible/&quot;&gt;Ansible&lt;/a&gt; playbooks which built and interacted with workloads running in &lt;a href=&quot;/tags/azure/&quot;&gt;Microsoft Azure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fast forward four years, I updated the pipeline for Chapter 15 of the second edition of &lt;span&gt;&lt;a href=&quot;https://www.packtpub.com/en-gb/product/learn-ansible-9781835088913&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Learn Ansible&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;; this version of the pipeline, which is covered in detail in the book, can be found in the repo linked below;&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/PacktPublishing/Learn-Ansible-Second-Edition/blob/main/Chapter15/azure-pipelines.yml&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;&lt;img src=&quot;/assets/link-previews/github.com-13db8d82d6b5-favicon.svg&quot; alt=&quot;&quot; width=&quot;20&quot; height=&quot;20&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;Learn-Ansible-Second-Edition/Chapter15/azure-pipelines.yml at main · PacktPublishing/Learn-Ansible-Second-Edition&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Learn Ansible, Second Edition, published by Packt. Contribute to PacktPublishing/Learn-Ansible-Second-Edition development by creating an account on GitHub.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;After the book was published, it occurred to me that something was missing from the pipeline, and that is caching !!!&lt;/p&gt;
&lt;h2&gt;The Learn Ansible Pipeline&lt;/h2&gt;
&lt;p&gt;For those of you who haven’t read the second edition of &lt;span&gt;&lt;a href=&quot;https://www.packtpub.com/en-us/product/learn-ansible-9781835088913&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Learn Ansible&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, this updated Azure DevOps pipeline automates the secure deployment of Ansible playbooks by first running a &lt;span&gt;&lt;a href=&quot;https://kics.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;KICS&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; (Keeping &lt;a href=&quot;/glossary/infrastructure-as-code/&quot;&gt;Infrastructure as Code&lt;/a&gt; Secure) scan to identify potential &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; vulnerabilities in the infrastructure code. It then parses the scan results to determine whether it’s safe to proceed with the deployment, setting a flag to either continue or halt the pipeline based on the severity of any issues found.&lt;/p&gt;
&lt;p&gt;If the security checks pass, the pipeline sets up the necessary &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; credentials, installs the required Ansible collections and dependencies, and finally executes the Ansible playbook, capturing and publishing the deployment output for easy review.&lt;/p&gt;
&lt;p&gt;The only downside was that it could take quite a while to execute. To circumvent this for another project I am working on, I have added a small level of caching to the pipeline code.&lt;/p&gt;
&lt;h2&gt;Introducing Caching&lt;/h2&gt;
&lt;p&gt;All of the work to introduce caching happens in the Run Ansible stage of the pipeline; the first of the changes happens just after the task, which sets up the Azure &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; and exposes the service principles credentials as environment variables.&lt;/p&gt;
&lt;p&gt;Before we get there, though, a few additional variables are introduced at the top of the file. These are …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Variables (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PATH_COLLECTION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/vsts/.ansible/collections/ansible_collections/azure/azcollection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PATH_VENV&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$(Pipeline.Workspace)/venv&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CACHE_VERSION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v001&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;REQUIREMENTS_FILE_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;requirements.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;While they mainly deal with paths, the last one is important as the Azure Collection maintainers at Microsoft kindly changed the name of the requirements file we are using to install the &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; modules we need for the pipeline to work. This changed the name of the requirements file, which broke all the builds I had that used it; thanks for that grrrrrrr 😠.&lt;/p&gt;
&lt;p&gt;Also, as a bonus, should you ever need to update the cache, change the &lt;code&gt;CACHE_VERSION&lt;/code&gt;￼ variable, which will force a new cache to be created when the pipeline subsequently runs.&lt;/p&gt;
&lt;h3&gt;Set up the virtual environment.&lt;/h3&gt;
&lt;p&gt;This task configures a &lt;a href=&quot;/glossary/virtual-environment/&quot;&gt;virtual environment&lt;/a&gt;; this is important as we will be taking a copy of the virtual environment for future runs of the pipelines:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Set up the virtual environment (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bash@3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;setup_environment&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;displayName&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Setup Python Environment&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;targetType&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;inline&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[group]Setup Python Virtual Environment&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;python3 -m venv $(PATH_VENV)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source $(PATH_VENV)/bin/activate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pip install --upgrade pip&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[endgroup]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The next task is as per the original pipeline, which grabs a copy of the private and public SSH keys and then copies them to the host running the Azure DevOps &lt;a href=&quot;/glossary/agent/&quot;&gt;agent&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Check if there is an active cache&lt;/h3&gt;
&lt;p&gt;Next up, we have the &lt;code&gt;Cache@2&lt;/code&gt; task; with this task, we both check to see if there is already an active cache and if there isn’t, at the end of the pipeline run, a cache will be created will save us time and network bandwidth by caching files between pipeline runs.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Check if there is an active cache (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cache@2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;cache_venv&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;displayName&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Cache virtual environment&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;venv$(CACHE_VERSION) | &quot;$(Agent.OS)&quot; | $(PATH_COLLECTION)/$(REQUIREMENTS_FILE_NAME)&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$(PATH_VENV)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;cacheHitVar&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CACHE_RESTORED&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The inputs needed for the task to run are;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Key&lt;/strong&gt;: The &lt;code&gt;key&lt;/code&gt; input is crucial for identifying the cache. It’s composed of:
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;CACHE_VERSION&lt;/code&gt; variable (allowing you to invalidate all caches by changing this version)&lt;/li&gt;
&lt;li&gt;The operating system of the agent (&lt;code&gt;Agent.OS&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The path to the requirements file (&lt;code&gt;PATH_COLLECTION/REQUIREMENTS_FILE_NAME&lt;/code&gt;)
This combination ensures that the cache is specific to the current environment and dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path&lt;/strong&gt;: The &lt;code&gt;path&lt;/code&gt; input specifies what should be cached, in this case, the virtual environment directory (&lt;code&gt;PATH_VENV&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache Hit Variable&lt;/strong&gt;: The &lt;code&gt;cacheHitVar&lt;/code&gt; is set to &lt;code&gt;CACHE_RESTORED&lt;/code&gt;. This variable will be set to ‘true’ if the cache is successfully restored, allowing subsequent tasks to know whether they need to reinstall dependencies or can use the cached version.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Install the requirements if there is no active cache&lt;/h3&gt;
&lt;p&gt;This next task is responsible for installing dependencies when the cache isn’t available.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install the requirements if there is no active cache (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bash@3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;install_dependencies&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;displayName&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Install Dependencies&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;condition&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ne(variables.CACHE_RESTORED, &apos;true&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;targetType&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;inline&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[group]Install pip requirements&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source $(PATH_VENV)/bin/activate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pip install ansible[azure]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible-galaxy collection install --force azure.azcollection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pip install -r $(PATH_COLLECTION)/$(REQUIREMENTS_FILE_NAME)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pip freeze &amp;gt; $(PATH_VENV)/$(REQUIREMENTS_FILE_NAME)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[endgroup]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Here’s an explanation of what’s happening:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Task Type: It’s using the &lt;code&gt;Bash@3&lt;/code&gt; task, which allows running Bash scripts in the pipeline.&lt;/li&gt;
&lt;li&gt;Purpose: The task installs necessary dependencies, including Ansible and its Azure-related packages, as well as any other requirements specified in a requirements file.&lt;/li&gt;
&lt;li&gt;Condition: The &lt;code&gt;condition&lt;/code&gt; field specifies that this task only runs if &lt;code&gt;CACHE_RESTORED&lt;/code&gt; is not ‘true’. This means it will skip the installation if the previous caching task successfully restored the virtual environment.&lt;/li&gt;
&lt;li&gt;Script Content:
&lt;ul&gt;
&lt;li&gt;It activates the virtual environment.&lt;/li&gt;
&lt;li&gt;Installs Ansible with Azure support using pip.&lt;/li&gt;
&lt;li&gt;Uses ansible-galaxy to install the Azure collection, forcing an update if it already exists.&lt;/li&gt;
&lt;li&gt;Installs additional requirements from a specified requirements file.&lt;/li&gt;
&lt;li&gt;Generates a new requirements file based on the current state of the virtual environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Output Grouping: The &lt;code&gt;##[group]&lt;/code&gt; and &lt;code&gt;##[endgroup]&lt;/code&gt; syntax is used to group the output in Azure DevOps, making the logs more readable.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This task ensures that all necessary dependencies are installed when the cache isn’t available or is outdated. By conditionally running this task, we are instructing the pipeline to use cached dependencies when possible, falling back to a fresh installation only when needed. This approach balances between speed (using cached dependencies) and reliability (ensuring up-to-date packages when necessary).&lt;/p&gt;
&lt;h3&gt;Run the Ansible Playbook.&lt;/h3&gt;
&lt;p&gt;This task is responsible for running the Ansible playbook and handling its output; there have been some changes to the task since the Learn Ansible version, these changes load the virtual environment and also confirm that the Azure modules are available:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Run the Ansible Playbook (YAML)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bash@3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ansible&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;displayName&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Run Ansible&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;AZURE_CLIENT_ID&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$(ARM_CLIENT_ID)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;AZURE_SECRET&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$(ARM_CLIENT_SECRET)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;AZURE_TENANT&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$(ARM_TENANT_ID)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;AZURE_SUBSCRIPTION_ID&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$(ARM_SUBSCRIPTION_ID)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;ANSIBLE_HOST_KEY_CHECKING&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;False&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;targetType&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;inline&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[group]Add SSH key&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;$(SSH_PUBLIC_KEY)&quot; &amp;gt; ~/.ssh/id_rsa.pub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;chmod 644 ~/.ssh/id_rsa.pub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[endgroup]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[group]Verify Azure module installation&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source $(PATH_VENV)/bin/activate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;python -c &quot;import azure; print(&apos;Azure module is available&apos;)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[endgroup]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[group]Run the Ansible Playbook&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible-playbook -i inv site.yml 2&amp;gt;&amp;amp;1 | tee $(System.DefaultWorkingDirectory)/ansible_output.log&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[endgroup]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[group]Create the markdown file for the Ansible Playbook Output&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mkdir -p $(System.DefaultWorkingDirectory)/markdown&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;# Ansible Playbook Output&quot; &amp;gt; $(System.DefaultWorkingDirectory)/markdown/summary.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;&amp;lt;details&amp;gt;&amp;lt;summary&amp;gt;Click to expand&amp;lt;/summary&amp;gt;&quot; &amp;gt;&amp;gt; $(System.DefaultWorkingDirectory)/markdown/summary.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;&quot; &amp;gt;&amp;gt; $(System.DefaultWorkingDirectory)/markdown/summary.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;\`\`\`&quot; &amp;gt;&amp;gt; $(System.DefaultWorkingDirectory)/markdown/summary.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cat $(System.DefaultWorkingDirectory)/ansible_output.log &amp;gt;&amp;gt; $(System.DefaultWorkingDirectory)/markdown/summary.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;\`\`\`&quot; &amp;gt;&amp;gt; $(System.DefaultWorkingDirectory)/markdown/summary.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;&amp;lt;/details&amp;gt;&quot; &amp;gt;&amp;gt; $(System.DefaultWorkingDirectory)/markdown/summary.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;##[endgroup]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Let’s take a deeper dive; as you can see, it uses the &lt;code&gt;Bash@3&lt;/code&gt; task to execute a series of bash commands. The task begins by setting up Azure-related environment variables for authentication and disables Ansible host key checking for non-interactive execution.&lt;/p&gt;
&lt;p&gt;The script content is divided into several sections. First, it sets up an &lt;a href=&quot;/glossary/ssh-key/&quot;&gt;SSH key&lt;/a&gt; by adding a public key to the pipeline environment and setting appropriate permissions. Next, it verifies the Azure module’s availability by activating the virtual environment and running a Python command to import the Azure module.&lt;/p&gt;
&lt;p&gt;The core of this task is executing the Ansible playbook. It runs the &lt;code&gt;site.yml&lt;/code&gt; playbook using a specified inventory file (&lt;code&gt;inv&lt;/code&gt;) and captures the output in both the console and a log file. This step likely configures or manages Azure resources as defined in the playbook.&lt;/p&gt;
&lt;p&gt;Following the playbook execution, the task creates a &lt;a href=&quot;/glossary/markdown/&quot;&gt;markdown&lt;/a&gt; summary of the Ansible output. It generates a markdown file with an expandable section containing the full Ansible output within code blocks. This summary allows quick access to the Ansible output directly in the Azure DevOps UI, particularly useful for troubleshooting or auditing pipeline runs.&lt;/p&gt;
&lt;p&gt;Throughout the script, the task uses &lt;code&gt;##[group]&lt;/code&gt; and &lt;code&gt;##[endgroup]&lt;/code&gt; commands to organize the pipeline logs into clear, collapsible sections. This improves readability in the Azure DevOps interface.&lt;/p&gt;
&lt;h2&gt;Seeing it in action&lt;/h2&gt;
&lt;p&gt;As you can see from the screens below, the first run took nearly 6 minutes; however, the second run took nearly half the time and was just under 3 minutes now that the cache is in place:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/01-no-cache-overview.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/01-no-cache-overview.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/02-cache-miss.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/02-cache-miss.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/03-Install Dependencies.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/03-Install Dependencies.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/04-cache-run.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/04-cache-run.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/05-cache-hit.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/05-cache-hit.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/06-Install Dependencies-Skipped.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/06-Install Dependencies-Skipped.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/07-side-by-side.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-06-28-azure-devops-ansible-pipeline-updated/images/cache/07-side-by-side.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The introduction of caching in our Azure DevOps Ansible pipeline has significantly improved its efficiency and reduced execution time. By implementing the &lt;code&gt;Cache@2&lt;/code&gt; task and conditionally installing dependencies, we’ve created a pipeline that balances speed and reliability. This optimization not only saves time but also reduces network bandwidth usage, making our &lt;a href=&quot;/glossary/devops/&quot;&gt;DevOps&lt;/a&gt; processes more streamlined and cost-effective.&lt;/p&gt;
&lt;p&gt;You can find the full code below:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://github.com/russmckendrick/DevOpsAnsiblePipeline2024&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;GitHub - russmckendrick/DevOpsAnsiblePipeline2024: Discover how to optimize your Azure DevOps Ansible pipeline with caching techniques. Learn to reduce execution time, improve efficiency, and maintain security checks in your infrastructure as code deployments.&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Discover how to optimize your Azure DevOps Ansible pipeline with caching techniques. Learn to reduce execution time, improve efficiency, and maintain security checks in your infrastructure as code ...&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;github.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/github.com-f55df50f56be.jpg&quot; alt=&quot;Discover how to optimize your Azure DevOps Ansible pipeline with caching techniques. Learn to reduce execution time, improve efficiency, and maintain security checks in your infrastructure as code ...&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;Further reading and links&lt;/h2&gt;
&lt;p&gt;You can buy the second edition of Learn Ansible from the link below:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.packtpub.com/en-gb/product/learn-ansible-9781835088913&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;packtpub.com&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;packtpub.com&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Read the original blog post here:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://www.russ.cloud/2020/05/10/azure-devops-ansible-pipeline/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Azure DevOps Ansible Pipeline&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Streamline your DevOps workflows by setting up an Azure pipeline using Ansible playbooks. Learn how to integrate Azure CLI for faster results!&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;russ.cloud&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/russ.cloud-b817bd4d39bd.jpg&quot; alt=&quot;Azure DevOps Ansible Pipeline&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Find out more on the &lt;code&gt;Cache@2&lt;/code&gt; task at:&lt;/p&gt;
&lt;div class=&quot;link-preview-card my-6 overflow-hidden&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/cache-v2?view=azure-pipelines&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;div class=&quot;flex-1 p-4 flex flex-col justify-center min-w-0&quot;&gt;&lt;div class=&quot;link-preview-title font-bold line-clamp-2 flex items-center gap-2&quot;&gt;Cache@2 - Cache v2 task&lt;/div&gt;&lt;div class=&quot;link-preview-description mt-1 text-sm line-clamp-2&quot;&gt;Cache files between runs.&lt;/div&gt;&lt;div class=&quot;link-preview-domain mt-2 text-xs&quot;&gt;learn.microsoft.com&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;link-preview-image relative aspect-[1200/630] sm:aspect-auto sm:flex-[0_0_35%]&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/link-previews/learn.microsoft.com-21592fd7b931.jpg&quot; alt=&quot;Microsoft Learn&quot; width=&quot;1200&quot; height=&quot;630&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;Audio Summary&lt;/h2&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2024-06-28-azure-devops-ansible-pipeline-updated/2024-06-28-azure-devops-ansible-pipeline-updated.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-06-28-azure-devops-ansible-pipeline-updated.NAaD-3oW.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/06/28/azure-devops-ansible-pipeline-boosting-efficiency-with-caching-og.png</blog:ogImage><blog:readingTime>9</blog:readingTime><category>ansible</category><category>devops</category><category>azure</category><category>infrastructure-as-code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Day to Day Tools, the 2024 edition</title><link>https://www.russ.cloud/2024/06/16/day-to-day-tools-the-2024-edition/</link><guid isPermaLink="true">https://www.russ.cloud//2024/06/16/day-to-day-tools-the-2024-edition/</guid><description>Day-to-day tools for a macOS user in 2024: Visual Studio Code, Sublime Text, Paste, 1Password, and more. Discover essential desktop, terminal, and online services for productivity.</description><pubDate>Sun, 16 Jun 2024 13:53:40 GMT</pubDate><content:encoded>&lt;p&gt;It looks like I have written about the tools I use day-to-day since &lt;a href=&quot;/2017/01/05/day-to-day-tools/&quot;&gt;2017&lt;/a&gt;; I wonder how much has changed. 🤔&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;As you may already know, I am a &lt;a href=&quot;/tags/macos/&quot;&gt;macOS&lt;/a&gt; user, so all of the tools covered in this post primarily run on Macs. 🍎&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;Desktop Apps 💻&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Visual Studio Code&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: This is still my primary IDE; I use it for all of the &lt;a href=&quot;/glossary/infrastructure-as-code/&quot;&gt;infrastructure as code&lt;/a&gt; I write along with any other code I need to write and debug, and trust me, there is more debugging going on than writing most of the time. 🐛&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.sublimetext.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sublime Text&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: For raw text editing, I use a pretty vanilla version of Sublime Text. 📝&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://pasteapp.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Paste&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: I have used Paste for a few years. It’s great to have everything I have copied and pasted across my devices in an easy-to-use searchable database-I’m not sure what I would do without it now, as everything is a quick &lt;strong&gt;[CMD]&lt;/strong&gt;+&lt;strong&gt;[Shift]&lt;/strong&gt;+&lt;strong&gt;V&lt;/strong&gt; away. 📋&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://1password.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;1Password&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: 1Password remains my password manager of choice, and it has been since I started blogging over ten years ago! 🔒&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.drawio.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Draw.io&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Being a macOS user, I don’t get to use what I assume is the “awesomeness” of Visio - luckily, this tool blows it out of the water 😃 🎨&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://bear.app/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Bear Notes&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Note applications have come and gone, but Bear Notes remains my go-to app. It has a clean interface, powerful &lt;a href=&quot;/glossary/markdown/&quot;&gt;markdown&lt;/a&gt; support, and seamless syncing across my devices. 🐻📝&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://tapbots.com/ivory/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ivory&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: This app is perfect for those who need a robust, feature-rich, and Tweetbot-like Mastonden client. Ivory provides a smooth and customizable user experience, making staying connected and engaged on social media easier. 🐘💬&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://iterm2.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;iTerm2&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: iTerm is my terminal emulator of choice. It offers more features and customization options than the default Terminal app, including split panes, custom color schemes, and extensive keyboard shortcuts. 💻&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Terminal Apps ⌨️&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/di-sukharev/opencommit&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenCommit&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: this &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; tool uses AI to automatically generate meaningful commit messages for your code changes. It generated the commit messages for this post if you look &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/blog/commits/main/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. 🤖💬&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebrew&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;: Another mainstay of the tools I use, this is the only real package manager for macOS. It simplifies the installation of software and tools, making it easy to keep my development environment up-to-date. 🍺&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.terraform.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Terraform&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is an infrastructure as code tool that allows me to define and provision cloud resources using a high-level configuration language. I use it both at work and at home and covered in more detail &lt;a href=&quot;/tags/terraform/&quot;&gt;here&lt;/a&gt; and wrote a book about which you can find &lt;span&gt;&lt;a href=&quot;https://www.packtpub.com/product/infrastructure-as-code-for-beginners/9781837631636&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. ☁️🏗️&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.ansible.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ansible&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is my go-to &lt;a href=&quot;/glossary/automation/&quot;&gt;automation&lt;/a&gt; tool for configuration management and application deployment. Its simple, agentless architecture makes it easy to manage and automate tasks across multiple servers. Oh yeah, &lt;span&gt;&lt;a href=&quot;https://www.packtpub.com/product/learn-ansible-second-edition/9781835088913&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;I have also just released a book on it as well&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. 🤖📚&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://docs.conda.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Conda&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is an open-source package management and environment management system, see &lt;a href=&quot;/2024/04/06/conda-for-python-environment-management-on-macos/&quot;&gt;this post&lt;/a&gt; for more information. 🐍📦&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://ollama.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ollama&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is a versatile tool for running and managing large language models (LLMs) locally. It supports various models like Llama 3, Mistral, and others, allowing you to customize and run LLMs efficiently on your machine. I covered this in more detail &lt;a href=&quot;/2024/03/29/running-llms-locally-with-ollama/&quot;&gt;here&lt;/a&gt;. 🦙🧠&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Services 🌐&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.grammarly.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Grammarly&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is an advanced AI-powered writing assistant that helps improve your writing by providing real-time grammar, spelling, and style suggestions. It’s indispensable for ensuring that all of my written content, from emails to blog posts, is clear, concise, and error-free. According to the weekly email I get there have been over 21,657,835 words analyzed by Grammarly since joined up on Nov 6, 2016. ✍️🤖&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.openai.com/chatgpt&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ChatGPT&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; by OpenAI is a conversational AI model that assists with generating human-like text based on the input provided. It’s useful for brainstorming ideas, drafting content, and even coding assistance, making it an essential tool for both creative and technical tasks - it also helped with some bits of this blog post. 💬🤖&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://www.midjourney.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Midjourney&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is an AI-based image-generation service that creates stunning visuals from text prompts. It’s a valuable tool for producing unique graphics, visual content, and creative illustrations for various projects and presentations - it was used to create the header of this blog post. 🖼️🤖&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://kagi.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Kagi&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is a privacy-focused search engine that provides fast, ad-free, and highly relevant search results. It enhances my web browsing experience by delivering accurate information without the clutter of ads or tracking. 🔍🕵️&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://pages.cloudflare.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cloudflare Pages&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is a platform for deploying fast and secure static websites. It integrates seamlessly with my development workflow, allowing me to quickly publish and manage my web projects with ease, including this &lt;a href=&quot;/&quot;&gt;blog&lt;/a&gt; and &lt;span&gt;&lt;a href=&quot;https://www.russ.fm/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;my record collection page&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. 🌩️🕸️&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given all the advances in AI at the moment, I wonder how much this list is going to change over the next 12 months !!!! 🤯🔮&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-06-16-2024-tools-update.oh_do6dq.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/06/16/day-to-day-tools-the-2024-edition-og.png</blog:ogImage><blog:readingTime>5</blog:readingTime><category>tools</category><category>macos</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Updating my blog using CrewAI</title><link>https://www.russ.cloud/2024/05/06/updating-my-blog-using-crewai/</link><guid isPermaLink="true">https://www.russ.cloud//2024/05/06/updating-my-blog-using-crewai/</guid><description>Discover how CrewAI revolutionized my blog&apos;s AI-generated music roundups, enhancing content quality and accuracy. Unlock the potential of collaborative AI for your creative projects.</description><pubDate>Mon, 06 May 2024 09:12:31 GMT</pubDate><content:encoded>&lt;p&gt;I have been reading a lot about CrewAI recently and as it was a long weekend I decided to tip my toe in the water and do something with it, but what?&lt;/p&gt;
&lt;p&gt;About a year ago, while bored, I added a &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; script to this blog which used the OpenAI &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; to write a blog post based on what I had listened to the previous week using data from &lt;span&gt;&lt;a href=&quot;http://last.fm/user/RussMckendrick&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Last.FM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The first of these posts &lt;a href=&quot;/2023/05/22/the-cure-dominates-my-week-a-deep-dive-into-post-punk-melancholy-and-gothic-rock/&quot;&gt;can be found here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The Original Code&lt;/h2&gt;
&lt;p&gt;The code to write this was very basic, there was a simple function that calls the OpenAI API …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Original GPT Function (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get_gpt3_text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prompt&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;completion &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; openai&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ChatCompletion&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;gpt-4-1106-preview&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;messages&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&apos;role&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;user&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&apos;content&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; prompt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;completion&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;choices&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;message&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;content&apos;&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;strip&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This function was then called with the following information which defined the instructions and then passed them to the function …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Prompt (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;top_artist_summary &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get_wiki_summary&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;top_artist &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot; band&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chat_post_summary &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;According to LastFM data the artist I most played this week was &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;top_artist&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;. Can you write a short 50 word summary to say this. It is going to be used as a description for a blog post so should be descriptive and interesting.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chat_intro &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Write a casual blog post which details what music I have been listening to this week. The blog post should be 1000 words long. Feel free to use emjois and markdown formatting to make the post more interesting.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; top_artist_summary&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;chat_top_artist_info &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;The most played artist this week was &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;top_artist&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;, Wikipedia has this to say about &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;top_artist&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; ... &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;top_artist_summary&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;chat_top_artist_info &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;The most played artist this week was &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;top_artist&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chat_other_artists &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Other artists I listened to this week include &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;, &apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;artist &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; artist&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; count &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;top_artists&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;]])&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;, mention these too the end, but don&apos;t repeat any information you have already given.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chat_data_source &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;The data for this blog post was collected from Last.fm you can find my profile at https://www.last.fm/user/RussMckendrick.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;chat_ai_generated &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Also, mention that this part of the blog post was AI generated - this part of the post should be short&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gpt3_prompt &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;chat_intro&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;chat_top_artist_info&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;chat_other_artists&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;chat_data_source&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;chat_ai_generated&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gpt3_summary &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get_gpt3_text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;chat_post_summary&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gpt3_post &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get_gpt3_text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;gpt3_prompt&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, I was getting information on the top artists, using Wikipedia and then passing everything to OpenAI to write the blog post, there is also code to pull down artist and album images from &lt;span&gt;&lt;a href=&quot;https://russ.fm/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;russ.fm &lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; - which is my other site that catalogs my record collection.&lt;/p&gt;
&lt;p&gt;This was all run from a &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; Action which was automatically triggered early Monday morning, creating a pull request for me to review on my way to work.&lt;/p&gt;
&lt;p&gt;It worked, but it wasn’t perfect - it had a habit of making things up, also, from time to time it would try to embed images that didn’t exist and one case ignored everything I told it and made up an album from the top artist - referencing songs and reviews that simply weren’t real !!!&lt;/p&gt;
&lt;p&gt;This seemed to like the perfect bit of code to review and replace with CrewAI.&lt;/p&gt;
&lt;h2&gt;Introducing CrewAI&lt;/h2&gt;
&lt;p&gt;The &lt;span&gt;&lt;a href=&quot;https://www.crewai.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;CrewAI website&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; describes the tool as …&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;… which does sound a little far-fetched, but then I started looking at some of the example code and it VERY easy to read and understand, the code below is taken from the &lt;span&gt;&lt;a href=&quot;https://docs.crewai.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;documentation&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating the agents (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;os&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;environ&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;SERPER_API_KEY&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Your Key&quot;&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# serper.dev API key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;os&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;environ&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Your Key&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; crewai &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; crewai_tools &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; SerperDevTool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;search_tool &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SerperDevTool&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Creating a senior researcher agent with memory and verbose mode&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;researcher &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;Senior Researcher&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;goal&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;Uncover groundbreaking technologies in &lt;/span&gt;&lt;span&gt;{topic}&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;verbose&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;memory&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;backstory&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;Driven by curiosity, you&apos;re at the forefront of&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;innovation, eager to explore and share knowledge that could change&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;the world.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;search_tool&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;allow_delegation&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Creating a writer agent with custom tools and delegation capability&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;writer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;Writer&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;goal&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;Narrate compelling tech stories about &lt;/span&gt;&lt;span&gt;{topic}&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;verbose&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;memory&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;backstory&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;With a flair for simplifying complex topics, you craft&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;engaging narratives that captivate and educate, bringing new&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;discoveries to light in an accessible manner.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;search_tool&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;allow_delegation&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;False&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, this is adding two agents, one who will research the subject &lt;code&gt;{topic}&lt;/code&gt; and the other who will write about it. Both can access and search the internet using &lt;span&gt;&lt;a href=&quot;https://serper.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Serper &lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. With the two agents defined you then need to create tasks …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating the tasks (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; crewai &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Task&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Research task&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;research_task &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;Identify the next big trend in &lt;/span&gt;&lt;span&gt;{topic}&lt;/span&gt;&lt;span&gt;.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;Focus on identifying pros and cons and the overall narrative.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;Your final report should clearly articulate the key points,&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;its market opportunities, and potential risks.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;expected_output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;A comprehensive 3 paragraphs long report on the latest AI trends.&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;search_tool&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;researcher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Writing task with language model configuration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;write_task &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;Compose an insightful article on &lt;/span&gt;&lt;span&gt;{topic}&lt;/span&gt;&lt;span&gt;.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;Focus on the latest trends and how it&apos;s impacting the industry.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;This article should be easy to understand, engaging, and positive.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;expected_output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;A 4 paragraph article on &lt;/span&gt;&lt;span&gt;{topic}&lt;/span&gt;&lt;span&gt; advancements formatted as markdown.&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;search_tool&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;writer&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;async_execution&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;False&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;output_file&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;new-blog-post.md&apos;&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Example of output customization&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Again, it easy to see what is happening here as each of the two tasks is assigned an &lt;a href=&quot;/glossary/agent/&quot;&gt;agent&lt;/a&gt; and that expected output is being clearly defined. With the agents and tasks defined we can then assemble the crew …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Assembling the crew (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; crewai &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Crew&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; Process&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Forming the tech-focused crew with some enhanced configurations&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;crew &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Crew&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;agents&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;researcher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; writer&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;research_task&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; write_task&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;Process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sequential&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Optional: Sequential task execution is default&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;memory&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;max_rpm&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;share_crew&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, we can then run the tasks …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Start the task (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# Starting the task execution process with enhanced feedback&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; crew&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;kickoff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;topic&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;AI in healthcare&apos;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Running the code above, but using the topic of “AI in blogging” gave the following results …&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Rise of AI in Blogging: A Double-Edged Sword?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Artificial Intelligence (AI) is redefining the blogging landscape, with a surge in AI-powered writing tools and techniques. These tools leverage advancements in Natural Language Processing (NLP) and Machine Learning (ML) to generate ideas and overcome writer’s block, making content creation more efficient and cost-effective. In addition, AI-powered brainstorming apps and topic generators are gaining traction, aiding writers in exploring new perspectives and identifying trending topics.&lt;/p&gt;
&lt;p&gt;However, the AI-powered revolution in blogging isn’t without its challenges. Critics have raised concerns over AI-generated content’s quality, with a common critique being the lack of human personality and creativity. AI’s inability to replicate the emotional depth and personal touch of a human writer can sometimes result in content that feels sterile or impersonal.&lt;/p&gt;
&lt;p&gt;In addition, the risk of plagiarism is a significant concern in AI-generated content. As AI tools draw from existing online content to generate new material, there’s a potential for inadvertently replicating copyrighted work. Furthermore, ethical questions have emerged about transparency, with readers having a right to know if the content they’re consuming is AI-generated.&lt;/p&gt;
&lt;p&gt;Despite these challenges, the industry is looking to mitigate these risks. The key lies in using reliable AI writing assistants that produce high-quality, original content. Maintaining transparency about the use of AI in content creation and upholding ethical standards regarding the use of private information are also crucial steps. In conclusion, while AI offers exciting new possibilities for the blogging industry, it’s essential to navigate its use responsibly and ethically to reap its benefits while minimizing potential drawbacks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;… there is a lot of output when running the script, I added a copy of &lt;span&gt;&lt;a href=&quot;https://gist.github.com/russmckendrick/6b3dc6872d4cfe8b4f5fed6b9c0f1a26&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;the full script and the output to this GitHub Gist&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; so you can review it all, as I am sure you will agree - it is impressive stuff.&lt;/p&gt;
&lt;h2&gt;What did listen to?&lt;/h2&gt;
&lt;p&gt;I decided that I should have two separate crews in my code, the first will generate the post title and add an SEO-friendly description …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Subject and Summary Crew (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sanitize_text_output&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Clean the output text by removing backticks, double quotes, and any characters that are not word characters, whitespace, or hyphens.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Args:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;text (str): The input text to be cleaned.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Returns:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;str: The cleaned text with backticks, double quotes, and specified characters removed.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; re&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sub&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;\&apos;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; text&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;generate_title_and_summary&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;date_str_start&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;week_number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;top_artists&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;top_albums&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Generate a title and summary for a weekly music blog post using a crew of agents.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;This function takes the start date, week number, top artists, and top albums as input and kicks off&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;a crew of agents to generate a catchy and SEO-friendly title and a concise summary for the blog post.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;The crew consists of two agents: a &quot;Title Generator&quot; agent and a &quot;Summary Generator&quot; agent.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;The &quot;Title Generator&quot; agent is assigned a task to generate a title for the blog post, considering the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;top artists and albums of the week. The title should be catchy, SEO-friendly, and not exceed 70&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;characters or use special characters such as :, -, |, quotes, or emojis.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;The &quot;Summary Generator&quot; agent is assigned a task to generate a summary for the blog post, providing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;a brief overview of the post&apos;s content. The summary should be concise, SEO-friendly, and not exceed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;180 characters or use special characters.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Args:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;date_str_start (str): The start date of the week in string format.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;week_number (int): The number of the week.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;top_artists (list): A list of tuples representing the top artists of the week, where each tuple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;contains the artist name and the play count.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;top_albums (list): A list of tuples representing the top albums of the week, where each tuple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;contains a tuple of the artist name and album name, and the play count.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Returns:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tuple: A tuple containing two elements:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- title (str): The generated title for the blog post.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- summary (str): The generated summary for the blog post.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Raises:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;AgentError: If an error occurs during the agents&apos; execution of the tasks.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TaskError: If an error occurs while processing the tasks.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CrewError: If an error occurs during the crew&apos;s execution.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Notes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The function uses the clean_output function to remove special characters from the generated&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title and summary.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The crew is set up with a maximum of 10 interactions per minute.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The function prints the result of the crew&apos;s execution, the generated title, and the generated&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;summary.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title_agent &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Title Generator&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;goal&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Generate a catchy and SEO-friendly title for a weekly music blog post. The post is about the top artists and albums listened to this week, &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;, &apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;artist &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; artist&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; top_artists&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; and top albums: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;, &apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;album &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; album&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; top_albums&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;. Do not exceed 70 characters or use special characters such a :, -, |, quotes or emojis.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;backstory&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;You are an expert in creating creative, engaging and SEO-optimized titles for blog posts. Your titles should grab the reader&apos;s attention and include relevant keywords.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;verbose&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;max_inter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title_task &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Generate a title for a weekly music blog post featuring the top artists: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;, &apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;artist &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; artist&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; top_artists&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; and top albums: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;, &apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;album &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; album&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; top_albums&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;expected_output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;A catchy and SEO-friendly title for the blog post. Do not exceed 70 characters or use special characters such a :, -, |, quotes or emojis.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;max_inter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;title_agent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;summary_agent &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Summary Generator&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;goal&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Generate a concise and SEO-friendly summary for a weekly music blog post. The post is about the top artists and albums listened to in week &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;week_number&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; starting from &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;date_str_start&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;backstory&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;You are an expert in creating informative and SEO-optimized summaries for blog posts. Your summaries should provide a brief overview of the post&apos;s content and include relevant keywords.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;verbose&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;max_inter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;summary_task &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Generate a summary for a weekly music blog post featuring the top artists: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;, &apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;artist &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; artist&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; top_artists&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; and top albums: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&apos;, &apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;album &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; album&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; top_albums&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;expected_output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;A concise and SEO-friendly summary for the blog post. It shouldn&apos;t be more than 180 characters and it should NOT use special characters such a :, -, |,  quotes or emojis.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;max_inter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;summary_agent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;crew &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Crew&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;agents&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;title_agent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; summary_agent&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;title_task&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; summary_task&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;max_rpm&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;full_output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; crew&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;kickoff&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sanitize_text_output&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tasks_outputs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;exported_output&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;summary &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sanitize_text_output&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tasks_outputs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;exported_output&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; title&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; summary&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You might have noticed that I am passing the output through another function that removes some of the characters that would break the blog posts front-mater as I found that it generates something that would look something like &lt;code&gt;&quot;&quot;My Blog Post&quot;&quot;&lt;/code&gt; and the &lt;code&gt;&quot;&quot;&lt;/code&gt; would break Hugo.&lt;/p&gt;
&lt;p&gt;This was called using …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Calling the function (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; summary &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;generate_title_and_summary&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;date_str_start&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; week_number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; top_artists&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; top_albums&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next up we have the crew that researched the albums I listened to one by one …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Album Research Crew (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;research_an_album&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Research an album and generate a blog post section using a crew of agents.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;This function takes an album name as input and kicks off a crew of agents to research the album&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;and generate a well-structured blog post section. The crew consists of a single &quot;Music Research&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;agent with a specific goal and backstory. The agent is assigned a task to search for details about&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;the album and write an informative and engaging blog post section in markdown format.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Args:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;album (str): The name of the album to research and write about.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Returns:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;str: The generated blog post section about the album in markdown format.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Raises:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;AgentError: If an error occurs during the agent&apos;s execution of the task.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TaskError: If an error occurs while processing the task.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CrewError: If an error occurs during the crew&apos;s execution.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Notes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The agent uses search and web tools (search_tool and web_tool) to gather information about the album.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The generated blog post section should be well-organized, easy to read, and in markdown format.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The section should be no more than 800 words and include relevant emojis.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The agent is limited to a single interaction (max_inter=1) to generate the content.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The crew is set up with sequential processing and a maximum of 10 interactions per minute.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- The function returns the full output of the crew&apos;s execution, which includes the generated blog post section.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;blogger &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Music Research&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;goal&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;You are a Music lover and are going to be writing sections of a blog post containing information on the albums you have listed to this week. One the albums you listened to is &apos;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos;. Find a good summary of &apos;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos; which can be used to write the blog post.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;backstory&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;You are an expert music Blogger on Internet. Include details on the album &apos;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos;, artist and any other interesting facts you can find. You have a passion for music of all genres and you are excited to share your thoughts with the world.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;verbose&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;max_inter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;task_blog_post &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Search for details about the album &apos;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos;. Your final answer MUST be a consolidated content that can be as a section of a blog post. This content should be well organized, and should be very easy to read. You must provide a 800 word section for a blog post.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;expected_output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;Write a well structured section for a blog post on &apos;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos;. A comprehensive section on &apos;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&apos; in markdown format - do not use any H1 headers, only H2 and below, add lots of relevant emojis and make it no more than 800 words.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;max_inter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;search_tool&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; web_tool&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;blogger&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;crew &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Crew&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;agents&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;blogger&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;task_blog_post&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;Process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sequential&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;max_rpm&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;full_output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; crew&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;kickoff&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This was called by a for loop which contained details of the album …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Calling the function (PYTHON)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;topics &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; by &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;artist&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;artist&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; album&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; top_albums&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;blog_post_sections &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; album &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; topics&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;research_an_album&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;album&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;output_str &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;final_output&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;blog_post_sections&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;append&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;output_str&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;blog_post &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\n\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;blog_post_sections&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This resulted in a BIG improvement to the size, quality and accuracy of the posts, I went through and updated all of April’s posts and today’s (6th May 2024) was also automatically generated:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2024/04/01/weekly-top-tunes-from-beach-boys-to-thundercat/&quot;&gt;2024/04/01 - Weekly Top Tunes: From Beach Boys to Thundercat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/04/08/this-weeks-top-music-hits-from-inxs-to-the-who/&quot;&gt;2024/04/08 - This Weeks Top Music Hits: From INXS to The Who&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/04/15/this-weeks-top-music-picks-rancid-to-tears-for-fears/&quot;&gt;2024/04/15 - This Weeks Top Music Picks: Rancid to Tears for Fears&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/04/22/this-weeks-top-music-hits-from-springsteen-to-r.e.m./&quot;&gt;2024/04/22 - This Weeks Top Music Hits: From Springsteen to R.E.M.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/04/29/this-weeks-top-music-hits-eagles-blur-kim-gordon-and-more/&quot;&gt;2024/04/29 - This Weeks Top Music Hits: Eagles, Blur, Kim Gordon and More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/05/06/this-weeks-top-music-hits-from-pink-floyd-to-soulwax/&quot;&gt;2024/05/06 - This Weeks Top Music Hits: From Pink Floyd to Soulwax&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The full output of today’s post being generated &lt;span&gt;&lt;a href=&quot;https://gist.github.com/russmckendrick/f5a3407b84f2850be0ffcacf5d32124a&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;can be found in this GitHub Gist&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and the full code is &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/blog/blob/main/generate_blog_post.py&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;If you want to keep an eye on what I am listening to then &lt;a href=&quot;/tunes/&quot;&gt;follow this link&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Integrating CrewAI into my blog’s AI-generated weekly music roundup feature has been a game-changer. By leveraging the power of collaborative AI agents, I’ve been able to significantly improve the quality, accuracy, and depth of the content generated for these posts.&lt;/p&gt;
&lt;p&gt;The Subject and Summary Crew ensures that each post has a compelling, SEO-friendly title and a concise summary that captures the essence of the content. Meanwhile, the Album Research Crew does a fantastic job of researching the albums I’ve listened to and generating informative, engaging sections for the blog post.&lt;/p&gt;
&lt;p&gt;The results speak for themselves – the updated posts are more comprehensive, better structured, and far more enjoyable to read. CrewAI has allowed me to take my AI-generated content to the next level, providing my readers with valuable insights into my weekly music listening habits.&lt;/p&gt;
&lt;p&gt;As I continue to explore the capabilities of CrewAI and other AI technologies, I’m excited about the possibilities they offer for enhancing my blog and delivering high-quality content to my audience. By embracing these tools and using them responsibly, we can unlock new ways to inform, entertain, and engage our readers.&lt;/p&gt;
&lt;p&gt;If you’re interested in seeing how AI can revolutionize your content creation process, I highly recommend giving CrewAI a try. With its intuitive framework and powerful collaboration features, it’s an invaluable tool for anyone looking to harness the power of AI in their creative endeavors.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-05-06-updating-my-blog-using-crewai.BRDQBZA-.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/05/06/updating-my-blog-using-crewai-og.png</blog:ogImage><blog:readingTime>16</blog:readingTime><category>ai</category><category>blog</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Announcing the second edition of Learn Ansible</title><link>https://www.russ.cloud/2024/04/21/announcing-the-second-edition-of-learn-ansible/</link><guid isPermaLink="true">https://www.russ.cloud//2024/04/21/announcing-the-second-edition-of-learn-ansible/</guid><description>My new book, the second edition of Learn Ansible is now available for pre-order.</description><pubDate>Sun, 21 Apr 2024 15:07:25 GMT</pubDate><content:encoded>&lt;p&gt;Exciting News! I’m thrilled to announce that the 2nd edition of my book, “Learn &lt;a href=&quot;/glossary/ansible/&quot;&gt;Ansible&lt;/a&gt;: Automate your cloud infrastructure, &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; configuration, and application deployment using Ansible” is now available for pre-order on Amazon and Packt Publishing’s website! 📣 🎉 📚&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Learn-Ansible-infrastructure-configuration-application-ebook/dp/B0D1Y2D5Z6/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Amazon ( 🇺🇸 )&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.co.uk/dp/B0D1Y2D5Z6/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Amazon ( 🇬🇧 )&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.packtpub.com/product/learn-ansible-second-edition/9781835088913&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Packt Publishing ( 🌍 )&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whether you’re a beginner or an intermediate user looking to take your Ansible skills to the next level, this hands-on guide will empower you to automate your infrastructure and application deployments consistently and efficiently. 💻 🚀&lt;/p&gt;
&lt;p&gt;What you’ll learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install and configure Ansible on Linux, macOS, and Windows ✅&lt;/li&gt;
&lt;li&gt;Write playbooks to automate system configuration and deployment ✅&lt;/li&gt;
&lt;li&gt;Deploy LAMP stacks, WordPress, and other applications with Ansible ✅&lt;/li&gt;
&lt;li&gt;Create reusable roles and share them on Ansible Galaxy ✅&lt;/li&gt;
&lt;li&gt;Automate infrastructure deployments on &lt;a href=&quot;/glossary/aws/&quot;&gt;AWS&lt;/a&gt; and &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; ✅&lt;/li&gt;
&lt;li&gt;Integrate Ansible into &lt;a href=&quot;/glossary/ci-cd/&quot;&gt;CI/CD&lt;/a&gt; pipelines like &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; Actions and &lt;a href=&quot;/glossary/azure-devops/&quot;&gt;Azure DevOps&lt;/a&gt; ✅&lt;/li&gt;
&lt;li&gt;Scan playbooks for security issues and harden systems using Ansible ✅&lt;/li&gt;
&lt;li&gt;Leverage Ansible AWX to centralize and manage Ansible deployments ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This practical learning resource is perfect for system administrators, developers, and infrastructure engineers who want to streamline their workflows and save time by automating repetitive tasks. Technology professionals working with public cloud platforms will also benefit from the book’s in-depth coverage of automating deployments on AWS and Azure. 🌐☁️&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-21-announcing-the-second-edition-of-learn-ansible/images/cover_image_large.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2024-04-21-announcing-the-second-edition-of-learn-ansible/images/cover_image_large.jpg&quot; alt=&quot;Cover image of the book &apos;Learn Ansible&apos; by Russ McKendrick, 2nd edition, featuring a photograph of a modern bridge against a blue sky. The book focuses on automating cloud infrastructure, security configuration, and application deployment using Ansible, published by Packt.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1216&quot; height=&quot;1500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Don’t miss this opportunity to master Ansible and take your deployments to the next level! Pre-order your copy today and be among the first to receive the book upon its release in late May. 📚💨&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-04-21-announcing-the-second-edition-of-learn-ansible.CzA1otQs.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/04/21/announcing-the-second-edition-of-learn-ansible-og.png</blog:ogImage><blog:readingTime>2</blog:readingTime><category>book</category><category>ansible</category><category>life</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Installing and running InvokeAI on macOS</title><link>https://www.russ.cloud/2024/04/14/installing-and-running-invokeai-on-macos/</link><guid isPermaLink="true">https://www.russ.cloud//2024/04/14/installing-and-running-invokeai-on-macos/</guid><description>A step-by-step guide on installing and running InvokeAI on macOS for local AI image generation using Conda and Python.</description><pubDate>Sun, 14 Apr 2024 06:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Just a quick post this week, following on from &lt;a href=&quot;/2024/04/06/conda-for-python-environment-management-on-macos/&quot;&gt;my last post last week about using Conda&lt;/a&gt; and also the one from a few weeks about about &lt;a href=&quot;/2024/03/29/running-llms-locally-with-ollama/&quot;&gt;running LLMs locally using ollama&lt;/a&gt; I decided to explore the world of AI image generation on my local macOS machine.&lt;/p&gt;
&lt;p&gt;To do this I thought I would revisit a tool I dabbled with about a year ago called &lt;span&gt;&lt;a href=&quot;https://github.com/invoke-ai/InvokeAI&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;InvokeAI&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, the tool is described as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;InvokeAI is a leading creative engine for Stable Diffusion models, empowering professionals, artists, and enthusiasts to generate and create visual media using the latest AI-driven technologies. The solution offers an industry leading WebUI, supports terminal use through a &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt;, and serves as the foundation for multiple commercial products.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Installing InvokeAI&lt;/h2&gt;
&lt;p&gt;While there is an automated installer, I thought it best to run through a manual installation so I have a little more control over what is installed and where on my machine.&lt;/p&gt;
&lt;h3&gt;Preparing the Python environment&lt;/h3&gt;
&lt;p&gt;I am going to use &lt;a href=&quot;/glossary/conda/&quot;&gt;Conda&lt;/a&gt; so I don’t end up messing up my newly organized &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; environment:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating the InvokeAI conda environment&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda create -n InvokeAI python=3.11&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda activate InvokeAI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, we are setting up the environment using Python 3.11, this is because not everything that InvokeAI uses has been updated to use Python 3.12, yet.&lt;/p&gt;
&lt;h3&gt;Setting the path and installing&lt;/h3&gt;
&lt;p&gt;With our Python environment ready we can now set the path where InvokeAI will store its config, database and files:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting the path&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export INVOKEAI_ROOT=~/invokeai&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mkdir $INVOKEAI_ROOT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The path I have used, &lt;code&gt;~/invokeai&lt;/code&gt; is the default one, if you change this I would recommend adding &lt;code&gt;export INVOKEAI_ROOT=~/your/own/path/to/invokeai&lt;/code&gt; to your &lt;code&gt;~/.zshrc&lt;/code&gt; or &lt;a href=&quot;/2024/04/02/updating-my-dotfiles/&quot;&gt;Dotfiles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can now install InvokeAI itself, to do this run the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing InvokeAI and its requirements&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip install InvokeAI --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu121&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, it installs &lt;strong&gt;alot&lt;/strong&gt; of packages and takes a few minutes to download and install everything.&lt;/p&gt;
&lt;h3&gt;Starting the web interface&lt;/h3&gt;
&lt;p&gt;Once installed, all that is left to do is to start the web interface - to do this run the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Starting the InvokeAI web interface&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;invokeai-web&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The first time you run this command it will take slightly longer than it normally would take due to it creating the configuration files and databases etc.&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/install/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/install/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/install/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/install/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the output above, once it has started you will prompted to open your browser and entering &lt;span&gt;&lt;a href=&quot;http://127.0.0.1:9090/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://127.0.0.1:9090/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Using InvokeAI&lt;/h2&gt;
&lt;p&gt;Upon first opening the web UI you will be presented with a screen which looks like one below:&lt;/p&gt;

&lt;h3&gt;Downloading a model&lt;/h3&gt;
&lt;p&gt;Before we can generate an image we need to download a model, I chose to download &lt;span&gt;&lt;a href=&quot;https://civitai.com/models/133005?modelVersionId=348913&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Juggernaut XL v9&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; which is one of the ones listed in the model manager - as you can see from the samples below, it is capable of quite a few different styles of images:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/05.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/05.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/06.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/06.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/07.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/07.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/08.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/08.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/09.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/09.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/10.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-14-installing-and-running-invokeai-on-macos/images/web/samples/10.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;All of the images above are taken from the &lt;span&gt;&lt;a href=&quot;https://civitai.com/models/133005?modelVersionId=348913&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Juggernaut XL v9 page&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; at &lt;span&gt;&lt;a href=&quot;http://civitai.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;CivitAI&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h3&gt;Creating the cover image for this post&lt;/h3&gt;
&lt;p&gt;With the model downloaded, I went to the test prompt page and entered the following as the prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Photo of a 1950s vintage retro robot working intently on his laptop, seated in a bustling coffee shop. The scene captures a realistic setting with natural lighting from the window, reflecting off his detailed, focused expression. Surrounding him are blurred patrons and baristas, adding depth and life to the image, complemented by the ambient coffee shop interior with wooden tables and cozy decor. Natural light, 35mm photograph, film, professional, 4k, highly detailed, Golden hour lighting, Depth of field F2, Rule of Thirds Composition.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I also entered this as the negative prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;malformed, extra limbs, poorly drawn anatomy, badly drawn, extra legs, low resolution, blurry, Watermark, Text, censored, deformed, bad anatomy, disfigured, poorly drawn face, mutated, extra limb, ugly, poorly drawn hands, missing limb, floating limbs, disconnected limbs, disconnected head, malformed hands, long neck, mutated hands and fingers, bad hands, missing fingers, cropped, worst quality, low quality, mutation, poorly drawn, huge calf, bad hands, fused hand, missing hand, disappearing arms, disappearing thigh, disappearing calf, disappearing legs, missing fingers, fused fingers, abnormal eye proportion, Abnormal hands, abnormal legs, abnormal feet, abnormal fingers, drawing, painting, crayon, sketch, graphite, impressionist, noisy, blurry, soft, deformed, ugly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then I clicked the button to generate the image:&lt;/p&gt;

&lt;p&gt;After a few minutes, and after clicking the upscale button I was left with this:&lt;/p&gt;

&lt;p&gt;It wasn’t exactly what I asked for - but as a first effort, not a bad result - you can find the upscaled version &lt;a href=&quot;/2024/04/14/installing-and-running-invokeai-on-macos/images/web/cover/raw.png&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Trying again&lt;/h3&gt;
&lt;p&gt;Let’s take the prompts and settings from one of the examples and run that through our own local copy of the model:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A hyperdetailed photograph of a Cat dressed as a mafia boss holding a fish walking down a Japanese fish market with an angry face, 8k resolution, best quality, beautiful photograph, dynamic lighting,&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With a negative prompt of:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(worst quality, low quality, normal quality, lowres, low details, oversaturated, undersaturated, overexposed, underexposed, grayscale, bw, bad photo, bad photography, bad art:1.4), (watermark, signature, text font, username, error, logo, words, letters, digits, autograph, trademark, name:1.2), (blur, blurry, grainy), morbid, ugly, asymmetrical, mutated malformed, mutilated, poorly lit, bad shadow, draft, cropped, out of frame, cut off, censored, jpeg artifacts, out of focus, glitch, duplicate, (airbrushed, cartoon, anime, semi-realistic, cgi, render, blender, digital art, manga, amateur:1.3), (3D ,3D Game, 3D Game Scene, 3D Character:1.1), (bad hands, bad anatomy, bad body, bad face, bad teeth, bad arms, bad legs, deformities:1.3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Clicking on the Invoke button gives …&lt;/p&gt;

&lt;p&gt;We can soon start to see the image coming together …&lt;/p&gt;

&lt;p&gt;Until we have our final image …&lt;/p&gt;

&lt;p&gt;Not quite sure whats up with the fish - but another great effort …&lt;/p&gt;

&lt;p&gt;You can find the untouched upscaled image &lt;a href=&quot;/2024/04/14/installing-and-running-invokeai-on-macos/images/web/again/raw.png&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Shutting down and starting up&lt;/h2&gt;
&lt;p&gt;When you have finished with InvokeAI you can shut it down by pressing &lt;strong&gt;CTRL+C&lt;/strong&gt; to quit. When you need to reopen, open a terminal and run:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Starting InvokeAI up again&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;activate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;InvokeAI&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;invokeai-web&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Returning to &lt;span&gt;&lt;a href=&quot;http://127.0.0.1:9090/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://127.0.0.1:9090/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; will let you pick up where you left off.&lt;/p&gt;
&lt;h2&gt;Further reading&lt;/h2&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;As this is a local install it is not moderated - because of this, depending on the models you download you may end with NSFW images. Some of the links below may also contain images that could be classed as NSFW.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://lexica.art&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Lexica&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, a source of inspiration for prompts&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;http://civitai.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;CivitAI&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, as already mentioned, it’s a great source for models and modifiers as well as images.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://arthub.ai&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ArtHub.ai&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, another source of prompts for Stable Diffusion based models&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;InvokeAI is a powerful tool for generating AI-driven images locally on your macOS machine. By following the step-by-step installation process outlined in this post, you can set up a dedicated Python environment using Conda and install InvokeAI along with its dependencies.&lt;/p&gt;
&lt;p&gt;Once installed, the InvokeAI web interface provides an intuitive way to explore and generate images using various models. By downloading a model like Juggernaut XL v9 and experimenting with different prompts and settings, you can create stunning and unique images right on your own computer.&lt;/p&gt;
&lt;p&gt;While the results may not always be exactly what you envisioned, the ability to iterate and refine your prompts allows for a creative and engaging experience. With a little practice and exploration, you can unleash your imagination and generate impressive AI-driven images.&lt;/p&gt;
&lt;p&gt;It’s important to keep in mind that local installations of InvokeAI are not moderated, so be cautious when downloading models and be prepared for the possibility of encountering NSFW content.&lt;/p&gt;
&lt;p&gt;Overall, InvokeAI is a fascinating tool that opens up a world of possibilities for AI image generation on macOS. Whether you’re an artist, designer, or simply curious about the capabilities of AI, InvokeAI is definitely worth exploring. So dive in, experiment, and let your creativity run wild!&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-04-14-installing-and-running-invokeai-on-macos.C_qYcKHR.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/04/14/installing-and-running-invokeai-on-macos-og.png</blog:ogImage><blog:readingTime>8</blog:readingTime><category>macOS</category><category>Python</category><category>AI</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Conda for Python environment management on macOS</title><link>https://www.russ.cloud/2024/04/06/conda-for-python-environment-management-on-macos/</link><guid isPermaLink="true">https://www.russ.cloud//2024/04/06/conda-for-python-environment-management-on-macos/</guid><description>Learn how to efficiently manage Python environments on your MacBook Pro using Conda. This comprehensive guide covers installation, creating and activating environments, installing packages, and best practices for streamlining your Python development workflow.</description><pubDate>Sat, 06 Apr 2024 06:30:00 GMT</pubDate><content:encoded>&lt;p&gt;As mentioned in a &lt;a href=&quot;/2024/03/29/running-llms-locally-with-ollama/&quot;&gt;previous post&lt;/a&gt;, I recently finished writing the second edition of Learn &lt;a href=&quot;/glossary/ansible/&quot;&gt;Ansible&lt;/a&gt;. This had me using &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; a lot, and my one key takeaway is that managing Python has become a massive chore. Now, with the book complete, I thought I would strip back on my machine, start from scratch - or as close to clean as I could get - and develop a cleaner, more streamlined way of managing Python on my local machine.&lt;/p&gt;
&lt;p&gt;I have tried a few methods in the past-see these posts from &lt;a href=&quot;/2019/12/29/upgrade-python-on-macos/&quot;&gt;December 2019&lt;/a&gt;, &lt;a href=&quot;/2021/01/10/managing-python-on-macos-big-sur/&quot;&gt;January 2021&lt;/a&gt; and &lt;a href=&quot;/2021/10/30/managing-python-on-macos-monterey/&quot;&gt;October 2021&lt;/a&gt;. While these worked, they quickly became a chore to maintain due to all the different things that were trying to use Python. For example, there is the version of Python built into macOS, Homebrew tries to maintain its own Python installations for packages it downloads, and then there is my stuff on top.&lt;/p&gt;
&lt;h2&gt;Introducing Conda&lt;/h2&gt;
&lt;p&gt;I did a little research and chose &lt;a href=&quot;/glossary/conda/&quot;&gt;Conda&lt;/a&gt; - which can be found &lt;span&gt;&lt;a href=&quot;https://www.conda.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. While we will cover the Conda command in this post - I am using &lt;span&gt;&lt;a href=&quot;https://docs.anaconda.com/free/miniconda/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Miniconda&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Miniconda is described as the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Miniconda is a free minimal installer for conda. It is a small bootstrap version of Anaconda that includes only conda, Python, the packages they both depend on, and a small number of other useful packages (like pip, zlib, and a few others).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, as I wanted to keep things as simple as possible, I used Miniconda rather than the more full-fat Anaconda.&lt;/p&gt;
&lt;h3&gt;Installing and configuring Miniconda on macOS&lt;/h3&gt;
&lt;p&gt;The installation guide for Miniconda is simple if you are using &lt;span&gt;&lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebrew&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, you just need to run the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Miniconda&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew install miniconda&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once installed, we must ensure it is loaded when we open our terminal. To add the right lines to &lt;code&gt;~/.zshrc&lt;/code&gt; run the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Update the ~/.zshrc file&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda init zsh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This adds the following code to the end of &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Contents appended to ~/.zshrc (TEXT)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# &amp;gt;&amp;gt;&amp;gt; conda initialize &amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# !! Contents within this block are managed by &apos;conda init&apos; !!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;__conda_setup=&quot;$(&apos;/opt/homebrew/Caskroom/miniconda/base/bin/conda&apos; &apos;shell.zsh&apos; &apos;hook&apos; 2&amp;gt; /dev/null)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if [ $? -eq 0 ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;eval &quot;$__conda_setup&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if [ -f &quot;/opt/homebrew/Caskroom/miniconda/base/etc/profile.d/conda.sh&quot; ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;. &quot;/opt/homebrew/Caskroom/miniconda/base/etc/profile.d/conda.sh&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;export PATH=&quot;/opt/homebrew/Caskroom/miniconda/base/bin:$PATH&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;unset __conda_setup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# &amp;lt;&amp;lt;&amp;lt; conda initialize&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I have already &lt;a href=&quot;/2024/04/02/updating-my-dotfiles/&quot;&gt;updated my dotfiles&lt;/a&gt; to include the above and also to use the &lt;span&gt;&lt;a href=&quot;https://github.com/romkatv/powerlevel10k&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Powerline10k theme&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; so it shows the Conda environment that is currently selected in my terminal:&lt;/p&gt;

&lt;p&gt;As you can see from the prompt above, where it shows &lt;strong&gt;&lt;strong&gt;base&lt;/strong&gt;&lt;/strong&gt; on the right-hand side - with &lt;strong&gt;&lt;strong&gt;base&lt;/strong&gt;&lt;/strong&gt; being the default environment.&lt;/p&gt;
&lt;h2&gt;Adding an Ansible environment&lt;/h2&gt;
&lt;p&gt;In this example, I will create an environment for running Ansible; let’s start by creating the environment itself.&lt;/p&gt;
&lt;h3&gt;Creating the environment&lt;/h3&gt;
&lt;p&gt;To add an environment for running just Ansible, I ran the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Creating the Ansible environment&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda create -n ansible python=3.12&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The command gave me the following output:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Output of the create command&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Channels:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- defaults&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Platform: osx-arm64&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Collecting package metadata (repodata.json): done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Solving environment: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Package Plan ##&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;environment location: /opt/homebrew/Caskroom/miniconda/base/envs/ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;added / updated specs:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- python=3.12&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The following packages will be downloaded:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;package                    |            build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;---------------------------|-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bzip2-1.0.8                |       h80987f9_5         129 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ca-certificates-2024.3.11  |       hca03da5_0         128 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;python-3.12.2              |       h99e199e_0        14.0 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tzdata-2024a               |       h04d1e81_0         116 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xz-5.4.6                   |       h80987f9_0         372 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                                           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Total:        14.8 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bzip2              pkgs/main/osx-arm64::bzip2-1.0.8-h80987f9_5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ca-certificates    pkgs/main/osx-arm64::ca-certificates-2024.3.11-hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expat              pkgs/main/osx-arm64::expat-2.5.0-h313beb8_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;libcxx             pkgs/main/osx-arm64::libcxx-14.0.6-h848a8c0_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;libffi             pkgs/main/osx-arm64::libffi-3.4.4-hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ncurses            pkgs/main/osx-arm64::ncurses-6.4-h313beb8_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;openssl            pkgs/main/osx-arm64::openssl-3.0.13-h1a28f6b_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pip                pkgs/main/osx-arm64::pip-23.3.1-py312hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;python             pkgs/main/osx-arm64::python-3.12.2-h99e199e_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;readline           pkgs/main/osx-arm64::readline-8.2-h1a28f6b_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;setuptools         pkgs/main/osx-arm64::setuptools-68.2.2-py312hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sqlite             pkgs/main/osx-arm64::sqlite-3.41.2-h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tk                 pkgs/main/osx-arm64::tk-8.6.12-hb8d0fd4_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tzdata             pkgs/main/noarch::tzdata-2024a-h04d1e81_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wheel              pkgs/main/osx-arm64::wheel-0.41.2-py312hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xz                 pkgs/main/osx-arm64::xz-5.4.6-h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;zlib               pkgs/main/osx-arm64::zlib-1.2.13-h5a0b063_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Proceed ([y]/n)? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Downloading and Extracting Packages:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Preparing transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Verifying transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Executing transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# To activate this environment, use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#     $ conda activate ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# To deactivate an active environment, use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#     $ conda deactivate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Activating the environment&lt;/h3&gt;
&lt;p&gt;With the dedicated Ansible environment created, we can activate using the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Activating the Ansible environment&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda activate ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We can now check the path of the &lt;code&gt;python&lt;/code&gt; binary by running &lt;code&gt;which python&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Checking which python is being used&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;which python&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/opt/homebrew/Caskroom/miniconda/base/envs/ansible/bin/python&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, it is using the &lt;code&gt;python&lt;/code&gt; binary within our environment, which means that Ansible can now be installed along with some collections:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Ansible and some collections&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;python -m pip install ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible-galaxy collection install azure.azcollection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;python -m pip install -r ~/.ansible/collections/ansible_collections/azure/azcollection/requirements-azure.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The screen below shows an example of what the terminal looks like when switched to the Ansible environment:&lt;/p&gt;

&lt;p&gt;So far so good !!!&lt;/p&gt;
&lt;h2&gt;Digging Deeper&lt;/h2&gt;
&lt;p&gt;Here are some notes on package management and &lt;a href=&quot;/glossary/virtual-environment/&quot;&gt;virtual environment&lt;/a&gt; management.&lt;/p&gt;
&lt;h3&gt;Pip vs Conda&lt;/h3&gt;
&lt;p&gt;As well as being a Python environment manager, Conda also provides it own package management, so rather that running pip using&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Ansible using pip&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;python -m pip install ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I could have run the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The Conda install command&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda install conda-forge::ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This would install Ansible from the main &lt;span&gt;&lt;a href=&quot;https://anaconda.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Anaconda&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; site, which hosts its own files at  &lt;span&gt;&lt;a href=&quot;https://anaconda.org/conda-forge/ansible&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;conda-forge/ansible&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; for Ansible, if I had taken this route the installation would have looked like the following:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Going through the installation&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Channels:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- defaults&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- conda-forge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Platform: osx-arm64&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Collecting package metadata (repodata.json): done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Solving environment: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Package Plan ##&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;environment location: /opt/homebrew/Caskroom/miniconda/base/envs/ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;added / updated specs:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- conda-forge::ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The following packages will be downloaded:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;package                    |            build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;---------------------------|-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible-9.4.0              |     pyh707e725_0        20.7 MB  conda-forge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible-core-2.16.5        |     pyh707e725_0         1.3 MB  conda-forge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cryptography-42.0.5        |  py312hd4332d6_0         1.4 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;jinja2-3.1.3               |  py312hca03da5_0         327 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;markupsafe-2.1.3           |  py312h80987f9_0          24 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;packaging-23.2             |  py312hca03da5_0         169 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pyyaml-6.0.1               |  py312h80987f9_0         172 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resolvelib-0.8.1           |  py312hca03da5_0          30 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;yaml-0.2.5                 |       h1a28f6b_0          71 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                                           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Total:        24.1 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible            conda-forge/noarch::ansible-9.4.0-pyh707e725_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible-core       conda-forge/noarch::ansible-core-2.16.5-pyh707e725_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cffi               pkgs/main/osx-arm64::cffi-1.16.0-py312h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cryptography       pkgs/main/osx-arm64::cryptography-42.0.5-py312hd4332d6_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;jinja2             pkgs/main/osx-arm64::jinja2-3.1.3-py312hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;markupsafe         pkgs/main/osx-arm64::markupsafe-2.1.3-py312h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;packaging          pkgs/main/osx-arm64::packaging-23.2-py312hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pycparser          pkgs/main/noarch::pycparser-2.21-pyhd3eb1b0_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pyyaml             pkgs/main/osx-arm64::pyyaml-6.0.1-py312h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resolvelib         pkgs/main/osx-arm64::resolvelib-0.8.1-py312hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;yaml               pkgs/main/osx-arm64::yaml-0.2.5-h1a28f6b_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Proceed ([y]/n)? n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CondaSystemExit: Exiting.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;However, in my case, as I wanted to keep things simple - I am sticking to what I know and will use &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I did, however, add the following &lt;code&gt;alias&lt;/code&gt; for &lt;code&gt;pip&lt;/code&gt; in my &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/dotfiles&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;dotfile&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to call &lt;code&gt;python -m pip&lt;/code&gt; each time I run the &lt;code&gt;pip&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Python 3.10 (TEXT)&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;alias pip=&apos;python -m pip&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This means that if I run ￼&lt;code&gt;pip install ansible&lt;/code&gt;￼ , the command ￼&lt;code&gt;python -m pip install ansible&lt;/code&gt;￼ would be run, so I can be sure that I am not calling the &lt;code&gt;pip&lt;/code&gt; executable from some other random place.&lt;/p&gt;
&lt;h3&gt;Installing a different Python version&lt;/h3&gt;
&lt;p&gt;Now, not every piece of code supports the latest and greatest version of Python. Luckily, as you might have already guessed, we can define what version of Python to install when you create your virtual environment using Conda.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Python 3.10&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda create -n test python=3.10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, we are requesting that Python 3.10 be installed instead of Python 3.12, which is what our other virtual environments use.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The output of installing Python 3.10&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Channels:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- defaults&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Platform: osx-arm64&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Collecting package metadata (repodata.json): done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Solving environment: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Package Plan ##&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;environment location: /opt/homebrew/Caskroom/miniconda/base/envs/test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;added / updated specs:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- python=3.10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The following packages will be downloaded:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;package                    |            build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;---------------------------|-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pip-23.3.1                 |  py310hca03da5_0         2.7 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;python-3.10.14             |       hb885b13_0        13.0 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;setuptools-68.2.2          |  py310hca03da5_0         942 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wheel-0.41.2               |  py310hca03da5_0         107 KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                                           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Total:        16.7 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The following NEW packages will be INSTALLED:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bzip2              pkgs/main/osx-arm64::bzip2-1.0.8-h80987f9_5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ca-certificates    pkgs/main/osx-arm64::ca-certificates-2024.3.11-hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;libffi             pkgs/main/osx-arm64::libffi-3.4.4-hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ncurses            pkgs/main/osx-arm64::ncurses-6.4-h313beb8_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;openssl            pkgs/main/osx-arm64::openssl-3.0.13-h1a28f6b_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pip                pkgs/main/osx-arm64::pip-23.3.1-py310hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;python             pkgs/main/osx-arm64::python-3.10.14-hb885b13_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;readline           pkgs/main/osx-arm64::readline-8.2-h1a28f6b_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;setuptools         pkgs/main/osx-arm64::setuptools-68.2.2-py310hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sqlite             pkgs/main/osx-arm64::sqlite-3.41.2-h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tk                 pkgs/main/osx-arm64::tk-8.6.12-hb8d0fd4_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tzdata             pkgs/main/noarch::tzdata-2024a-h04d1e81_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wheel              pkgs/main/osx-arm64::wheel-0.41.2-py310hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xz                 pkgs/main/osx-arm64::xz-5.4.6-h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;zlib               pkgs/main/osx-arm64::zlib-1.2.13-h5a0b063_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Proceed ([y]/n)? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Downloading and Extracting Packages:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Preparing transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Verifying transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Executing transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# To activate this environment, use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#     $ conda activate test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# To deactivate an active environment, use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#     $ conda deactivate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the screen below, switching to the new virtual environment loads the older version:&lt;/p&gt;

&lt;h3&gt;Managing Virtual Environments&lt;/h3&gt;
&lt;p&gt;You can list all of your virtual environments using the command below:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Listing all envs&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda env list&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will return something like the following:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The output of conda env list&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# conda environments:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;base                  *  /opt/homebrew/Caskroom/miniconda/base&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible                  /opt/homebrew/Caskroom/miniconda/base/envs/ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;discogs                  /opt/homebrew/Caskroom/miniconda/base/envs/discogs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;test                     /opt/homebrew/Caskroom/miniconda/base/envs/test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now let’s remove the test virtual environment:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Removing the test env&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda remove -n test --all&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will nuke everything to do with the test virtual environment:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;The output of conda remove -n test --all&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Remove all packages in environment /opt/homebrew/Caskroom/miniconda/base/envs/test:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Package Plan ##&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;environment location: /opt/homebrew/Caskroom/miniconda/base/envs/test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The following packages will be REMOVED:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bzip2-1.0.8-h80987f9_5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ca-certificates-2024.3.11-hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;libffi-3.4.4-hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ncurses-6.4-h313beb8_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;openssl-3.0.13-h1a28f6b_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pip-23.3.1-py310hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;python-3.10.14-hb885b13_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;readline-8.2-h1a28f6b_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;setuptools-68.2.2-py310hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sqlite-3.41.2-h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tk-8.6.12-hb8d0fd4_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tzdata-2024a-h04d1e81_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wheel-0.41.2-py310hca03da5_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xz-5.4.6-h80987f9_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;zlib-1.2.13-h5a0b063_0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Proceed ([y]/n)? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Preparing transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Verifying transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Executing transaction: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Everything found within the environment (/opt/homebrew/Caskroom/miniconda/base/envs/test), including any conda environment configurations and any non-conda files, will be deleted. Do you wish to continue?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(y/[n])? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Updating packages&lt;/h3&gt;
&lt;p&gt;Even if, like me, you use &lt;code&gt;pip&lt;/code&gt; there are still some packages managed by Conda in your Virtual environment, you can update them by switching to the virtual environment you want to update and then run:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Updating the Conda managed packages in your env&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;conda update --all&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Managing Python environments can be a daunting task, especially when dealing with multiple projects and dependencies. However, by leveraging the power of Conda and Miniconda, you can streamline your Python environment management process and ensure a clean, efficient setup on your local machine.&lt;/p&gt;
&lt;p&gt;With Conda, you can easily create isolated Python environments for different projects, specifying the exact Python version and package requirements. This allows you to maintain separate environments for each project, avoiding conflicts and ensuring compatibility.&lt;/p&gt;
&lt;p&gt;By following the steps outlined in this post, you can install and configure Miniconda on your macOS system, create dedicated environments for tools like Ansible, and manage packages using either Conda’s package management or the familiar &lt;code&gt;pip&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Additionally, Conda provides flexibility in installing different Python versions within each environment, enabling you to work with projects that require specific Python versions seamlessly.&lt;/p&gt;
&lt;p&gt;With the ability to list, remove, and update virtual environments and packages, Conda offers a comprehensive solution for Python environment management. By incorporating Conda into your workflow, you can enhance your productivity, maintain a clean development setup, and efficiently manage your Python projects.&lt;/p&gt;
&lt;p&gt;So, whether you’re a beginner or an experienced Python developer, embracing Conda for Python environment management can greatly simplify your development process and help you focus on writing awesome code!&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-04-06-effortless-python-environment-management-on-mac-os-with-conda.C__CfSOC.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/04/06/conda-for-python-environment-management-on-macos-og.png</blog:ogImage><blog:readingTime>10</blog:readingTime><category>macOS</category><category>Python</category><category>Ansible</category><category>Code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Updating my dotfiles</title><link>https://www.russ.cloud/2024/04/02/updating-my-dotfiles/</link><guid isPermaLink="true">https://www.russ.cloud//2024/04/02/updating-my-dotfiles/</guid><description>For the first time in nearly a decade, I updated my dotfiles blog post.</description><pubDate>Tue, 02 Apr 2024 06:30:00 GMT</pubDate><content:encoded>&lt;p&gt;I decided to spring clean my &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/dotfiles&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Dotfiles repo&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, as it had been a while. Then I noticed that the last time I mentioned them on the blog was &lt;a href=&quot;/2014/08/10/dotfiles/&quot;&gt;nearly ten years ago&lt;/a&gt;, so I wanted to give a quick update. The most significant change is that I have moved to using &lt;span&gt;&lt;a href=&quot;https://www.zsh.org/&quot; title=&quot;Zsh&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Zsh&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://ohmyz.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Oh My Zsh&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; since &lt;a href=&quot;/glossary/zsh/&quot;&gt;Zsh&lt;/a&gt; became the default Shell in macOS.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;This is all VERY customised to my requirements, and there are a lot of hard-coded bits in there. You probably don’t want to blindly copy and paste any of the commands below as things will break!!!&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;Install everything needed for the dotfiles&lt;/h2&gt;
&lt;p&gt;The commands below assume you are starting from a clean installation, which I do at least once a year. The first thing is to install Oh MyZsh:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Oh My ZSH&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sh -c &quot;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once installed, we can download and install the Fonts and Theme I use:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the fonts and theme&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew tap homebrew/cask-fonts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew install font-hack-nerd-font&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;There are some other tools called by the dot files, so lets install those too.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing and configuring the other bits&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew install tree pygments ffmpeg yt-dlp visual-studio-code drawio thefuck stow zoxide gh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gh extension install github/gh-copilot # enable the gh-copilot  extension&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gh copilot alias -- zsh # run one and accept the t&amp;amp;cs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Pulling and setting up the dotfiles&lt;/h2&gt;
&lt;p&gt;First, we need to pull &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/dotfiles/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;my dotfiles repo&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Some Command&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git clone git@github.com:russmckendrick/dotfiles.git ~/.dotfiles&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then, create backups of all of the existing files:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Backup some files&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mv ~/.zprofile ~/.dotfiles/backups/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mv ~/.zshrc ~/.dotfiles/backups/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mv ~/.gitconfig ~/.dotfiles/backups/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now we can create symbolic links to load contents from our &lt;code&gt;~/.dotfiles&lt;/code&gt; folder using &lt;code&gt;stow&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Create the Symbolic Links&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd ~/.dotfiles&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;stow .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;What does it look like?&lt;/h2&gt;
&lt;p&gt;Below are some screens of what my current terminal looks like:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-02-updating-my-dotfiles/images/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-02-updating-my-dotfiles/images/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-02-updating-my-dotfiles/images/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-02-updating-my-dotfiles/images/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-02-updating-my-dotfiles/images/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-02-updating-my-dotfiles/images/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-02-updating-my-dotfiles/images/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-02-updating-my-dotfiles/images/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also click on the link below to see it in action:&lt;/p&gt;

&lt;a href=&quot;https://asciinema.org/a/PTUqbpxikms7nFWNs4R7OhMQR&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://asciinema.org/a/PTUqbpxikms7nFWNs4R7OhMQR.svg&quot; alt=&quot;See what my terminal is like&quot; /&gt;&lt;/a&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this blog post, we walked through updating my &lt;a href=&quot;/glossary/dotfiles/&quot;&gt;dotfiles&lt;/a&gt; repository and setting up your development environment on a fresh macOS installation. I created a highly personalised and efficient terminal setup by using tools like Oh My Zsh, custom fonts, and a custom theme. While these dotfiles are tailored to my specific needs and may not be suitable for everyone to copy directly, they provide a great example of how to customize and streamline your development workflow. Keeping your dotfiles up to date and versioned in a repository is a great way to maintain consistency across your machines and make setting up new environments a breeze.&lt;/p&gt;
&lt;p&gt;If you have any questions, feel free to leave a comment.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-04-02-updating-my-dotfiles.CKGleD8s.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/04/02/updating-my-dotfiles-og.png</blog:ogImage><blog:readingTime>3</blog:readingTime><category>macos</category><category>code</category><category>life</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Saving money with Azure Logic Apps</title><link>https://www.russ.cloud/2024/04/01/saving-money-with-azure-logic-apps/</link><guid isPermaLink="true">https://www.russ.cloud//2024/04/01/saving-money-with-azure-logic-apps/</guid><description>Discover how Azure Logic Apps save you money by automating tasks like stopping and starting VMs and App Gateways. Follow this step-by-step guide to deploy cost-saving automation efficiently.</description><pubDate>Mon, 01 Apr 2024 10:30:00 GMT</pubDate><content:encoded>&lt;p&gt;During the day job, I have to get a little creative with controlling costs for &lt;a href=&quot;/glossary/azure/&quot;&gt;Azure&lt;/a&gt; resources; there are some everyday tasks and scenarios where &lt;a href=&quot;/glossary/azure-logic-apps/&quot;&gt;Azure Logic Apps&lt;/a&gt; can help you save money 💰.&lt;/p&gt;
&lt;p&gt;The first is quite a typical scenario: stopping and starting Azure Virtual Machines. There was a time when I would have just used an Azure &lt;a href=&quot;/glossary/automation/&quot;&gt;Automation&lt;/a&gt; Account and the Microsoft-supplied workflow. Still, Automation Accounts are a little (well, a lot) too reliant on PowerShell for my macOS using / Linux administrating tastes.&lt;/p&gt;
&lt;p&gt;Another common requirement I have encountered is Azure Application Gateways. With the retirement of v1 Azure Application Gateway SKUs, v2 SKUs can be expensive if you run multiple Azure Application Gateways for Dev, Test, and PreProd environments that are not needed 24/7. A little-known feature, mainly because you cannot do it from the Azure Portal, is that you can stop Azure Application Gateways. You will not be charged for them while they are in the stopped state.&lt;/p&gt;
&lt;p&gt;I could discuss some other common scenarios, but before we do that, let’s examine the LogicApps and workflow I have settled on for the two scenarios I mentioned.&lt;/p&gt;
&lt;h2&gt;Targeting Azure Virtual Machines&lt;/h2&gt;
&lt;p&gt;I have settled on a standard (ish) workflow using the Azure REST &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt;. We will need a few supporting resources and the Azure Logic App itself.&lt;/p&gt;
&lt;h3&gt;Deploying the Azure Logic App and supporting resources&lt;/h3&gt;
&lt;p&gt;Our first task is to clone the &lt;span&gt;&lt;a href=&quot;https://github.com/russmckendrick/money-saving-azure-logic-apps&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GitHub repo, which contains the JSON definition&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; of the Azure Logic Apps we will be deploying;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Cloning to accompanying repo&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git clone https://github.com/russmckendrick/money-saving-azure-logic-apps.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd money-saving-azure-logic-apps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the repo cloned, we can now create the Azure Resources; I will be using the Azure &lt;a href=&quot;/glossary/command-line-interface/&quot;&gt;CLI&lt;/a&gt; to do this, starting with setting some environment variables which you can update as needed:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting some environment variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-logicapps-blogpost-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export REGION=&quot;uksouth&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export SUBSCRIPTION_ID=$(az account show --query id --output tsv)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export MANAGED_ID_NAME=&quot;mi-logicapps-blogpost-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export LOGIC_APP_NAME=&quot;la-virtualMachineStopStart-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the variables in place, we can deploy the resource group;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Create the resource group&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The first resource we will need to deploy is a user-managed identity; we will be granting this ID permissions to do “stuff” in our Azure subscription and attaching it to the Azure Logic App to use when we connect to the Azure REST API :&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Create the user managed identity&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az identity create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $MANAGED_ID_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As the Azure Logic App will need to search for resources in the subscription and also be able to interact with Azure virtual machines; we should permit it to do just those tasks by granting our User Assigned Identity the Reader and Virtual Machine Contributor roles :&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Granting RBAC permissions&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az role assignment create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-principal-type &quot;ServicePrincipal&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-object &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query principalId --output tsv)&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--role &quot;Reader&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--scope &quot;/subscriptions/$SUBSCRIPTION_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az role assignment create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-principal-type &quot;ServicePrincipal&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-object &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query principalId --output tsv)&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--role &quot;Virtual Machine Contributor&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--scope &quot;/subscriptions/$SUBSCRIPTION_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the User Managed Identity in place, we can now create the Azure Logic App.&lt;/p&gt;
&lt;p&gt;To do this, run the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Create the Azure Logic App&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--mi-user-assigned &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query id --output tsv)&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--state &quot;Disabled&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--definition &quot;virtualMachineStopStart.json&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This part of the Azure CLI is, at the time of writing this post, in preview, so you may get the following prompt; if you do, follow the on-screen instructions:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Do you need to install the extension?&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The command requires the extension logic. Do you&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;want to install it now? The command will continue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;to run after the extension is installed. (Y/n)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once complete, you should have a Logic App with code imported and the User-Assigned Identity assigned to it. The Azure Logic App is also currently disabled as we haven’t finished the configuration yet; there are more bits of information we need to update.&lt;/p&gt;
&lt;p&gt;The Logic App &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; contains four Parameters these are;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;managedId&lt;/strong&gt;: This parameter must be updated to contain the full resource ID for the User Managed Identity we have assigned to the Logic App.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;subscriptionId&lt;/strong&gt;: This parameter must be updated with the ID of the Azure Subscription that contains the resources you want to manage with the Logic App.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;tagName&lt;/strong&gt;: The default value is ￼&lt;code&gt;virtualMachineStopStart&lt;/code&gt;￼  , so any Virtual Machines with this tag, with the value below, will have their power state managed by this Logic App.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;tagValue&lt;/strong&gt;: &lt;code&gt;included&lt;/code&gt; is the default here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luckily, we can use the Azure CLI to do that as well; here are the two commands, starting with one to update the &lt;strong&gt;managedId&lt;/strong&gt; parameter:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting the managedId parameter&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow update \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--set &quot;definition.parameters.managedId.defaultValue=$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query id --output tsv)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now the &lt;strong&gt;subscriptionId&lt;/strong&gt; parameter:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting the subscriptionId parameter&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow update \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--set &quot;definition.parameters.subscriptionId.defaultValue=$(az account show --query id --output tsv)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Logging into &lt;span&gt;&lt;a href=&quot;https://portal.azure.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;the Azure Portal&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;; you should see your newly deployed resources:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/deploying-the-logic-app-and-supporting-resources/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/deploying-the-logic-app-and-supporting-resources/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/deploying-the-logic-app-and-supporting-resources/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/deploying-the-logic-app-and-supporting-resources/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/deploying-the-logic-app-and-supporting-resources/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/deploying-the-logic-app-and-supporting-resources/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/deploying-the-logic-app-and-supporting-resources/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/deploying-the-logic-app-and-supporting-resources/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We are now ready to apply our cost-saving automation; well, we would be if we had the resources, so let’s launch some Azure Virtual Machines.&lt;/p&gt;
&lt;h3&gt;Creating test Azure Virtual Machine resources&lt;/h3&gt;
&lt;p&gt;We will go through the Logic App in more detail soon; for now, let’s create some Azure Virtual Machines we can target with the Azure Logic App. The commands below will launch three virtual machines, two of which are tagged and will be targeted by our Azure Logic App:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Launching three test VMs&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-demo-vms-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export REGION=uksouth&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export VNET_NAME=vnet-demo-vms-uks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export SUBNET_NAME=sub-vms&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export VM_IMAGE=&quot;Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts-gen2:latest&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export VM_NAME=&quot;vm-demo&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az network vnet create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $VNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--address-prefix 10.0.0.0/24 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--subnet-name $SUBNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--subnet-prefix 10.0.0.0/27&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;for i in {1..3}; do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if [ $i -le 2 ]; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;az vm create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $VM_NAME-$(openssl rand -hex 5)-uks \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--image $VM_IMAGE \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--admin-username azureuser \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--vnet-name $VNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--subnet $SUBNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--generate-ssh-keys \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--public-ip-address &quot;&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--size Standard_B2ms \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--tags &quot;virtualMachineStopStart=included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;az vm create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $VM_NAME-$(openssl rand -hex 5)-uks \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--image $VM_IMAGE \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--admin-username azureuser \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--vnet-name $VNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--subnet $SUBNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--generate-ssh-keys \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--public-ip-address &quot;&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--size Standard_B2ms&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/creating-some-target-resources/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/creating-some-target-resources/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/creating-some-target-resources/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/creating-some-target-resources/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With the resources now deployed, let’s run the Azure Logic App and start saving money.&lt;/p&gt;
&lt;h3&gt;Running the Azure Logic App&lt;/h3&gt;
&lt;p&gt;In &lt;span&gt;&lt;a href=&quot;https://portal.azure.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;the Azure Portal&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, go to your Logic App and press the &lt;strong&gt;Enable&lt;/strong&gt; button. This will trigger a Run, and clicking on &lt;strong&gt;Refresh&lt;/strong&gt; should show you that a run is in progress:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-stop/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-stop/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-stop/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-stop/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-stop/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-stop/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-stop/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-stop/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the screens above, the Logic App ran, and because the two tagged Virtual Machines were running - it powered them down; well, actually - it Stopped and Deallocated them, so we are not charged while they are in the Stopped state.&lt;/p&gt;
&lt;p&gt;Running the Logic App for a second time will start the two tagged machines as they were both Stopped (Deallocted):&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-start/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-start/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-start/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-start/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-start/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-start/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-start/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/vm-running-the-logic-app-start/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you are following along and have deployed the example resources, now would be a good time to terminate them. You can do this by running:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Removing the demo Virtual Machines and Group&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-demo-vms-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group delete --name $RESOURCE_GROUP_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Working through the Azure Logic App&lt;/h3&gt;
&lt;p&gt;As you may have already spotted in the Logic app designer section, there are quite a few steps defined, so let’s work through them now; below is the full workflow:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/vm-full-logic-app.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/vm-full-logic-app.png&quot; alt=&quot;The full machine workflow&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2087&quot; height=&quot;5338&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So, let’s start at the beginning.&lt;/p&gt;
&lt;h4&gt;Recurrence (Azure Virtual Machine)&lt;/h4&gt;
&lt;p&gt;This is our trigger, by default, is set to run at 07:00 and 18:00 on Monday, Tuesday, Wednesday, Thursday, and Friday every week. The for this step is below:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Recurrence&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;recurrence&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;frequency&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Week&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;interval&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;schedule&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hours&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;7&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;18&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;minutes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;weekDays&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Monday&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Tuesday&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Wednesday&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Thursday&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;Friday&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;timeZone&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GMT Standard Time&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h4&gt;Get a list of all resources tagged to be managed (Azure Virtual Machine)&lt;/h4&gt;
&lt;p&gt;This is where our first HTTP call to the Azure REST API takes place; it makes a &lt;strong&gt;GET&lt;/strong&gt; request to the &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/rest/api/resources/resources/list?view=rest-resources-2021-04-01&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://management.azure.com/subscriptions/{subscriptionId}/resources?api-version=2021-04-01&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; API and filters using the contents of the &lt;strong&gt;tagName&lt;/strong&gt; and &lt;strong&gt;tagValue&lt;/strong&gt; parameters:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://management.azure.com/subscriptions/@&amp;amp;#123;parameters(&apos;subscriptionId&apos;)&amp;amp;#125;/resources?api-version=2021-04-01&amp;amp;$filter=tagName eq &apos;@{parameters(&apos;tagName&apos;)}&apos; and tagValue eq &apos;@{parameters(&apos;tagValue&apos;)}&apos;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GET&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;identity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{parameters(&apos;managedId&apos;)}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ManagedServiceIdentity&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runtimeConfiguration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contentTransfer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;transferMode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Chunked&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When we first ran the Azure Logic App, this is an example of the output that was passed onto the following task:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/RG-DEMO-VMS-UKS/providers/Microsoft.Compute/disks/vm-demo-d62ef52290-uks_OsDisk_1_23c583676cb8413c9ac875058f5c1f30&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-d62ef52290-uks_OsDisk_1_23c583676cb8413c9ac875058f5c1f30&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Compute/disks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sku&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Premium_LRS&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tier&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Premium&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Compute/virtualMachines/vm-demo-d62ef52290-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/RG-DEMO-VMS-UKS/providers/Microsoft.Compute/disks/vm-demo-e8ab6db95a-uks_OsDisk_1_5be3d505ec324ef3a411406f24e13bf2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-e8ab6db95a-uks_OsDisk_1_5be3d505ec324ef3a411406f24e13bf2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Compute/disks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sku&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Premium_LRS&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tier&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Premium&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Compute/virtualMachines/vm-demo-e8ab6db95a-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Compute/virtualMachines/vm-demo-d62ef52290-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-d62ef52290-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Compute/virtualMachines&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Compute/virtualMachines/vm-demo-e8ab6db95a-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-e8ab6db95a-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Compute/virtualMachines&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Network/networkInterfaces/vm-demo-d62ef52290-uksVMNic&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-d62ef52290-uksVMNic&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/networkInterfaces&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Regular&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Network/networkInterfaces/vm-demo-e8ab6db95a-uksVMNic&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-e8ab6db95a-uksVMNic&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/networkInterfaces&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Regular&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Network/networkSecurityGroups/vm-demo-d62ef52290-uksNSG&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-d62ef52290-uksNSG&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/networkSecurityGroups&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Network/networkSecurityGroups/vm-demo-e8ab6db95a-uksNSG&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-e8ab6db95a-uksNSG&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/networkSecurityGroups&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h4&gt;Filter everything but the VMs we are managing (Azure Virtual Machine)&lt;/h4&gt;
&lt;p&gt;While testing the Logic App, I found that more than just the virtual machine resource could be tagged with &lt;strong&gt;tagName&lt;/strong&gt;; for example, in the output of the previous step, disks, network interfaces, and more all have the same tag. Now, this would result in an error when running later parts of the workflow, so here we filtered down the list of resources the last step fetched to where the resource &lt;strong&gt;type&lt;/strong&gt; is equal to &lt;strong&gt;Microsoft.Compute/virtualMachines&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Query&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@body(&apos;Get_a_list_of_all_resources_tagged_to_be_managed&apos;)[&apos;value&apos;]&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@equals(item()[&apos;type&apos;],&apos;Microsoft.Compute/virtualMachines&apos;)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Get_a_list_of_all_resources_tagged_to_be_managed&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;There is an annoying limitation when searching for resources using the &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/rest/api/resources/resources/list?view=rest-resources-2021-04-01&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://management.azure.com/subscriptions/{subscriptionId}/resources?api-version=2021-04-01&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; API. While you can filter on any number of &lt;strong&gt;tags&lt;/strong&gt; you like or even the resource &lt;strong&gt;type&lt;/strong&gt;, you can not filter on both simultaneously, which is why we have this extra step.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Once this filter has been applied, we are now left with details on the two machines from the test run:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Compute/virtualMachines/vm-demo-d62ef52290-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-d62ef52290-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Compute/virtualMachines&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Compute/virtualMachines/vm-demo-e8ab6db95a-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-e8ab6db95a-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Compute/virtualMachines&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;virtualMachineStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h4&gt;Process just the VMs (Azure Virtual Machine)&lt;/h4&gt;
&lt;p&gt;Now that we have a JSON object which contains only the information on virtual machines with the tags that indicate the workflow should be working with them, we need to parse the JSON object and turn the data into something we can use; this task takes the schema and does just that:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ParseJson&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@body(&apos;Filter_everything_but_the_VMs_we_are_managing&apos;)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;schema&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;items&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scheduledVMPowerOnOff&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;object&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;id&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;kind&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;managedBy&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;location&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;tags&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;object&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;array&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Filter_everything_but_the_VMs_we_are_managing&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;While the output looks the same as the last step, it is now referenced in a way that allows us to loop over it with a for each task.&lt;/p&gt;
&lt;h4&gt;For Each VM (Azure Virtual Machine)&lt;/h4&gt;
&lt;p&gt;This is the first of two loops we need to run, the second of which is nested in this loop.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Foreach&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@body(&apos;Process_just_the_VMs&apos;)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;The rest in the for each task are detailed below are displayed here&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Process_just_the_VMs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can see in the screens below it looped through two virtual machines:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/vm-1of2.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/vm-1of2.png&quot; alt=&quot;VM 1of 2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2166&quot; height=&quot;690&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Clicking on the right arrow will take us to the second virtual machine:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/vm-2of2.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/vm-2of2.png&quot; alt=&quot;VM 2 of 2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2166&quot; height=&quot;690&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now, let’s see what we ran for each virtual machine.&lt;/p&gt;
&lt;h5&gt;Get some information on the Virtual Machine (Azure Virtual Machine)&lt;/h5&gt;
&lt;p&gt;The first task of the loop gets information about the virtual machine we are currently processing, it does this by making a GET request to the &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/instance-view?view=rest-compute-2024-03-01&amp;amp;tabs=HTTP&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachines/{vmName}/instanceView?api-version=2024-03-01&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; Azure REST API endpoint:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://management.azure.com@&amp;amp;#123;items(&apos;For_Each_VM&apos;)[&apos;id&apos;]&amp;amp;#125;/instanceView?api-version=2024-03-01&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GET&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;identity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{parameters(&apos;managedId&apos;)}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ManagedServiceIdentity&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runtimeConfiguration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contentTransfer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;transferMode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Chunked&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the code above, we are using &lt;code&gt;{items(&apos;For_Each_VM&apos;)[&apos;id&apos;]}&lt;/code&gt; - this contains all of the information we need to connect to the endpoint, for example:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Text&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-vms-uks/providers/Microsoft.Compute/virtualMachines/vm-demo-e8ab6db95a-uks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Which returns the following output:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;computerName&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-e8ab6db95a-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;osName&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ubuntu&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;osVersion&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;22.04&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;vmAgent&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;vmAgentVersion&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2.10.0.8&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;statuses&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ProvisioningState/succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;level&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Info&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;displayStatus&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Ready&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Guest Agent is running&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2024-03-31T12:49:57+00:00&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;extensionHandlers&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;disks&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;vm-demo-e8ab6db95a-uks_OsDisk_1_5be3d505ec324ef3a411406f24e13bf2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;statuses&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ProvisioningState/succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;level&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Info&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;displayStatus&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Provisioning succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2024-03-31T12:43:14.0729588+00:00&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hyperVGeneration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;V2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;statuses&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ProvisioningState/succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;level&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Info&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;displayStatus&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Provisioning succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2024-03-31T12:43:41.2609336+00:00&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;PowerState/running&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;level&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Info&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;displayStatus&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;VM running&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;So, we now need to know the power status.&lt;/p&gt;
&lt;h5&gt;Filter just the status about the PowerState (Azure Virtual Machine)&lt;/h5&gt;
&lt;p&gt;Again, we are running another query - we need to do this because there are two statuses being returned from the initial API call, one for the &lt;strong&gt;ProvisioningState&lt;/strong&gt; and the second for the &lt;strong&gt;PowerState&lt;/strong&gt; - it is this one we need to work with:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Query&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@body(&apos;Get_some_information_on_the_Virtual_Machine&apos;)[&apos;statuses&apos;]&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@contains(item()[&apos;code&apos;],&apos;PowerState&apos;)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Get_some_information_on_the_Virtual_Machine&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This leaves us with the following output to use as input for the next task:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;PowerState/running&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;level&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Info&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;displayStatus&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;VM running&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h5&gt;Process just the VMs power state&lt;/h5&gt;
&lt;p&gt;Again, we need to get the data into a format we can use:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ParseJson&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@body(&apos;Filter_just_the_status_about_the_PowerState&apos;)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;schema&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;items&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;displayStatus&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;level&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;string&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;code&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;level&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;displayStatus&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;object&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;array&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Filter_just_the_status_about_the_PowerState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We then take this and pass it to our nest loop.&lt;/p&gt;
&lt;h4&gt;For each PowerState code (Azure Virtual Machine)&lt;/h4&gt;
&lt;p&gt;Because we are going to be using a conditional, we need to use another loop to get the output from above into a state we can use:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Foreach&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@outputs(&apos;Process_just_the_VMs_power_state&apos;)[&apos;body&apos;]&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;The rest in the for each task are detailed below are displayed here&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Process_just_the_VMs_power_state&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h5&gt;Condition (Azure Virtual Machine)&lt;/h5&gt;
&lt;p&gt;Here we have the final step: if &lt;code&gt;@items(&apos;For_each_PowerState_code&apos;)[&apos;code&apos;]&lt;/code&gt; is equal to &lt;code&gt;PowerState/deallocated&lt;/code&gt;, ie &lt;strong&gt;True&lt;/strong&gt;, then we need to power the virtual machine on.&lt;/p&gt;
&lt;p&gt;To do this we make a &lt;strong&gt;POST&lt;/strong&gt; to &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/start?view=rest-compute-2024-03-01&amp;amp;tabs=HTTP&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachines/{vmName}/start?api-version=2024-03-01&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;@items(&apos;For_each_PowerState_code&apos;)[&apos;code&apos;]&lt;/code&gt; is not equal to &lt;code&gt;PowerState/deallocated&lt;/code&gt;, ie &lt;strong&gt;False&lt;/strong&gt;,  then we assume that the resource is running and we need to deallocate by doing  a &lt;strong&gt;POST&lt;/strong&gt; to &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/deallocate?view=rest-compute-2024-03-01&amp;amp;tabs=HTTP&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachines/{vmName}/deallocate?api-version=2024-03-01&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The JSON for the conditional is below:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;If&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;expression&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;equals&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;@items(&apos;For_each_PowerState_code&apos;)[&apos;code&apos;]&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;PowerState/deallocated&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Start_Virtual_Machine&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://management.azure.com@&amp;amp;#123;items(&apos;For_Each_VM&apos;)[&apos;id&apos;]&amp;amp;#125;/start?api-version=2024-03-01&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;POST&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;identity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{parameters(&apos;managedId&apos;)}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ManagedServiceIdentity&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runtimeConfiguration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contentTransfer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;transferMode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Chunked&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Deallocate_Virtual_Machine&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://management.azure.com@&amp;amp;#123;items(&apos;For_Each_VM&apos;)[&apos;id&apos;]&amp;amp;#125;/deallocate?api-version=2024-03-01&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;POST&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;identity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{parameters(&apos;managedId&apos;)}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ManagedServiceIdentity&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runtimeConfiguration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contentTransfer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;transferMode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Chunked&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;In our first run, this final task’s output was &lt;strong&gt;False&lt;/strong&gt;, so the virtual machines were deallocated, as you can see from the results below:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/vm-2of2results.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/vm-2of2results.png&quot; alt=&quot;The full machine workflow&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1726&quot; height=&quot;3018&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Let’s now see what the approach for an Azure Application Gateway looks like.&lt;/p&gt;
&lt;h2&gt;Targeting Azure Application Gateways&lt;/h2&gt;
&lt;p&gt;As already mentioned, there is no way to stop an Application Gateway in the Azure portal; you need to use the Azure CLI or Powershell - both of which call the Azure REST API - so how can we do the same with an Azure Logic App?&lt;/p&gt;
&lt;h3&gt;Deploying the Logic App and supporting resources&lt;/h3&gt;
&lt;p&gt;Again, our first task is to clone the &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; repo, which contains the JSON definition of the Logic Apps we will be deploying. If you already have this, open a new terminal session and change to the &lt;code&gt;money-saving-azure-logic-apps&lt;/code&gt; folder.:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Cloning to accompanying repo&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;git clone git@github.com:russmckendrick/money-saving-azure-logic-apps.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd money-saving-azure-logic-apps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, we can set some environment variables:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting some environment variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-logicapps-blogpost-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export REGION=&quot;uksouth&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export SUBSCRIPTION_ID=$(az account show --query id --output tsv)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export MANAGED_ID_NAME=&quot;mi-logicapps-blogpost-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export LOGIC_APP_NAME=&quot;la-applicationGatewayStopStart-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The commands below are repeated from the last section of the post, so if you have already run them, you can move on to the next set of commands; if you haven’t, then we need to create the resource group, user-managed identity and assign Reader access:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Create the resource group, identity and assign Reader access&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group create --name $RESOURCE_GROUP_NAME --location $REGION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az identity create --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az role assignment create --assignee-principal-type &quot;ServicePrincipal&quot; --assignee-object &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query principalId --output tsv)&quot; --role &quot;Reader&quot; --scope &quot;/subscriptions/$SUBSCRIPTION_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next, we need to assign the user-managed identity Network Contributor access to our subscription:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Granting Network Contributor permissions&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az role assignment create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-principal-type &quot;ServicePrincipal&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--assignee-object &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query principalId --output tsv)&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--role &quot;Network Contributor&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--scope &quot;/subscriptions/$SUBSCRIPTION_ID&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The access is sorted, we can create the Logic App:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Create the Logic App&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--mi-user-assigned &quot;$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query id --output tsv)&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--state &quot;Disabled&quot; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--definition &quot;applicationGatewayStopStart.json&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now we can update the parameters:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting the managedId and subscriptionId parameters&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow update \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--set &quot;definition.parameters.managedId.defaultValue=$(az identity show --resource-group $RESOURCE_GROUP_NAME --name $MANAGED_ID_NAME --query id --output tsv)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az logic workflow update \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $LOGIC_APP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--set &quot;definition.parameters.subscriptionId.defaultValue=$(az account show --query id --output tsv)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;So far, it’s very similar to the workflow for Azure Virtual Machines which I aimed for with this approach. This time, the &lt;strong&gt;tagName&lt;/strong&gt; we are looking for is &lt;strong&gt;applicationGatewayStopStart&lt;/strong&gt;, and the &lt;strong&gt;tagValue&lt;/strong&gt; is again &lt;strong&gt;included&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Creating an Azure Application Gateway&lt;/h3&gt;
&lt;p&gt;Again, for testing, we need a resource to target; the commands below deploy an Azure Application Gateway with the most basic configuration I could get away with:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Launching an Azure Application Gateway&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export RESOURCE_GROUP_NAME=&quot;rg-demo-appgw-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export REGION=uksouth&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export VNET_NAME=vnet-demo-appgw-uks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export AG_SUBNET_NAME=sub-appwg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export BACKEND_SUBNET_NAME=sub-vms&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export AG_PUBLIC_IP_NAME=pip-agw-demo-appgw-uks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export APP_GATEWAY_NAME=agw-demo-appgw-uks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az group create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az network vnet create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $VNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--address-prefix 10.21.0.0/16 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--subnet-name $AG_SUBNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--subnet-prefix 10.21.0.0/24&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az network vnet subnet create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $BACKEND_SUBNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--vnet-name $VNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--address-prefix 10.21.1.0/24&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az network public-ip create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $AG_PUBLIC_IP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--allocation-method Static \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--sku Standard&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az network application-gateway create \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--name $APP_GATEWAY_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--location $REGION \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--resource-group $RESOURCE_GROUP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--capacity 2 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--sku Standard_v2 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--public-ip-address $AG_PUBLIC_IP_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--vnet-name $VNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--subnet $AG_SUBNET_NAME \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--priority 100 \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--tags &quot;applicationGatewayStopStart=included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The Azure Application Gateway will finish deploying in about 10 minutes, so now would be a good time to grab a drink ☕.&lt;/p&gt;
&lt;h3&gt;Running the Logic App (Azure Application Gateway)&lt;/h3&gt;
&lt;p&gt;In &lt;span&gt;&lt;a href=&quot;https://portal.azure.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;the Azure Portal&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, go to your Azure Logic App and press the &lt;strong&gt;Enable&lt;/strong&gt; button. This will trigger a Run, and clicking on &lt;strong&gt;Refresh&lt;/strong&gt; should show you that a run is in progress:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/05.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-stop/05.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the screens above, the Azure Logic App ran, and because the Application Gateway had an &lt;strong&gt;Operational State&lt;/strong&gt; of &lt;strong&gt;Running&lt;/strong&gt;, it Stopped the Azure Application Gateway. You can check the &lt;strong&gt;Operational State&lt;/strong&gt; by going to the Azure Application Gateway in the Azure portal and going to &lt;strong&gt;Properties&lt;/strong&gt; in the left-hand menu.&lt;/p&gt;
&lt;p&gt;Running the Azure Logic App for a second time will start the Azure Application Gateway with an &lt;strong&gt;Operational State&lt;/strong&gt; of &lt;strong&gt;Running&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/05.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-04-01-saving-money-with-azure-logic-apps/images/agw-running-the-logic-app-start/05.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;At this point, if you are testing, you should terminate the Azure Application Gateway so you don’t end up getting an unexpected cost (even with the Azure Logic App in place it’s not cheap 😃)&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Removing the demo Virtual Machines and Group&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; RESOURCE_GROUP_NAME&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rg-demo-appgw-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;group&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--name&lt;/span&gt;&lt;span&gt; $RESOURCE_GROUP_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Working through the Azure Logic App&lt;/h3&gt;
&lt;p&gt;As you may have already spotted in the Logic app designer section, the workflow looks pretty similar:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/agw-full-logic-app.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/agw-full-logic-app.png&quot; alt=&quot;The full machine workflow&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2087&quot; height=&quot;4921&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So let’s work through it.&lt;/p&gt;
&lt;h4&gt;Recurrence &amp;amp; Get a list of all resources tagged to be managed (Azure Application Gateway)&lt;/h4&gt;
&lt;p&gt;Both of these steps are the same as detailed in &lt;a href=&quot;#recurrence&quot;&gt;Recurrence&lt;/a&gt; and &lt;a href=&quot;#recurrence&quot;&gt;Get a list of all resources tagged to be managed&lt;/a&gt; when we covered the Azure Virtual Machine workflow. The output on the first example run was:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;agw-demo-appgw-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;kind&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;managedBy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;applicationGatewayStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h4&gt;Filter everything but the App Gateways we are managing (Azure Application Gateway)&lt;/h4&gt;
&lt;p&gt;Here, we are filtering on &lt;strong&gt;Microsoft.Network/applicationGateways&lt;/strong&gt;  resource type incase a supporting resource, such as the public IP address, just happens to have the tag applied:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Query&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@body(&apos;Get_a_list_of_all_resources_tagged_to_be_managed&apos;)[&apos;value&apos;]&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@equals(item()[&apos;type&apos;],&apos;Microsoft.Network/applicationGateways&apos;)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Get_a_list_of_all_resources_tagged_to_be_managed&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h4&gt;For Each App Gateway (Azure Application Gateway)&lt;/h4&gt;
&lt;p&gt;So begins our loop; this differs slightly in that there is no nested loop this time as all of the information we need is available in a single place rather than the multiple statuses we had returned when working with Azure Virtual Machines:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Foreach&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;foreach&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@body(&apos;Procetipless_just_the_App_Gateways&apos;)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;The rest in the for each task are detailed below are displayed here&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Process_just_the_App_Gateways&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h5&gt;Get some information on the Application Gateway (Azure Application Gateway)&lt;/h5&gt;
&lt;p&gt;This performs a &lt;strong&gt;GET&lt;/strong&gt; against &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/rest/api/application-gateway/application-gateways/get?view=rest-application-gateway-2023-09-01&amp;amp;tabs=HTTP&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/applicationGateways/{applicationGatewayName}?api-version=2023-09-01&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; which returns information on the Azure Application Gateway:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://management.azure.com@&amp;amp;#123;items(&apos;For_Each_App_Gateway&apos;)[&apos;id&apos;]&amp;amp;#125;?api-version=2023-09-01&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;GET&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;identity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{parameters(&apos;managedId&apos;)}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ManagedServiceIdentity&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runtimeConfiguration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contentTransfer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;transferMode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Chunked&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the output below, there is a lot of information returned:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;agw-demo-appgw-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;etag&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;W/&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;5599fe28-15f0-40b8-a25d-56971263aaf8&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;uksouth&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;applicationGatewayStopStart&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;included&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;provisioningState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;resourceGuid&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;39ae7e1b-3890-4bc5-a12d-37f885c607aa&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sku&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Standard_v2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tier&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Standard_v2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;family&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Generation_2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;capacity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;operationalState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Running&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;gatewayIPConfigurations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;appGatewayFrontendIP&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/gatewayIPConfigurations/appGatewayFrontendIP&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;etag&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;W/&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;5599fe28-15f0-40b8-a25d-56971263aaf8&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;provisioningState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subnet&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/virtualNetworks/vnet-demo-appgw-uks/subnets/sub-appwg&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways/gatewayIPConfigurations&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sslCertificates&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;trustedRootCertificates&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;trustedClientCertificates&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sslProfiles&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;frontendIPConfigurations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;appGatewayFrontendIP&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/frontendIPConfigurations/appGatewayFrontendIP&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;etag&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;W/&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;5599fe28-15f0-40b8-a25d-56971263aaf8&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways/frontendIPConfigurations&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;provisioningState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;privateIPAllocationMethod&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Dynamic&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;publicIPAddress&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/publicIPAddresses/pip-agw-demo-appgw-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;httpListeners&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/httpListeners/appGatewayHttpListener&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;frontendPorts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;appGatewayFrontendPort&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/frontendPorts/appGatewayFrontendPort&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;etag&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;W/&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;5599fe28-15f0-40b8-a25d-56971263aaf8&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;provisioningState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;httpListeners&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/httpListeners/appGatewayHttpListener&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways/frontendPorts&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;backendAddressPools&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;appGatewayBackendPool&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/backendAddressPools/appGatewayBackendPool&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;etag&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;W/&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;5599fe28-15f0-40b8-a25d-56971263aaf8&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;provisioningState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;backendAddresses&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;requestRoutingRules&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/requestRoutingRules/rule1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways/backendAddressPools&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;loadDistributionPolicies&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;backendHttpSettingsCollection&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;appGatewayBackendHttpSettings&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/backendHttpSettingsCollection/appGatewayBackendHttpSettings&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;etag&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;W/&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;5599fe28-15f0-40b8-a25d-56971263aaf8&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;provisioningState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;protocol&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cookieBasedAffinity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Disabled&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;connectionDraining&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enabled&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;drainTimeoutInSec&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pickHostNameFromBackendAddress&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;requestTimeout&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;requestRoutingRules&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/requestRoutingRules/rule1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways/backendHttpSettingsCollection&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;backendSettingsCollection&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;httpListeners&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;appGatewayHttpListener&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/httpListeners/appGatewayHttpListener&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;etag&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;W/&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;5599fe28-15f0-40b8-a25d-56971263aaf8&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;provisioningState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;frontendIPConfiguration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/frontendIPConfigurations/appGatewayFrontendIP&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;frontendPort&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/frontendPorts/appGatewayFrontendPort&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;protocol&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hostNames&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;requireServerNameIndication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enableHttp3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;requestRoutingRules&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/requestRoutingRules/rule1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways/httpListeners&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;listeners&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;urlPathMaps&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;requestRoutingRules&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;rule1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/requestRoutingRules/rule1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;etag&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;W/&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;5599fe28-15f0-40b8-a25d-56971263aaf8&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;provisioningState&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ruleType&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Basic&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;priority&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;httpListener&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/httpListeners/appGatewayHttpListener&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;backendAddressPool&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/backendAddressPools/appGatewayBackendPool&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;backendHttpSettings&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/subscriptions/ce7aa0b9-3545-4104-99dc-d4d082339a05/resourceGroups/rg-demo-appgw-uks/providers/Microsoft.Network/applicationGateways/agw-demo-appgw-uks/backendHttpSettingsCollection/appGatewayBackendHttpSettings&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Microsoft.Network/applicationGateways/requestRoutingRules&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;routingRules&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;probes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;rewriteRuleSets&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;redirectConfigurations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;privateLinkConfigurations&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;privateEndpointConnections&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;defaultPredefinedSslPolicy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;AppGwSslPolicy20150501&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h5&gt;Condition  (Azure Application Gateway)&lt;/h5&gt;
&lt;p&gt;This checks if &lt;strong&gt;operationalState&lt;/strong&gt; is equal to &lt;strong&gt;Running&lt;/strong&gt; and &lt;strong&gt;True&lt;/strong&gt; is returned, then a &lt;strong&gt;POST&lt;/strong&gt; against &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/rest/api/application-gateway/application-gateways/stop?view=rest-application-gateway-2023-09-01&amp;amp;tabs=HTTP&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://management.azure.com/subscriptions/{subscriptionId}/resource groups/{resourceGroupName}/providers/Microsoft.Network/applicationGateways/{applicationGatewayName}/stop?api-version=2023-09-01&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is made, this instructs the Application Gateway to stop.&lt;/p&gt;
&lt;p&gt;Or if &lt;strong&gt;operationalState&lt;/strong&gt; is not equal to &lt;strong&gt;Running&lt;/strong&gt; and &lt;strong&gt;False&lt;/strong&gt; is returned, then a &lt;strong&gt;POST&lt;/strong&gt; against &lt;span&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/rest/api/application-gateway/application-gateways/start?view=rest-application-gateway-2023-09-01&amp;amp;tabs=HTTP&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/applicationGateways/{applicationGatewayName}/start?api-version=2023-09-01&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is executed, this starts the Application Gateway.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;If&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;expression&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;equals&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;@body(&apos;Process_information_on_the_ApplicationGateway&apos;)[&apos;properties&apos;][&apos;operationalState&apos;]&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;Running&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Stop_the_Application_Gateway&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://management.azure.com/@&amp;amp;#123;items(&apos;For_Each_App_Gateway&apos;)[&apos;id&apos;]&amp;amp;#125;/stop?api-version=2023-09-01&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;POST&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;identity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{parameters(&apos;managedId&apos;)}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ManagedServiceIdentity&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runtimeConfiguration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contentTransfer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;transferMode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Chunked&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Start_the_Application_Gateway&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Http&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inputs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://management.azure.com/@&amp;amp;#123;items(&apos;For_Each_App_Gateway&apos;)[&apos;id&apos;]&amp;amp;#125;/start?api-version=2023-09-01&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;POST&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;audience&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;identity&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@{parameters(&apos;managedId&apos;)}&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;ManagedServiceIdentity&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runtimeConfiguration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contentTransfer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;transferMode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Chunked&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;runAfter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Process_information_on_the_ApplicationGateway&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;Succeeded&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As the Azure Application Gateway was running when we ran the Azure Logic App the first time, the run stopped the Application Gateway:&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/agw-results.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2024-04-01-saving-money-with-azure-logic-apps/images/working-through-the-logic-app/agw-results.png&quot; alt=&quot;The full Application Gateway workflow&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1244&quot; height=&quot;2818&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see, while we are working with two different resource types, the workflow follows the same steps: get information on tagged resources, filter the list of resources to just the kind we are targeting with the workflow, get the status, and, depending on the status, perform an action.&lt;/p&gt;

&lt;h2&gt;Removing the Azure Logic Apps&lt;/h2&gt;
&lt;p&gt;To remove the Azure Logic Apps and also the user-managed identity run the following:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Setting some environment variables&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; RESOURCE_GROUP_NAME&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rg-logicapps-blogpost-uks&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;az&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;group&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--name&lt;/span&gt;&lt;span&gt; $RESOURCE_GROUP_NAME&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You may also have to tidy up the RBAC assignments on your subscription in the &lt;span&gt;&lt;a href=&quot;http://portal.azure.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Azure Portal&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Azure Logic Apps provide a powerful and flexible way to automate cost-saving tasks in your Azure environment. By leveraging the Azure REST API and managed identities, you can create workflows that intelligently start and stop your Virtual Machines and Application Gateways based on tags and schedules.&lt;/p&gt;
&lt;p&gt;The approach outlined in this post offers a consistent and scalable solution for managing your resources across different types. Whether you’re dealing with Virtual Machines, Application Gateways, or other Azure services, the core steps remain the same: retrieve tagged resources, filter them, check their status, and perform actions based on that status.&lt;/p&gt;
&lt;p&gt;Implementing these cost-saving automations can significantly impact your Azure spend, especially for non-production environments that don’t need to run 24/7. Ensuring your resources are only running when necessary allows you to optimise your cloud costs without sacrificing functionality or performance.&lt;/p&gt;
&lt;p&gt;As you explore optimising your Azure environment, consider how Azure Logic Apps can help you streamline your operations and reduce expenses. With a little creativity and some JSON skills, you can create powerful automations that save you time and money in the long run.&lt;/p&gt;
&lt;h2&gt;Audio Summary&lt;/h2&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Please note&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;The following audio summary of this blog post was generated by &lt;span&gt;&lt;a href=&quot;https://notebooklm.google&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NotebookLM&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;audio-container my-6&quot;&gt;&lt;audio controls&gt;&lt;source type=&quot;audio/mpeg&quot; src=&quot;/assets/2024-04-01-saving-money-with-azure-logic-apps/2024-04-01-saving-money-with-azure-logic-apps.mp3&quot;&gt;&lt;/source&gt;Your browser does not support the audio element.&lt;/audio&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-04-01-saving-money-with-azure-logic-apps.C6H4bPll.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/04/01/saving-money-with-azure-logic-apps-og.png</blog:ogImage><blog:readingTime>25</blog:readingTime><category>azure</category><category>code</category><category>cloud</category><category>automation</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Running LLMs locally with Ollama</title><link>https://www.russ.cloud/2024/03/29/running-llms-locally-with-ollama/</link><guid isPermaLink="true">https://www.russ.cloud//2024/03/29/running-llms-locally-with-ollama/</guid><description>A step-by-step guide to installing Ollama on macOS and running large language models like llama2 and Mistral entirely offline. Learn how to interact with the models via chat, API, and even remotely using ngrok.</description><pubDate>Fri, 29 Mar 2024 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;As I am coming to the end of writing the second edition of Lean &lt;a href=&quot;/glossary/ansible/&quot;&gt;Ansible&lt;/a&gt; (more news on that coming soon), I thought now would be a great time to have a look at what exciting developments have been happening in the six months now that I have a little more free time.&lt;/p&gt;
&lt;p&gt;One of the things I have been keeping an eye on is the state of Large Language Models (&lt;a href=&quot;/glossary/large-language-model/&quot;&gt;LLM&lt;/a&gt; for short), especially since the introduction of open-source models such as &lt;span&gt;&lt;a href=&quot;https://llama.meta.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Llama from Meta&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://mistral.ai/news/announcing-mistral-7b/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Mistral 7B&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, which you can run locally.&lt;/p&gt;
&lt;p&gt;Luckily for me, the fact I have been busy writing has meant enough time has passed for deployment methods to be much more straightforward and streamlined than they first were. The first tool I will look at in this post is &lt;a href=&quot;/glossary/ollama/&quot;&gt;Ollama&lt;/a&gt;; while it has been available since July last year, it has come on leaps and bounds since November 2023.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;As you may know from reading other my blog posts, I am a MacOS user, so the commands in this post will cover only MacOS. I also have an M3 MacBook Pro with 36GB of RAM, so your mileage may vary depending on your machine’s specifications.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2&gt;Ollama&lt;/h2&gt;
&lt;p&gt;So, what is Ollama?  The &lt;span&gt;&lt;a href=&quot;https://ollama.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ollama website&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; describes the tool as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Get up and running with large language models, locally. Run Llama 2, Code Llama, and other models. Customize and create your own.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The description is simple and to the point, much like the tool itself. Once you start using the tool, it will feel simple and basic - but don’t let that fool you; a lot is happening in the background.&lt;/p&gt;
&lt;p&gt;You are getting a tool that allows you to pull, update, and maintain copies of dozens of models-it also runs as a server in the background on your local machine. It gives you a standard &lt;a href=&quot;/glossary/api/&quot;&gt;API&lt;/a&gt; endpoint to connect to, allowing you to consume the models in a standardised way.&lt;/p&gt;
&lt;p&gt;Rather than discussing the tool’s features further, let’s install it and run some tests.&lt;/p&gt;
&lt;h3&gt;Installing on macOS&lt;/h3&gt;
&lt;p&gt;Installing Ollama on macOS using &lt;span&gt;&lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebrew&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; couldn’t be simpler; all you need to do is run:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing the desktop version of ollama&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cask&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ollama&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The keen-eyed amongst you may have noticed that I am passing the &lt;code&gt;-cask&lt;/code&gt; flag; this installs the desktop version of ollama rather than just the terminal version, which you can install by running:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;LInstalling the terminal version of ollama&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ollama&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;While the desktop version of Olama doesn’t have many features, running allows you to quickly start and stop the web services that run in the background by opening and closing the application. Another reason to prefer the desktop application over just running it on the command line is that it quietly handles updating itself in the background, prompting you to restart whenever a fresh update is available for download.&lt;/p&gt;
&lt;p&gt;So now that we have it installed let’s do something.&lt;/p&gt;
&lt;h3&gt;Pulling and running a model&lt;/h3&gt;
&lt;p&gt;Anyone familiar with the Docker way of pulling and using images will instantly feel at home to download and install the llama2 7b model we need to run:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Pulling llama2&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ollama&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;llama2:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This should give you something like the following output:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Output&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;manifest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;8934d96d3f08...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3.8&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;8c17c2ebb0ea...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7.0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7c23fb36d801...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4.8&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2e0493f67d0c...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;59&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fa304d675061...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;91&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;42ba7f8a01dd...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;557&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;verifying&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sha256&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;digest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;writing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;manifest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;removing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;any&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unused&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;layers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;success&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Please note the file size: 3.8GB, so ensure you have the bandwidth available. Luckily, once downloaded, Ollama doesn’t have to connect to the internet again (unless you want to download another model or update it).&lt;/p&gt;
&lt;p&gt;With the model downloaded, we can now interact with it by running the command below:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Opening a chat with llama2&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ollama&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;llama2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once launched, you will be dropped into a chat prompt and from here, you can ask your questions:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Chatting with llama2&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span&gt;Why&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sky&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blue?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sky&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appears&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;because&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phenomenon&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;called&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rayleighscattering.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;When&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sunlight&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;enters&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Earth&apos;s atmosphere, it encounters tiny molecules of gases such as nitrogen and&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;oxygen. These molecules absorb some of the light and scatter the rest in all&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;directions. The shorter wavelengths of light, such as blue and violet, are scattered&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;more than the longer wavelengths, such as red and orange. This is known as&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Rayleigh scattering.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;As a result of this scattering, the blue light is dispersed throughout the atmosphere,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;giving the sky its blue appearance. The same effect occurs when you see a sunset or a&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sunrise, where the light has to travel through more of the Earth&apos;s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;atmosphere,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;resulting&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;more&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scattering&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;more&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;intense&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;color.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;It&lt;/span&gt;&lt;span&gt;&apos;s worth noting that the exact shade of blue can vary depending on a number of&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;factors, including the time of day, the amount of dust and water vapor in the air&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;and the angle of the sun. But overall, the blue color of the sky is a result of&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;the way light interacts with the Earth&apos;&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;atmosphere.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span&gt;/?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Available&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Commands:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/set&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;variables&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/show&lt;/span&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;Show&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;information&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/load&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;model&amp;gt;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;Load&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;or&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/save&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;model&amp;gt;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;Save&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;your&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;current&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/bye&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;Exit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/?,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/help&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;Help&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shortcuts&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Help&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;keyboard&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shortcuts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot; to begin a multi-line message.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; /bye&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Entering any text in at the &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; prompt will be sent directly to the module; there is a help menu that can be accessed by typing ￼&lt;code&gt;/?&lt;/code&gt;￼; this will show the available commands, one of which is &lt;code&gt;/bye&lt;/code&gt; which exists the chat.&lt;/p&gt;
&lt;h3&gt;Using the API&lt;/h3&gt;
&lt;p&gt;As mentioned, Ollama runs a web-based API on your local machine, which listens on port 11434 by default. You can view this by going &lt;span&gt;&lt;a href=&quot;http://localhost:11434&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://localhost:11434&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; in your browser, and you should receive the message “Ollama is running” - you can send requests directly to the API using cURL, for example:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Asking a question using cURL&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;POST&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http://localhost:11434/api/generate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;model&quot;: &quot;llama2&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;prompt&quot;:&quot;Why is the sky blue?&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;stream&quot;: false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;After a second or two, a response is returned:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;llama2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2024-03-29T11:16:00.231151Z&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;The sky appears blue because of a phenomenon called Rayleigh scattering. When sunlight enters Earth&apos;s atmosphere, it encounters tiny molecules of gases such as nitrogen and oxygen. These molecules scatter the light in all directions, but they scatter shorter (blue) wavelengths more than longer (red) wavelengths. This is known as Rayleigh scattering.&lt;/span&gt;&lt;span&gt;\n\n&lt;/span&gt;&lt;span&gt;As a result of this scattering, the blue light is dispersed throughout the atmosphere, giving the sky its blue appearance. The blue light is scattered in all directions, but it is most visible in the direction of the sun, which is why the sky appears blue during the daytime.&lt;/span&gt;&lt;span&gt;\n\n&lt;/span&gt;&lt;span&gt;It&apos;s worth noting that the color of the sky can appear different under different conditions. For example, during sunrise and sunset, the sky can take on hues of red, orange, and pink due to the angle of the sunlight and the scattering of light by atmospheric particles. In urban areas, the light pollution from city streets can make the sky appear more yellow or orange than blue.&lt;/span&gt;&lt;span&gt;\n\n&lt;/span&gt;&lt;span&gt;So, to summarize, the sky appears blue because of Rayleigh scattering, which scatters shorter (blue) wavelengths of light more than longer (red) wavelengths, giving the appearance of a blue sky.&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;done&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;518&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;25580&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;29962&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;29889&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;total_duration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9717238042&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;load_duration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;663584&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;prompt_eval_duration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;167513000&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eval_count&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;291&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eval_duration&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9548392000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;In the output above, I truncated the context values as many exist.&lt;/p&gt;
&lt;h3&gt;Running another model&lt;/h3&gt;
&lt;p&gt;Do you want to run another model, like the newly launched Mistral 7B v0.2 release (which, when writing this post, was released last week)? No problem, just run:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Download and chat with Mistral v0.2&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ollama&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mistral:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will pull the model and drop us straight at a chat prompt:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Download and chat with Mistral v0.2&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;manifest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e8a35b5937a5...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4.1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;43070e2d4e53...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;11&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e6836092461f...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;42&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ed11eda7790d...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pulling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f9b1e3196ecf...&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;▕████████████████████████████████████▏&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;483&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;verifying&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sha256&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;digest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;writing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;manifest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;removing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;any&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unused&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;layers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;success&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span&gt;Why&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sky&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blue?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;The&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sky&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appears&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;due&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;called&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rayleigh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scattering.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;When&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sun&apos;s rays reach Earth&apos;s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;atmosphere,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;they&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;are&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scattered&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;directions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;gases&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;particles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;air.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Blue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;light&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shorter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wavelength&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gets&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scattered&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;more&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;easily&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;than&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;other&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;colors&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;such&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;red&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;or&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yellow.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;As&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;when&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;we&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;look&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;at&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sky,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;we&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;see&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;predominantly.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Additionally,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;during&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sunrise&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sunset,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sky&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;can&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;appear&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;red,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pink,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;or&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;orange&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;due&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scattering&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sunlight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Earth&apos;s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;atmosphere at those specific angles.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; /bye&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, this time, it was a 4.1GB download - which now means we have around 8GB of LLMs downloaded and sat on our local machine. To interact with mistral using the API, switch the model name and send your request:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Asking a question using cURL&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;POST&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http://localhost:11434/api/generate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;model&quot;: &quot;mistral&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;prompt&quot;:&quot;Why is the sky blue?&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;stream&quot;: false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This returns the same &lt;a href=&quot;/glossary/json/&quot;&gt;JSON&lt;/a&gt; response (apart from content, of course, as it’s a different model). Before moving on to the next part of the post, let’s pull down one more model:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Pulling codellama&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ollama&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;codellama:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;Open WebUI&lt;/h2&gt;

&lt;p&gt;The authors describe the project, which was formally called Ollama WebUI - so you can guess what it used for, as;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is distributed in a &lt;a href=&quot;/glossary/container/&quot;&gt;container&lt;/a&gt;, so we can run it using Docker or &lt;a href=&quot;/glossary/podman/&quot;&gt;Podman&lt;/a&gt; - with little in the way of any prerequisites needing to be installed.&lt;/p&gt;
&lt;h3&gt;Running on macOS&lt;/h3&gt;
&lt;p&gt;The only steps we need to do is create somewhere to store our data in, to do this I have a folder called &lt;code&gt;~/Containers/&lt;/code&gt; on my machine so lets stick an &lt;code&gt;open-webui&lt;/code&gt; folder in there:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Sorting out a directory to store our data in&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Containers/open-webui/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Containers/open-webui/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ghcr.io/open-webui/open-webui:main&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the folder in there and the image pulled, the following command will launch Open WebUI and bind it to port 3000 on our local machine:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Launching Open WebUI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--detach&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--publish&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3000:8080&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--add-host=host.docker.internal:host-gateway&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--volume&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Containers/open-webui:/app/backend/data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open-webui&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--restart&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;always&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;ghcr.io/open-webui/open-webui:main&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the container running, go to &lt;span&gt;&lt;a href=&quot;http://localhost:3000&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://localhost:3000/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h3&gt;Our first chat&lt;/h3&gt;
&lt;p&gt;A login page should greet you; click on the Sign-Up link and create a user; once you have an account - you will be presented with a ChatGPT-like interface - select a model from the drop-down menu at the top of the chat box and ask your question:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-01/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-01/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-01/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-01/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-01/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-01/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-01/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-01/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the screenshots above, the interface is familiar and quite function-rich, so let’s try something a little more interesting.  As mentioned at the start of the post, I have completed the first full draft of the second edition of Learn Ansible, which means I have PDF copies of all the chapters on my machine. Let’s upload Chapter 01 and chat about it.&lt;/p&gt;
&lt;h2&gt;Talking with a document&lt;/h2&gt;
&lt;p&gt;Click on Documents in the left-hand menu and then the &lt;strong&gt;+&lt;/strong&gt; button, upload your file and if you like tag it:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-02/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-02/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-02/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-02/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-02/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-02/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-02/04.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/open-webui-02/04.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now, let’s ask Mistral something about the document I uploaded. In the chapter, I talk about a declarative approach to &lt;a href=&quot;/glossary/infrastructure-as-code/&quot;&gt;IaC&lt;/a&gt; and give an example of how a declarative IaC would run.&lt;/p&gt;

&lt;p&gt;As you can see from the screens above, I typed in &lt;code&gt;#chatper01pd&lt;/code&gt; to select and attached the uploaded document to the chat prompt and then asked the question below:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Me&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;What happens in the declarative example given in the document? Also, can you give me a summary of each of the runs documented?&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;After a few seconds, I was given the following response:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Mistral:Latest&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;In the declarative example given in the document, our configuration management tool executes tasks declared in a certain order to achieve a desired state. The tasks include creating a group, creating users, and granting privileges. However, due to the eventual consistency principle, the final state may not be achieved immediately after each run. Instead, the system may reach the desired state after several runs.&lt;/p&gt;&lt;p&gt;Run 1:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Task 2 (create group Team) executed but could not create user Alice since the group did not exist yet.&lt;/li&gt;
&lt;li&gt;Task 3 (create user Bob) was successful as the group Team existed at that time.&lt;/li&gt;
&lt;li&gt;Task 4 (grant escalated privileges to user Alice) failed because there was no user called Alice on the system for the privileges to be applied to.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Run 2:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;The tasks were executed in the same order as Run 1.&lt;/li&gt;
&lt;li&gt;This time, since the group Team existed before user creation, user Alice was created without errors.&lt;/li&gt;
&lt;li&gt;As Alice was present during this run, she received escalated privileges.&lt;/li&gt;
&lt;li&gt;No changes were required during Run 3 as the system was already in the expected state.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;In summary, during each run, the tool attempts to reach the desired configuration but may not succeed immediately due to eventual consistency. The tasks are executed based on their order in the definition, and the final state is reached eventually through multiple runs.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;I must admit that it is pretty spot on with what I wrote. Let’s get a little meta, pardon the pun, and get Llama2 to write our response first. Select the file again by using &lt;code&gt;#chatper01pdf&lt;/code&gt; and then type &lt;code&gt;@llama2&lt;/code&gt; , you will get a prompt that Llama2 is thinking, and then it will fill in a response:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Llama2:Latest&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Ah, I see! Thank you for explaining that to me. That makes sense. So, in each run, the tool tries to reach the desired configuration, but it may not succeed immediately due to eventual consistency. And it seems like each run is trying to achieve the same goal, but the order of the tasks matters. Is that right?&lt;/p&gt;&lt;p&gt;And can you tell me more about the final state? What does it mean for the system to be in the expected state? Is it just a matter of having the desired configuration, or are there other factors at play as well?&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Upon clicking send, after a second, I got the following response:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Mistral:Latest&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Yes, that is correct. In each run, the tool is trying to reach the desired configuration, but it may not succeed immediately due to eventual consistency. The order of tasks matters because some tasks may depend on others being completed first.&lt;/p&gt;&lt;p&gt;As for the final state or expected configuration, it generally refers to having all the desired components and settings in place for a system to function optimally according to its intended design or purpose. This can include specific configurations, installed software, user accounts, &lt;a href=&quot;/glossary/security/&quot;&gt;security&lt;/a&gt; settings, and other factors that contribute to the overall health and stability of the system. In some cases, there may be additional requirements such as compliance with industry standards or regulatory guidelines, or performance benchmarks that must also be met for a system to be considered in the expected state.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;There are a few important things to remember here: first, this is all running on my local machine-at no point did I send the Chapter01 PDF file to a third-party API, and I could have disabled the network if I wanted. It is incredible how quickly this technology is moving forward!!!&lt;/p&gt;
&lt;p&gt;I could discuss many more features, but this post is getting quite long, and there is one more thing I would like to cover.&lt;/p&gt;
&lt;h2&gt;One more thing&lt;/h2&gt;

&lt;p&gt;The Open WebUI interface also works well in a mobile browser, so let’s use ngrok to open a tunnel from our local machine to the internet so we can access it - to do this, sign up for an ngrok account at &lt;span&gt;&lt;a href=&quot;https://ngrok.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;their site&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; - the service is described as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ngrok combines your reverse proxy, firewall, API gateway, and global load balancing to deliver apps and APIs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once you have signed up, its free, and downloaded the &lt;a href=&quot;/glossary/agent/&quot;&gt;agent&lt;/a&gt;, run the following command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;ngrok is up and running&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ngrok&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This will create the tunnel, give you an HTTPS URL to access the content on and display some stats:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;ngrok is up and running&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ngrok&lt;/span&gt;&lt;span&gt;                                                        (Ctrl+C &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quit&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Full&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;capture&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;available&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;your&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;browser:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://ngrok.com/r/ti&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Session&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Status&lt;/span&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;online&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Account&lt;/span&gt;&lt;span&gt;                       &lt;/span&gt;&lt;span&gt;Russ&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Mckendrick&lt;/span&gt;&lt;span&gt; (Plan: &lt;/span&gt;&lt;span&gt;Free&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Version&lt;/span&gt;&lt;span&gt;                       &lt;/span&gt;&lt;span&gt;3.8.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Region&lt;/span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;Europe&lt;/span&gt;&lt;span&gt; (eu)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Web&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Interface&lt;/span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;span&gt;http://127.0.0.1:4040&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Forwarding&lt;/span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;https://f112-11e3-8082.ngrok-free.app&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http://lo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Connections&lt;/span&gt;&lt;span&gt;                   &lt;/span&gt;&lt;span&gt;ttl&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;opn&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;rt1&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;rt5&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;p50&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;p90&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;                              &lt;/span&gt;&lt;span&gt;39&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;1.20&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;1.20&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;0.12&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;0.19&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HTTP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Requests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/assets/fonts/Mona-Sans.woff2&lt;/span&gt;&lt;span&gt;                       &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/_app/immutable/assets/14.e43bb62b.css&lt;/span&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/static/favicon.png&lt;/span&gt;&lt;span&gt;                                 &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/_app/immutable/nodes/14.efdc13cb.js&lt;/span&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/_app/immutable/chunks/translation.d76e2229.js&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/_app/immutable/chunks/_commonjsHelpers.de833af9.js&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/_app/immutable/chunks/spread.8a54911c.js&lt;/span&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/_app/immutable/chunks/singletons.a9f7f5ea.js&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/_app/immutable/chunks/stores.a0da6027.js&lt;/span&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/_app/immutable/chunks/index.ec044ede.js&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the screens below, if you goto the URL given by ngrok and click through the warning you will be taken to the login page, and from their you can use the mobile UI:&lt;/p&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/ngrok/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/ngrok/01.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/ngrok/02.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/ngrok/02.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/ngrok/03.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2024-03-29-running-ll-ms-locally-with-ollama/images/ngrok/03.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once finished, press &lt;code&gt;ctrl+c&lt;/code&gt; to close the tunnel and quit the ngrok session.&lt;/p&gt;
&lt;h2&gt;Cleaning up&lt;/h2&gt;

&lt;p&gt;There is just one thing to do: clean up the resources we have launched, starting with the containers:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Stop and remove the containers&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open-webui&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open-webui&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Don’t worry again about the container being removed; the data and configuration are safe on the local machine, and to restore them, you need only run the &lt;code&gt;docker container run&lt;/code&gt; command from earlier in the post.&lt;/p&gt;
&lt;p&gt;The final thing you should do is quit the Ollama desktop application. This will shut down the API server and stop it from running in the background. Then, reopen the application to start everything back up again.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this post, we explored how to run powerful large language models like llama2, Mistral, and codellama entirely locally on macOS using the Ollama tool. We saw how easy it is to pull models, interact with them via a chat interface and API, run a feature-rich web UI, and even access the models remotely using ngrok.&lt;/p&gt;
&lt;p&gt;The fact that all of this cutting-edge AI capability can run offline on a laptop with a tool that’s simple to install is truly incredible. I encourage you to try out Ollama for yourself and experience the power of local LLMs. The possibilities are endless, from exploring and learning about these models to building your own applications on top of them.&lt;/p&gt;
&lt;p&gt;I hope this post has been informative and inspirational. Feel free to reach out with any questions, and happy local LLM’ing!&lt;/p&gt;
&lt;p&gt;Here are some links to the tools we have looked at in the post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://ollama.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://ollama.com/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/ollama/ollama&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/ollama/ollama&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://openwebui.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://openwebui.com&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/open-webui/open-webui&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/open-webui/open-webui&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://llama.meta.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://llama.meta.com&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://ai.meta.com/blog/code-llama-large-language-model-coding/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://ai.meta.com/blog/code-llama-large-language-model-coding/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://docs.mistral.ai/models/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://docs.mistral.ai/models/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://mistral.ai/news/announcing-mistral-7b/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://mistral.ai/news/announcing-mistral-7b/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://ngrok.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://ngrok.com/&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2024-03-29-running-ll-ms-locally-with-ollama.DcYkOZLQ.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2024/03/29/running-llms-locally-with-ollama-og.png</blog:ogImage><blog:readingTime>17</blog:readingTime><category>ai</category><category>code</category><category>macOS</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>My Top 10 of 2023</title><link>https://www.russ.cloud/2023/12/23/my-top-10-of-2023/</link><guid isPermaLink="true">https://www.russ.cloud//2023/12/23/my-top-10-of-2023/</guid><description>Explore 2023&apos;s diverse music scene with 17 albums, from The Polyphonic Spree&apos;s &apos;Salvage Enterprise&apos; to Peter Gabriel&apos;s &apos;I/O&apos;.</description><pubDate>Sat, 23 Dec 2023 12:01:41 GMT</pubDate><content:encoded>&lt;p&gt;2023 has been an extraordinary year for music, showcasing an eclectic mix of genres, styles, and artistic visions. This list of 17 albums represents the year’s rich musical landscape, each offering a unique perspective and sound. From the psychedelic choral pop of The Polyphonic Spree’s “Salvage Enterprise” to the introspective and nuanced melodies of Jenny Lewis in “Joy’All,” these albums traverse a spectrum of emotional and sonic experiences.&lt;/p&gt;
&lt;p&gt;The list includes revered artists like Peter Gabriel with his album “I/O” and new interpretations of classics like “The Endless Coloured Ways (The Songs Of Nick Drake)” by Various artists. It also features the experimental sounds of “Hologram” by Amplifier and the laid-back reggae vibes of Rhoda Dakar’s “Version Girl.” Each album stands as a testament to the artist’s creative vision, whether it be through revisiting and reimagining past genres or exploring new territories in sound and storytelling.&lt;/p&gt;
&lt;p&gt;From the ethereal shoegaze of Slowdive’s “Everything Is Alive” to the dynamic rock energy of Riverside’s “ID.Entity,” these albums collectively form a snapshot of 2023’s diverse and vibrant music scene. They celebrate the continuous evolution of music and its unending capacity to innovate, reflect, and connect.&lt;/p&gt;
&lt;h1&gt;The Top 10&lt;/h1&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/01.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/01.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/02.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/02.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/03.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/03.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/04.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/04.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/05.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/05.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/06.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/06.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/07.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/07.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/08.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/08.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/top10/09.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/top10/09.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Salvage Enterprise - The Polyphonic Spree (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/salvage-enterprise-28945606/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Salvage Enterprise” by The Polyphonic Spree, released in 2023, marks a significant return for the band, known for their theatrical and operatic approach to alternative music. The album is categorized as a rock album on Apple Music, but it encompasses a broader range of styles. The band continues its tradition of naming songs starting with “Section” followed by a number, a unique titling method that adds a layer of intrigue to the album.&lt;/p&gt;
&lt;p&gt;The album opens with “Section 44 (Galloping Seas),” a six-minute track that sets a dramatic and emotional tone for the album. This song, with its build-ups and shifts in tempo, exemplifies the band’s knack for creating a cinematic feel in their music. The lyrics convey hope and resilience in the face of adversity, a recurring theme throughout the album.&lt;/p&gt;
&lt;p&gt;Each track on “Salvage Enterprise” is a distinct journey, combining elements of drama, opera, rock, and indie music. The use of various instruments like strings, plucked guitar, and woodwinds adds depth and soulfulness to the music. The vocal delivery is gentle yet impactful, complementing the rich instrumentals.&lt;/p&gt;
&lt;p&gt;The album has been described as a ‘rising-from-the-ashes’ record by frontman Tim DeLaughter, indicating a theme of resilience and rebirth. Songs like “Shadows On The Hillside (Section 48)” and “Morning Sun, I Built The Stairs (Section 52)” highlight this theme, with the latter concluding the album on a high note, symbolizing overcoming challenges and emerging stronger.&lt;/p&gt;
&lt;p&gt;In summary, “Salvage Enterprise” is characterized by its dramatic and operatic style, blending rock with indie elements, and marked by its thematic focus on perseverance and renewal. The album is a testament to The Polyphonic Spree’s ability to create music that is both theatrically grand and intimately expressive.&lt;/p&gt;
&lt;p&gt;For more details, you can explore the reviews from &lt;span&gt;&lt;a href=&quot;https://music.mxdwn.com/2023/11/28/reviews/album-review-the-polyphonic-spree-salvage-enterprise/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;mxdwn Music&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://spectrumculture.com/2023/12/06/the-polyphonic-spree-salvage-enterprise-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Spectrum Culture&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://tinnitist.com/2023/11/17/albums-of-the-week-the-polyphonic-spree-salvage-enterprise/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tinnitist&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://glidemagazine.com/297692/the-polyphonic-sprees-robust-positive-pop-returns-on-salvage-enterprise-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Glide Magazine&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://www.allmusic.com/album/salvage-enterprise-mw0004100460&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AllMusic&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;News From The Invisible World - The Fierce &amp;amp; The Dead (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/news-from-the-invisible-world-28073356/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“News From The Invisible World” by The Fierce &amp;amp; The Dead is a significant evolution in the band’s journey, introducing vocals for the first time in their discography. This album, with its eight tracks spanning 42 minutes, marks a bold step forward, showcasing the band’s willingness to embrace change and expand their musical horizons.&lt;/p&gt;
&lt;p&gt;The album opens with “The Start,” a track that symbolizes strategic decision-making and crescendos from a subdued beginning to an intense finish. “Shake the Jar” follows, featuring impressive guitar work and a gritty garage rock aesthetic, transitioning from robustness to whimsy and back to a hard rock finale. “Golden Thread” surprises listeners with its shift from conventional hard rock to a more complex and alternative sound, enriched by techno-style vocal echoes.&lt;/p&gt;
&lt;p&gt;“Non-Player” stands out as a transformative piece, where the band simulates an orchestral sound with keyboards and synthesizers. The vocals evoke Jon Anderson of “Yes,” with a track that culminates in a free-spirited brass arrangement. “What a Time to Be Alive” is another notable track, highlighting the tight synchronicity between the bass and drums, and effortlessly shifting between intense rock and ethereal instrumentals.&lt;/p&gt;
&lt;p&gt;The album closes with “Nostalgia Now,” a contemplative piece that invites introspection with its insightful approach, leaving a lasting impression on the listener.&lt;/p&gt;
&lt;p&gt;Overall, “News From The Invisible World” is a journey of musical exploration and innovation. It’s an album that might not resonate with every listener, but its complexity and unique style firmly establish it as a work of art, worthy of attention from both long-time fans and newcomers.&lt;/p&gt;
&lt;p&gt;For detailed reviews, please visit &lt;span&gt;&lt;a href=&quot;https://psychedelicscene.com/2023/08/21/news-from-the-invisible-world-by-the-fierce-and-the-dead-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Psychedelic Scene Magazine&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://atthebarrier.com/2023/07/24/the-fierce-the-dead/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;At The Barrier&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://echoesanddust.com/2023/06/the-fierce-and-the-dead-news-from-the-invisible-world/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Echoes And Dust&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://www.progradar.org/?s=+News+From+The+Invisible+World&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Progradar&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;I/O -  Peter Gabriel (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/i-o-bright-side-mixes-29079523/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;Peter Gabriel’s album “i/o,” released in December 2023, marks his first album of new material in over two decades. The album unfolds a narrative of modern concerns and personal reflections. It was uniquely released track by track every full moon throughout 2023, creating a gradual buildup to the album’s release.&lt;/p&gt;
&lt;p&gt;Stylistically, “i/o” aligns with the tones and styles of Gabriel’s earlier works, particularly “Up” and “Us.” It features a blend of acoustic instruments and electronic elements, creating a rich, ambient soundscape. The album is available in two different versions: the “Bright-Side” and “Dark-Side” mixes, each offering a slightly different tonal experience but maintaining the core essence of the tracks.&lt;/p&gt;
&lt;p&gt;The album addresses a range of themes, from surveillance culture in “Panopticom” to the complexities of cancel culture in “The Court.” It also delves into deeper, more introspective themes, like the tragic naivety of corrupted religious faith in “Four Kinds of Horses” and the sobering reality of mortality in songs like “Playing for Time” and “So Much.” Yet, there are moments of brightness and celebration, such as in “Road to Joy,” a jubilant electro-funk track, and “Olive Tree,” a soulful pop song imagining a future of enhanced human connectivity.&lt;/p&gt;
&lt;p&gt;“i/o” has been described as a thought-provoking blend of modern concerns with personal reflections, offering a mixture of austere and jubilant moments. Gabriel’s exploration of mortality, grief, and hope for renewal is a significant theme throughout the album. Despite the weight of these themes, the album closes on a hopeful note with “Live and Let Live,” expressing a desire for global self-forgiveness and peace.&lt;/p&gt;
&lt;p&gt;Overall, “i/o” is seen as a worthy addition to Peter Gabriel’s catalog, reflecting his status as a future-facing artist who continues to push boundaries in music.&lt;/p&gt;
&lt;p&gt;For more detailed insights, you can refer to the reviews from &lt;span&gt;&lt;a href=&quot;https://progreport.com/peter-gabriel-i-o-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Prog Report&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://ultimateclassicrock.com/peter-gabriel-io-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ultimate Classic Rock&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://www.rollingstone.co.uk/music/album-reviews/peter-gabriel-i-o-review-a-return-well-worth-waiting-for-35083/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Rolling Stone UK&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Hologram - Amplifier (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/hologram-28259452/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Hologram,” Amplifier’s first album since 2017, emerges as a strong and diverse addition to the band’s catalog. Not originally intended as their next album, “Hologram” was created in the Autumn of 2022, during sessions for another project, “Gargantuan,” which is yet to be released. Realizing that the material from these sessions didn’t quite fit the “Gargantuan” project, the band decided to release it as “Hologram.”&lt;/p&gt;
&lt;p&gt;The album showcases Amplifier’s signature style, blending philosophical and abstract lyrical content with a slightly dystopian and sci-fi ambiance, reminiscent of the works of Philip K. Dick. The band’s evolution is evident, ranging from their crunchier, early rock albums to the full-on prog rock of “The Octopus,” and then to the more subtle art rock of “Echo Street.” Amplifier’s music is described as a celebration of the transcendent nature of rock music as escapism.&lt;/p&gt;
&lt;p&gt;Tracks like “Two Way Mirror” display classic Amplifier riffs with stoner tones, while “Sweet Perfume” presents a weirdly dissonant sound aiming at vacuous celebrity. The title track “Hologram” offers a more atmospheric soundscape with soft, dream-like vocals. “Tundra” is a standout piece, showcasing fluid drumming and layered guitars, while “Let Me Drive” returns to a theme with infectious synths and a groove guitar riff. The final song, “Gargantuan, Part One,” hints at what might be expected in the next full album.&lt;/p&gt;
&lt;p&gt;“Hologram” is a journey of just 36 minutes, but it’s praised for its quality rather than length, offering a glimpse into what the future holds for Amplifier’s music. The album has been well-received for its high quality, marking a welcome return for the band and generating excitement for their upcoming project “Gargantuan.”&lt;/p&gt;
&lt;p&gt;For more details on the album, you can refer to the reviews on &lt;span&gt;&lt;a href=&quot;https://www.sputnikmusic.com/review/86684/Amplifier-Hologram/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sputnikmusic&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://theprogressiveaspect.net/blog/2023/04/20/51910/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Progressive Aspect&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Pearlies - Emma Anderson (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/pearlies-28653319/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Pearlies,” the solo debut album from Emma Anderson, known for her work with Lush, is a significant return to the music scene. Released following the final breakup of Lush in 2016, “Pearlies” showcases Anderson’s journey back into the spotlight with a collection of songs that revisit the gauzy sound of Lush’s early shoegaze years. The album diverges from the brasher Britpop era of Lush, instead embracing a more atmospheric and dream-pop approach.&lt;/p&gt;
&lt;p&gt;The album stands out for its melodic quality, reminiscent of Lush’s early era, yet explores more adventurous and richer sounds. Tracks like “I Was Miles Away” demonstrate Anderson’s crystalline vocals and distinctive melodies, while “Bend The Round” and “Willow And Mallow” present a more haunting and spectral side, hinting at influences like Vashti Bunyan and Shirley Collins. The album also features a cinematic quality, influenced by Anderson’s initial plans to develop the music for film and TV soundtracks.&lt;/p&gt;
&lt;p&gt;“Pearlies” is not just a nostalgic trip to shoegaze but a step into broader musical landscapes, combining the sickly-sweet quality of 60s TV drama themes with an undercurrent of eeriness. The album’s strongest tracks are noted for their haunting quality, blending light-filled soundscapes with a sense of otherworldly melancholy.&lt;/p&gt;
&lt;p&gt;Overall, “Pearlies” is a glowingly lovely and nicely crafted record that shows Anderson’s ability to create music that is both soothing and subtly unsettling. It serves as a polished, less urgent version of Lush, displaying Anderson’s expertise in crafting neo-shoegaze music with a distinctive charm.&lt;/p&gt;
&lt;p&gt;For more detailed insights into the album, you can refer to the reviews on &lt;span&gt;&lt;a href=&quot;https://www.uncut.co.uk/reviews/emma-anderson-143992/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;UNCUT&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://spectrumculture.com/2023/10/22/emma-anderson-pearlies-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Spectrum Culture&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Ziggy Stardub - Easy Star All-Stars (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/ziggy-stardub-26844965/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Ziggy Stardub” by Easy Star All-Stars is a reggae reinterpretation of David Bowie’s iconic album “The Rise and Fall of Ziggy Stardust and The Spiders from Mars.” This album is a fresh take on Bowie’s work, delivered in a dub reggae style that’s both straightforward and impactful. Featuring a mix of younger and veteran vocalists, including notable names like Steel Pulse, Maxi Priest, and Macy Gray, the album offers a blend of reggae styles with well-established and emerging talents.&lt;/p&gt;
&lt;p&gt;One of the standout aspects of “Ziggy Stardub” is how it maintains the essence of the original Bowie songs while seamlessly integrating reggae elements. The album includes tracks like “Five Years,” sung by David Hinds of Steel Pulse, and “Rock’n’Roll Suicide,” featuring Macy Gray, whose husky voice adds a new dimension to the song. The album has been praised for its ability to transcend novelty, highlighting the strength of the original songs while offering an endlessly listenable reggae experience.&lt;/p&gt;
&lt;p&gt;The Easy Star All-Stars have a history of reimagining classic albums in reggae styles, as seen in their previous works like “Dub Side of The Moon” and “Radiodread.” “Ziggy Stardub” continues this tradition, demonstrating the band’s creativity and reverence for the original material. While some may argue it lacks the surprise factor of their previous works, the album is nonetheless celebrated for its high-quality production and its natural fit within the reggae genre.&lt;/p&gt;
&lt;p&gt;For a more detailed review and insights into the album, you can visit &lt;span&gt;&lt;a href=&quot;https://www.allmusic.com/album/ziggy-stardub-mw0003921922&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AllMusic&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.theartsdesk.com/new-music/album-easy-star-all-stars-ziggy-stardub&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Arts Desk&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://tinnitist.com/2023/04/21/albums-of-the-week-easy-star-all-stars-ziggy-stardub/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tinnitist&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Everything Is Alive - Slowdive (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/everything-is-alive-28131346/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Everything Is Alive” by Slowdive marks a significant evolution in the band’s sound, presenting an album that is both intimately familiar yet ventures into new sonic territories. The album, which follows their self-titled reunion album, manages to retain and invigorate the timeless magic of Slowdive’s music, while also exploring new dimensions in sound and emotion.&lt;/p&gt;
&lt;p&gt;The opener “Shanty” immediately establishes the album’s tone, integrating modular synths alongside Slowdive’s signature sound. The album is noted for its seamless blending of different elements, from aqueous, distorted guitar stabs to steady pulses, all while maintaining the quintessential Slowdive identity.&lt;/p&gt;
&lt;p&gt;One of the standout tracks, “Andalucia Plays,” is described as the most affecting song in Slowdive’s career, showcasing the band’s ability to evoke deep emotion and narrative through their music. The track, along with others like “Kisses” and “The Slab,” brings a sense of propulsion and intensity that surpasses the group’s previous works.&lt;/p&gt;
&lt;p&gt;Throughout the album, there’s a consistent balance between darkness and hope, with each track contributing to a comprehensive, yet varied, listening experience. The album also benefits from the skilled mixing of Shawn Everett, known for his work with artists like the War on Drugs and Kacey Musgraves, which adds to its powerful resonance.&lt;/p&gt;
&lt;p&gt;“Everything Is Alive” transcends its shoegaze origins, becoming something more expansive and emotionally resonant. It represents a band not only revisiting its roots but also expanding its horizon, creating a work that is both haunting and unassailably alive.&lt;/p&gt;
&lt;p&gt;This description of “Everything Is Alive” is based on reviews from &lt;span&gt;&lt;a href=&quot;https://www.spin.com/2023/08/slowdive-everything-is-alive-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;SPIN&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://ourculturemag.com/2023/08/31/album-review-slowdive-everything-is-alive/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Our Culture Magazine&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, where you can find more detailed analyses and insights into the album.&lt;/p&gt;
&lt;h2&gt;The Harmony Codex - Steven Wilson (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/the-harmony-codex-28455277/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Steven Wilson’s album ‘The Harmony &lt;a href=&quot;/glossary/codex/&quot;&gt;Codex&lt;/a&gt;’ represents a significant milestone in his career, showcasing an evolution in his sound and artistic expression. The album intertwines various musical styles, including progressive rock, jazz, electronica, and ambient soundscapes, creating a unique listening experience.&lt;/p&gt;
&lt;p&gt;The track “Inclination” opens the album with a blend of trumpet, synths, and percussion, setting an adventurous and experimental tone. “What Life Brings” follows, offering a more conventional song structure with strong guitar elements and captivating vocal sections. “Impossible Tightrope” stands out as an almost 11-minute epic, blending progressive rock with spiritual jazz and electronica, highlighted by Nate Wood’s drums and Theo Travis’ saxophone solo.&lt;/p&gt;
&lt;p&gt;The album also features a significant contribution from Ninet Tayeb, particularly on the track “Rock Bottom,” which she not only sings but also wrote. This song, along with others like “Beautiful Scarecrow,” showcases Wilson’s ability to create dark, atmospheric music with electronic backgrounds. The title track “The Harmony Codex” offers a nearly ten-minute journey through sounds and textures, different from anything on Wilson’s regular albums but in line with his sonic preferences.&lt;/p&gt;
&lt;p&gt;Overall, ‘The Harmony Codex’ is seen as a brave step forward for Wilson, with a mix of familiar elements and new explorations in sound. It’s a testament to his evolving career, offering diverse and challenging compositions that require and reward attentive listening.&lt;/p&gt;
&lt;p&gt;For more detailed insights into the album, you can explore the reviews on &lt;span&gt;&lt;a href=&quot;https://progreport.com/steven-wilson-the-harmony-codex-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Prog Report&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://atthebarrier.com/2023/09/28/steven-wilson-the-harmony-codex-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;At The Barrier&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://spinningplatters.com/2023/08/14/album-review-steven-wilson-the-harmony-codex/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Spinning Platters&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.”&lt;/p&gt;
&lt;h2&gt;Brothers &amp;amp; Sisters - Steve Mason (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/brothers-sisters-26299913/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Brothers &amp;amp; Sisters,” the fifth solo album by Steve Mason, is a creative and politically charged work that sees him returning to the experimental and melodic styles that marked his time with The Beta Band. The album opens with expansive synth waves, possibly paying homage to the late Vangelis, and quickly establishes itself as an uplifting and optimistic spiritual reckoning. Mason doesn’t shy away from addressing the turmoil and chaos of current global and political climates, using his music as a platform for expression and defiance.&lt;/p&gt;
&lt;p&gt;Tracks like “No More” and “I’m On My Way” exhibit Mason’s signature sound, blending introspection with psychedelic pop. “Pieces of Me” is a poignant ballad with searing synths and a reverbed bar-style piano, showcasing Mason’s heartfelt lyrics and emotional depth. The album also features Pakistani musician Javed Bashir, who adds a unique dimension to songs like “Brixton Fish Fry,” blending seamlessly with Mason’s drifting sounds.&lt;/p&gt;
&lt;p&gt;“Upon My Soul” brings a party-up vibe, with a swinging, groovy feel, and the album closes with the title track, “Brothers &amp;amp; Sisters,” which is an upbeat anthem promoting unity and positivity. The album is a kaleidoscope of sounds, creating a psychedelic groove that leaves listeners feeling uplifted and transported away from the world’s ills. Mason’s talent for beats and percussion and his knack for creating absorbing rhythms are evident throughout the album.&lt;/p&gt;
&lt;p&gt;“Brothers &amp;amp; Sisters” is fundamentally about connections and unity, delivering a message that things must work for everyone or they work for none. In creating this album, Mason reminds us that protest music needn’t always be angry; it can also be a unifying and joyous celebration.&lt;/p&gt;
&lt;p&gt;For more detailed reviews of the album, you can visit &lt;span&gt;&lt;a href=&quot;https://www.allmusic.com/album/brothers-sisters-mw0003856586&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AllMusic&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://narcmagazine.com/album-review-steve-mason-brothers-sisters/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NARC. Magazine&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://echoesanddust.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Echoes And Dust&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://www.live4ever.uk.com/2023/03/review-steve-mason-brothers-sisters/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Live4ever&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;The Endless Coloured Ways (The Songs Of Nick Drake) - Various (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/the-endless-coloured-ways-the-songs-of-nick-drake-27634329/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“The Endless Coloured Ways: The Songs of Nick Drake” is an album that stands as a significant tribute to the enduring musical legacy of Nick Drake, featuring an eclectic mix of artists who bring their unique interpretations to his songs. This compilation album, released on July 7, 2023, includes contributions from a variety of artists across different genres, all unified by Drake’s poignant lyricism.&lt;/p&gt;
&lt;p&gt;The album begins with Fontaines D.C.’s rendition of “Cello Song,” setting a high bar and indicating that the artists have embraced their creative liberties in approaching Drake’s music. Each track maintains the spirit of Drake’s originals, yet diverges in substantial and tasteful ways through musical phrases. French pop icon Camille, for instance, adds a whimsical touch to “Hazey Jane II” with delicate singing and instrumentation.&lt;/p&gt;
&lt;p&gt;The album showcases a range of styles, from the post-punk elements in Fontaines D.C.’s performance to the arty synth-pop of Let’s Eat Grandma in “From The Morning.” It also features notable collaborations, such as Bombay Bicycle Club joining forces with The Staves for “Road,” blending playfulness and charm. Aurora’s cover of “Pink Moon” and Philip Selway’s minimalistic approach to “Fly” are other highlights, showcasing the breadth of artistic expression found in this tribute.&lt;/p&gt;
&lt;p&gt;Overall, “The Endless Coloured Ways: The Songs of Nick Drake” is not only a celebration of Drake’s songwriting but also a demonstration of how his music transcends styles and continues to inspire a wide range of artists. It’s a testament to his influence in the music world and serves as a beautifully crafted homage to his legacy.&lt;/p&gt;
&lt;p&gt;For a deeper dive into the album, you can explore the reviews on &lt;span&gt;&lt;a href=&quot;https://tradfolk.co/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tradfolk&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.folkradio.co.uk/2023/07/the-endless-coloured-ways-the-songs-of-nick-drake-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Folk Radio UK&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.indiependent.co.uk/album-review-the-endless-coloured-ways-the-songs-of-nick-drake-various-artists/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Indiependent&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://music.mxdwn.com/2023/08/01/reviews/album-review-various-artists-the-endless-coloured-ways-songs-of-nick-drake/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;mxdwn Music&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h1&gt;Bubbling under&lt;/h1&gt;
&lt;div class=&quot;lightgallery-component lightgallery-wrapper&quot;&gt;&lt;div class=&quot;astro-lightgallery-adaptive-container&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/under/10.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/under/10.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/under/11.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/under/11.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/under/12.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/under/12.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/under/13.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/under/13.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/under/14.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/under/14.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/under/15.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/under/15.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/under/16.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/under/16.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-12-17-2023-top-10/under/17.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=400,height=400,quality=60,format=avif,fit=cover/assets/2023-12-17-2023-top-10/under/17.jpg&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Sky Void Of Stars - Katatonia (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/sky-void-of-stars-25820851/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Sky Void Of Stars” by Katatonia, released in 2023, showcases the band’s evolution from their previous work. This album is praised for its unique blend of gothic rock and prog elements, with a focus on brooding, morose songwriting. The opening track “Austerity” is highlighted for its complexity and melancholic melodies, setting the tone for the album.&lt;/p&gt;
&lt;p&gt;The album is noted for its dynamic range, from the gothic magnificence of “Opaline” to the more accessible “Birds” and “Atrium,” the latter embodying the grandeur of 80s goth rock. The track “Impermanence,” featuring guest vocalist Joel Ekelöf, is a standout, contributing to the album’s poignant atmosphere.&lt;/p&gt;
&lt;p&gt;Critics have commended “Sky Void Of Stars” for its cohesive collection of tracks, blending powerful and dramatic music with a tone of vibrant darkness. Each song offers a unique experience, from memorable choruses to heavy grooves, showcasing Katatonia’s skill in creating gloomy, melancholic metal.&lt;/p&gt;
&lt;p&gt;For more detailed reviews and insights into the album, you can explore the reviews from &lt;span&gt;&lt;a href=&quot;https://progreport.com/katatonia-sky-void-of-stars-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Prog Report&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.metalsucks.net/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;MetalSucks&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.angrymetalguy.com/katatonia-sky-void-of-stars-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Angry Metal Guy&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://distortedsoundmag.com/album-review-sky-void-of-stars-katatonia/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Distorted Sound Magazine&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://thesoundboardreviews.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Soundboard&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;ID.Entity - Riverside (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/id-entity-25826326/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“ID.Entity” by Riverside, released in 2023, is an album that delves into contemporary themes with emotional depth and melodic richness. The album reflects on the complexities of identity and the digital age, presenting these topics through a mix of prog rock and modern musical elements.&lt;/p&gt;
&lt;p&gt;The track “The Place Where I Belong” is the album’s epic centerpiece, extending over thirteen minutes. It begins with a quieter, more introspective tone, using acoustic guitar and gentle vocals, before escalating into a richer, more layered composition. This track stands out for its emotional intensity and lyrical depth, addressing themes of social conformity and personal disillusionment with the state of the world, particularly in the realm of social media. The song “Self-Aware” closes the album on an upbeat note, suggesting a message of hope and the importance of real human connection.&lt;/p&gt;
&lt;p&gt;Riverside’s music in “ID.Entity” is noted for its subtlety and complexity. Tracks like “Friend or Foe” and “Big Tech Brother” address contemporary issues like social media and the tech industry with a critical eye. The band’s ability to evolve their sound while maintaining a connection to their roots is evident throughout the album. Riverside’s blend of rock, prog, and art in “ID.Entity” makes it a significant release in their discography.&lt;/p&gt;
&lt;p&gt;The album’s production quality is highlighted for its detail and innovation, with the musicianship of Riverside members being a standout aspect. The album is seen as a continuation of Riverside’s established sound, but with a rejuvenated and more dynamic approach.&lt;/p&gt;
&lt;p&gt;Despite some criticisms regarding certain lyrical choices and the use of computer-generated voices in some tracks, “ID.Entity” is generally received as a strong addition to Riverside’s catalog, appealing to both long-time fans and newcomers.&lt;/p&gt;
&lt;p&gt;For more detailed insights into the album, you can explore the reviews from &lt;span&gt;&lt;a href=&quot;https://metalwani.com/2023/01/review-riverside-id-entity.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Metal Wani&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://progreport.com/riverside-i-d-entity-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Prog Report&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.angrymetalguy.com/riverside-id-entity-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Angry Metal Guy&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://maximumvolumemusic.com/review-riverside-id-entity-2023/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Maximum Volume Music&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://millstreampark.com/riverside-id-entity-2023-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Millstream Park&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://www.sonicperspectives.com/album-reviews/riverside-id-entity/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sonic Perspectives&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;The Girl Is Crying In Her Latte - Sparks (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/the-girl-is-crying-in-her-latte-27196083/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“The Girl Is Crying In Her Latte” by Sparks is a testament to the band’s creative endurance, marking their 26th studio album. This 2023 release showcases Sparks’ ability to blend their classic sound with a modern twist, infusing a more electronic and hard-edged approach compared to their previous works. The album is applauded for its versatility, featuring a range of styles and sounds that demonstrate the band’s evolving artistry while maintaining their distinct identity.&lt;/p&gt;
&lt;p&gt;Tracks like “Not That Well Defined” delve into themes of ambiguity and shades of grey, while “We Go Dancing” offers a satirical take on North Korean propaganda, presenting a dark humor characteristic of Sparks. The album also includes playful dance songs like “Escalator” adding a light-hearted contrast to the mix.&lt;/p&gt;
&lt;p&gt;The standout track “Nothing Is As Good As They Say It Is” presents a unique perspective, featuring a baby’s view of the world. This track, along with others like “Veronica Lake” and  Mona Lisa’s Packing, Leaving Later Tonight” exemplifies Sparks’ knack for offbeat, dark humor and storytelling through music.&lt;/p&gt;
&lt;p&gt;The album closes with “Gee, That Was Fun” a song about regret and missed opportunities, wrapping up the album with a sense of introspection.&lt;/p&gt;
&lt;p&gt;Overall, “The Girl Is Crying In Her Latte” is celebrated for its ingenious lyricism, varied musical styles, and the band’s undiminished energy and creativity. Critics and fans alike regard it as a worthy addition to Sparks’ impressive discography, reaffirming their status as innovative artists in the music world.&lt;/p&gt;
&lt;p&gt;For more detailed reviews and insights, you can refer to &lt;span&gt;&lt;a href=&quot;https://spillmagazine.com/spill-album-review-sparks-the-girl-is-crying-in-her-latte/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Spill Magazine&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://setthetape.com/2023/06/02/sparks-the-girl-is-crying-in-her-latte/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Set The Tape&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://maximumvolumemusic.com/review-sparks-the-girl-is-crying-in-her-latte-2023/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Maximum Volume Music&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://thefirenote.com/reviews/sparks-the-girl-is-crying-in-her-latte-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Fire Note&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://www.musicomh.com/reviews/albums/sparks-the-girl-is-crying-in-her-latte&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;musicOMH&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Simplicity - Matt Berry (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/simplicity-28940773/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;Matt Berry’s 2023 album “Simplicity” is a distinctive and innovative work, created in collaboration with the library music label KPM. The album consists of 11 instrumental tracks, each around three minutes long, and is notable for its retro tone and intelligent construction. This collection of songs is very much in the vein of Berry’s previous works like “Blue Elephant” and “Music for Insomniacs,” marked by their familiarity yet fresh and lively feel.&lt;/p&gt;
&lt;p&gt;The single “Top Brass” from the album is described as a jaunty tune that quickly engages the listener, encapsulating the theme tune territory. The album as a whole balances on the edge of being saccharine but never fully crosses into that territory, showcasing Berry’s ability to create unique tunes that complement his comedic talents.&lt;/p&gt;
&lt;p&gt;“Simplicity” is recognized for its sharp and vibrant instrumentals, which are predicted to feature in various productions for many years to come. The album is a testament to Berry’s prodigious talent, both in creating music and in his comedic work. His collaboration with KPM has allowed him to explore a range of musical styles, all while staying true to his unique artistic voice.&lt;/p&gt;
&lt;p&gt;For more detailed information and reviews on Matt Berry’s “Simplicity,” you can visit The Arts Desk &lt;span&gt;&lt;a href=&quot;https://www.theartsdesk.com/new-music/album-matt-berry-simplicity&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Arts Desk&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and AllMusic &lt;span&gt;&lt;a href=&quot;https://www.allmusic.com/album/simplicity-mw0004100933&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AllMusic&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Prism - The Orb (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/prism-26908850/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“The Orb’s album “Prism,” released on April 28, 2023, is a rich blend of various musical styles, demonstrating the duo’s versatility and mastery in electronic music. The album is described as one of their best, despite not introducing anything particularly new. It reaffirms The Orb’s love for music, especially reggae, which is prominently featured in the song-based material. The album spans about 70 minutes, offering a diverse musical journey.&lt;/p&gt;
&lt;p&gt;The track “H.O.M.E. (High Orbs Mini Earths)” is particularly noted for its intense house music feel, with strong references to the Chicago sound of Mr Fingers’ “Can You Feel It?”. Another highlight is “Picking Tea Leaves &amp;amp; Chasing Butterflies,” which features exquisite contributions from a solo trumpet, adding a colonial flavor to the piece.&lt;/p&gt;
&lt;p&gt;“A Ghetto Love Story” and “Tiger” are also mentioned as standout tracks. “A Ghetto Love Story” combines reggae with a trippy production and a booming bass, while “Tiger” is an emotionally deeper track with an urgent vocal exhorting happiness.&lt;/p&gt;
&lt;p&gt;The album also explores the realms of pure ambience in tracks like “Why Can You Be In Two Places At Once, When You Can’t Be Anywhere At All,” which is classic ambient Orb. Another track, “Living In Recycled Times,” ventures into drum ‘n’ bass, drawing inspiration from the likes of Adam F and Alex Reece.&lt;/p&gt;
&lt;p&gt;Overall, “Prism” is described as an uplifting and positive album, blending musical ambience, songwriting substance, and sample-based humor. It’s an album that’s not just for sitting back and relaxing, but also encourages the listener to get up and dance, reflecting The Orb’s reputation as the post-rave therapy of choice for generations of clubbers.&lt;/p&gt;
&lt;p&gt;For a more detailed exploration of the album, you can refer to the reviews from &lt;span&gt;&lt;a href=&quot;https://www.popmatters.com/the-orb-prism-album-review&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PopMatters&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://joeybeltdrives.com/the-orb-prism/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Joey Beltdrives&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.allmusic.com/album/prism-mw0004100933&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AllMusic&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.musicomh.com/reviews/albums/the-orb-prism&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;musicOMH&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://www.theartsdesk.com/new-music/album-orb-prism&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Arts Desk&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.”&lt;/p&gt;
&lt;h2&gt;Version Girl - Rhoda Dakar  (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/version-girl-27220650/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Version Girl” by Rhoda Dakar, released in 2023, is a remarkable album that showcases Dakar’s love for Reggae, Ska, and Rocksteady. This 12-song collection, her first album in five years, features a variety of covers from artists like David Bowie, Tim Buckley, The Pretenders, Louis Armstrong, and The Rolling Stones, each transformed and reimagined through Dakar’s unique musical lens.&lt;/p&gt;
&lt;p&gt;The album begins with the eponymous track “Version Girl,” which is a cover of Dandy Livingstone’s 1970 song and sets the tone for Dakar’s approach to the album. The track “As Tears Go By” is a notable cover of the Rolling Stones’ song, delivering a rendition that retains the original’s poignancy while adding a reggae twist. Another standout track, “Love Hurts,” shows Dakar’s ability to adapt a well-known song into a new genre, offering a ska-influenced take on this classic.&lt;/p&gt;
&lt;p&gt;“Stop Your Sobbing,” originally by The Pretenders, is transformed into a “reggae skinhead” track, demonstrating the adaptability of Jamaican musical genres. Dakar’s cover of Tim Buckley’s “Song to Siren” provides a moving rocksteady rendition that doesn’t lose the original’s emotional depth. Her version of “What a Wonderful World” by Louis Armstrong adds a fresh perspective to this beloved classic, infusing it with a reggae flavor.&lt;/p&gt;
&lt;p&gt;Overall, “Version Girl” is an album that radiates energy and vitality. It’s a testament to Dakar’s skill as an artist and her ability to breathe new life into well-known songs, proving her status as a significant figure in the UK pop-reggae scene. The album is celebrated for its diversity and the way it effortlessly shifts the meaning of the original songs, providing a set of covers that are both familiar and refreshingly new.&lt;/p&gt;
&lt;p&gt;For more detailed insights into the album, you can refer to the reviews from Louder Than War &lt;span&gt;&lt;a href=&quot;https://louderthanwar.com/rhoda-dakar-version-girl-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Louder Than War&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and God Is In The TV &lt;span&gt;&lt;a href=&quot;https://www.godisinthetvzine.co.uk/2023/05/29/rhoda-dakar-version-girl-sunday-best-recordings/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;God Is In The TV&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Joy’All - Jenny Lewis (&lt;span&gt;&lt;a href=&quot;https://www.russ.fm/albums/joy-all-27328932/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;)&lt;/h2&gt;
&lt;p&gt;“Joy’All,” the fifth solo album by Jenny Lewis, released in 2023, marks a significant step in her artistic journey. The album, recorded entirely in Nashville, is imbued with a country tinge, but it also reflects the complexity of emotions that Lewis is known for, as the former lead singer of Rilo Kiley.&lt;/p&gt;
&lt;p&gt;“Joy’All” is characterized by its light and breezy sound, a surprising element considering it was born out of a pandemic project. The album is a beautiful amalgamation of Lewis’ trademark wit, soulful ruminations, and insightful observations on life. Songs like “Puppy And A Track” stand out for their classic rock atmosphere, enhanced by little touches of pedal steel, encapsulating Lewis’s experience with a mid-life crisis in a lighthearted, yet profound way.&lt;/p&gt;
&lt;p&gt;The title track, “Joy’All,” recalls the style of Ani DiFranco, a former collaborator of Lewis, and is marked by handclaps, vocalizations, and multi-tracked harmonies. “Psychos,” the opening track, showcases Lewis’s witty lyricism, while “Giddy Up” is seductive and laidback, demonstrating her versatility as an artist.&lt;/p&gt;
&lt;p&gt;“Apples And Oranges” and “Balcony” are tracks where Lewis delves into deeper emotional territories, touching upon themes of loss and longing. “Essence Of Life” stands out for its moody waltz rhythm and Lewis’ ominously sparse vocal approach. The album also features an infusion of FM radio sheen in its production, adding a contemporary touch to the music.&lt;/p&gt;
&lt;p&gt;“Joy’All” has been likened to the work of Fleetwood Mac and Stevie Nicks, reflecting a soft rock ethos reminiscent of California’s golden era of singer-songwriters. The album is described as adult pop/rock with an attitude, showcasing Lewis’s growth and maturity as an artist.&lt;/p&gt;
&lt;p&gt;Overall, “Joy’All” is celebrated as possibly Jenny Lewis’s best solo work to date, embodying her fearless confrontation with middle age and the complexities it brings. It is seen as a record that both haunts and inspires, being fun and uniquely complicated, a testament to Lewis’s enduring talent and creativity.&lt;/p&gt;
&lt;p&gt;For more detailed reviews of Jenny Lewis’s “Joy’All,” you can read the insights from &lt;span&gt;&lt;a href=&quot;https://thefirenote.com/2023/06/14/jenny-lewis-joyall-album-review/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Fire Note&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.allmusic.com/album/joyall-mw0003963498&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AllMusic&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, &lt;span&gt;&lt;a href=&quot;https://www.popmatters.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PopMatters&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, and &lt;span&gt;&lt;a href=&quot;https://www.musicomh.com/reviews/albums/jenny-lewis-joyall&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;musicOMH&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2023-12-17-2023-top-10.DeTutQq4.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2023/12/23/my-top-10-of-2023-og.png</blog:ogImage><blog:readingTime>26</blog:readingTime><category>life</category><category>vinyl</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Playing with Aider Chat</title><link>https://www.russ.cloud/2023/07/15/playing-with-aider-chat/</link><guid isPermaLink="true">https://www.russ.cloud//2023/07/15/playing-with-aider-chat/</guid><description>I had some time so decided to have a play with a new tool called Aider Chat, which allows you to write and edit code with OpenAI’s GPT models.</description><pubDate>Sat, 15 Jul 2023 12:01:41 GMT</pubDate><content:encoded>&lt;p&gt;I had some time, so I thought I would take a look at &lt;span&gt;&lt;a href=&quot;https://aider.chat&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Aider Chat&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;,  Aidir Chat is described by its authors as;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;aider is a command-line chat tool that allows you to write and edit code with OpenAI’s GPT models. You can ask GPT to help you start a new project, or modify code in your existing &lt;a href=&quot;/glossary/git/&quot;&gt;git&lt;/a&gt; repo. Aider makes it easy to git commit, diff &amp;amp; undo changes proposed by GPT without copy/pasting. It also has features that &lt;span&gt;&lt;a href=&quot;https://aider.chat/docs/ctags.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;help GPT-4 understand and modify larger codebases&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;As you may know from reading other blog posts, I am a macOS user, so the commands in this post will cover macOS only.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Installing Aider Chat is a simple process which involves running two commands, the first installs Aider Chat;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installing Aider Chat&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; pip3 install aider-chat&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The second command installs the [Universal Ctags](https://github.com/universal-ctags/ctags) package using [Homebrew](https://brew.sh/);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Installing universal-ctags&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ brew install universal-ctags&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Before running Aider Chat, you need to provide a valid OpenAI API key. To do this, you can export it as an environment variable by running the following;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Exporting your OpenAI API key&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ export OPENAI_API_KEY=sk-test1234567890abcdef0123456789&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This will be valid for the life of your terminal session, or you can add it to a  config file which is stored at `~/.aider.conf.yml` by running the command which follows;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Creating the ~/.aider.conf.yml file&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ echo &quot;openai-api-key: sk-test1234567890abcdef0123456789&quot; &amp;gt; ~/.aider.conf.yml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Now we have everything installed and configured lets dive straight in and create a project, to do this create a new folder and initiate a chat by running the commands below;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Creating the ~/.aider.conf.yml file&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;mkdir aider-blog-post&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aider ./&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This will show you something like the following output;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Creating the ~/.aider.conf.yml file&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;No git repo found, create one to track GPT&apos;s changes (recommended)? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Git repository created in the current working directory.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Model: gpt-4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Git repo: .git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Repo-map: universal-ctags using 1024 tokens&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Use /help to see in-chat commands.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;From here, we can start asking questions;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;TipCallout title=&quot;Russ&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Write a basic Snake game using Python and Pygame, it should have the following …&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;- Have splash screen&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;- Use the Arrow keys to control the snake&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;- Each time the snake eats an apple, it should get faster&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;- Use Emjois for the graphics where possible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;- Call the game file main.py and create a requirements.txt containing the requirements needed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/TipCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This is what Aider had to say (please note, I have removed the code blocks for readability);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;NoteCallout title=&quot;Aider&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Sure, I can help you with that. Here&apos;s a basic structure of how the game could be implemented.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;First, let&apos;s create the `requirements.txt` file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Now, let&apos;s create the `main.py` file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Please note that this is a basic implementation of the game. You may need to add more features or modify the existing ones to suit your needs. Also, the use of emojis in Pygame is not straightforward and may require additional libraries or a different approach.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Allow creation of new file new_file/requirements.txt? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Add new_file/requirements.txt to git? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Allow creation of new file new_file/main.py? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Add new_file/main.py to git? y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Applied edit to new_file/main.py&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Applied edit to new_file/requirements.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Commit e7ef909 aider: Imported the necessary libraries and defined the main game loop, start screen, game over screen, and helper functions.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/NoteCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;I then tried to run the code, and got an error - lets give Aider the error;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;TipCallout title=&quot;Russ&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;name &apos;DARKGREEN&apos; is not defined&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/TipCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;To which I got the following response;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;NoteCallout title=&quot;Aider&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;It seems like the color `DARKGREEN` is not defined in your code. Let&apos;s add it to the color definitions in your `main.py` file.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This will define `DARKGREEN` as a darker shade of green. You can adjust the RGB values as needed to get the exact color you want.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Applied edit to new_file/main.py&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Commit 06f2242 aider: Added DARKGREEN as a darker shade of green to the color definitions in main.py.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/NoteCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;There were some other complaints about the code, after pasting the code in I decided to ask it something else;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;TipCallout title=&quot;Russ&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;More errors about colors, can you read through the code and name sure they are all correctly defined, the last error was &quot;NameError: name &apos;DARKGRAY&apos; is not defined. Did you mean: &apos;DARKGREEN&apos;?&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/TipCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This did the trick, not only did Aider address the issue, but also checked the entire code for other missing elements;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;NoteCallout title=&quot;Aider&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Sure, I can help with that. It seems like the color `DARKGRAY` is not defined in your code. Let&apos;s add it to the color definitions in your `main.py` file.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This will define `DARKGRAY` as a dark shade of gray. You can adjust the RGB values as needed to get the exact color you want.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Also, I noticed that there are some other variables like `FPS` and `SNAKESPEED` that are not defined in your code. Let&apos;s add them as well.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This will define `FPS` and `SNAKESPEED` as 15. You can adjust these values as needed to get the desired game speed.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Applied edit to new_file/main.py&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Commit 00f2451 aider: Added color `DARKGRAY` and constants `FPS` and `SNAKESPEED` to `main.py`.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/NoteCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;There was some more errors, this time around missing functions and some of the game logic - after about 10 minutes back and forth with Aider, I had a working game.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;TipCallout title=&quot;Russ&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Check the new_file/main.py file for any other missing functions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/TipCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Again, Aider was able to help;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;NoteCallout title=&quot;Aider&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Sure, I can help with that. Looking at your code, it seems like the functions `drawApple` and `drawScore` are being called but they are not defined in your code.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Let&apos;s add basic definitions for these functions in your `main.py` file.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The `drawApple` function draws the apple on the game screen by drawing a red rectangle at the apple&apos;s coordinates. The `drawScore` function displays the current score on the game screen. You can adjust the color and the size of the apple and the score display as needed.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Applied edit to new_file/main.py&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;gt; Commit 9d50b5a aider: Added missing functions `drawApple` and `drawScore` to `main.py`.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/NoteCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This time everything worked as expected and we have a mostly functional game as you can see from the following screens;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;LightGallery&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;layout={{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;imgs: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ src: &quot;/assets/2023-07-15-playing-with-aider-chat/images/001.png&quot; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ src: &quot;/assets/2023-07-15-playing-with-aider-chat/images/002.png&quot; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ src: &quot;/assets/2023-07-15-playing-with-aider-chat/images/003.png&quot; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ src: &quot;/assets/2023-07-15-playing-with-aider-chat/images/004.png&quot; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;options={{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;thumbnail: true,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The game code is below, it is untouched from what Aider Chat created and as you can see, functionality wise is a little hit and miss, but it well commented and easy to follow;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```python frame=&quot;code&quot; title=&quot;Python&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```python {linenos=true}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import pygame&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import sys&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import time&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import random&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import emoji&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;from pygame.locals import *&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# set up some constants&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WINDOWWIDTH = 800&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WINDOWHEIGHT = 600&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CELLSIZE = 20&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;FPS = 15&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;SNAKESPEED = 15&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# calculate cell width and height&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CELLWIDTH = int(WINDOWWIDTH / CELLSIZE)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CELLHEIGHT = int(WINDOWHEIGHT / CELLSIZE)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# set up the colours&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WHITE = (255, 255, 255)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;GREEN = (0, 255, 0)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;RED = (255, 0, 0)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;DARKGREEN = (0, 100, 0)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;DARKGRAY = (40, 40, 40)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;BGCOLOR = (0, 0, 0) # Black background&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# set up the direction variables&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;UP = &apos;up&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;DOWN = &apos;down&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LEFT = &apos;left&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;RIGHT = &apos;right&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HEAD = 0 # index of the snake&apos;s head&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def main():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;global FPSCLOCK, DISPLAYSURF, BASICFONT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.init()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FPSCLOCK = pygame.time.Clock()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;BASICFONT = pygame.font.Font(&apos;freesansbold.ttf&apos;, 18)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.display.set_caption(&apos;Snake Game&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showStartScreen()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;while True:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;runGame()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;showGameOverScreen()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def runGame():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Set a random start point.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;startx = random.randint(5, CELLWIDTH - 6)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;starty = random.randint(5, CELLHEIGHT - 6)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wormCoords = [{&apos;x&apos;: startx, &apos;y&apos;: starty},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&apos;x&apos;: startx - 1, &apos;y&apos;: starty},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&apos;x&apos;: startx - 2, &apos;y&apos;: starty}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;direction = RIGHT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Start the apple in a random place.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;apple = getRandomLocation()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;while True: # main game loop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;for event in pygame.event.get(): # event handling loop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if event.type == QUIT:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;terminate()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elif event.type == KEYDOWN:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (event.key == K_UP or event.key == K_w) and direction != DOWN:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;direction = UP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elif (event.key == K_DOWN or event.key == K_s) and direction != UP:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;direction = DOWN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elif (event.key == K_LEFT or event.key == K_a) and direction != RIGHT:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;direction = LEFT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elif (event.key == K_RIGHT or event.key == K_d) and direction != LEFT:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;direction = RIGHT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elif event.key == K_ESCAPE:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;terminate()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# check if the Snake has hit itself or the edge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if wormCoords[HEAD][&apos;x&apos;] == -1 or wormCoords[HEAD][&apos;x&apos;] == CELLWIDTH or wormCoords[HEAD][&apos;y&apos;] == -1 or wormCoords[HEAD][&apos;y&apos;] == CELLHEIGHT:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return # game over&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;for wormBody in wormCoords[1:]:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if wormBody[&apos;x&apos;] == wormCoords[HEAD][&apos;x&apos;] and wormBody[&apos;y&apos;] == wormCoords[HEAD][&apos;y&apos;]:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return # game over&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# check if Snake has eaten an apply&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if wormCoords[HEAD][&apos;x&apos;] == apple[&apos;x&apos;] and wormCoords[HEAD][&apos;y&apos;] == apple[&apos;y&apos;]:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# don&apos;t remove worm&apos;s tail segment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;apple = getRandomLocation() # set a new apple somewhere&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;del wormCoords[-1] # remove worm&apos;s tail segment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# move the worm by adding a segment in the direction it is moving&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if direction == UP:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;newHead = {&apos;x&apos;: wormCoords[HEAD][&apos;x&apos;], &apos;y&apos;: wormCoords[HEAD][&apos;y&apos;] - 1}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elif direction == DOWN:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;newHead = {&apos;x&apos;: wormCoords[HEAD][&apos;x&apos;], &apos;y&apos;: wormCoords[HEAD][&apos;y&apos;] + 1}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elif direction == LEFT:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;newHead = {&apos;x&apos;: wormCoords[HEAD][&apos;x&apos;] - 1, &apos;y&apos;: wormCoords[HEAD][&apos;y&apos;]}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elif direction == RIGHT:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;newHead = {&apos;x&apos;: wormCoords[HEAD][&apos;x&apos;] + 1, &apos;y&apos;: wormCoords[HEAD][&apos;y&apos;]}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wormCoords.insert(0, newHead)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DISPLAYSURF.fill(BGCOLOR)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;drawGrid()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;drawWorm(wormCoords)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;drawApple(apple)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;drawScore(len(wormCoords) - 3)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.display.update()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FPSCLOCK.tick(SNAKESPEED)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def drawPressKeyMsg():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pressKeySurf = BASICFONT.render(&apos;Press a key to play.&apos;, True, DARKGRAY)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pressKeyRect = pressKeySurf.get_rect()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pressKeyRect.topleft = (WINDOWWIDTH - 200, WINDOWHEIGHT - 30)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DISPLAYSURF.blit(pressKeySurf, pressKeyRect)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def checkForKeyPress():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if len(pygame.event.get(QUIT)) &amp;gt; 0:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;terminate()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;keyUpEvents = pygame.event.get(KEYUP)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if len(keyUpEvents) == 0:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return None&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if keyUpEvents[0].key == K_ESCAPE:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;terminate()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return keyUpEvents[0].key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def showStartScreen():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;titleFont = pygame.font.Font(&apos;freesansbold.ttf&apos;, 100)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;titleSurf1 = titleFont.render(&apos;Snake Game!&apos;, True, WHITE, DARKGREEN)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;degrees1 = 0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;degrees2 = 0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;while True:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DISPLAYSURF.fill(BGCOLOR)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rotatedSurf1 = pygame.transform.rotate(titleSurf1, degrees1)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rotatedRect1 = rotatedSurf1.get_rect()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rotatedRect1.center = (WINDOWWIDTH / 2, WINDOWHEIGHT / 2)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DISPLAYSURF.blit(rotatedSurf1, rotatedRect1)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;drawPressKeyMsg()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if checkForKeyPress():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.event.get() # clear event queue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.display.update()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FPSCLOCK.tick(FPS)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;degrees1 += 3 # rotate by 3 degrees each frame&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;degrees2 += 7 # rotate by 7 degrees each frame&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def terminate():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.quit()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sys.exit()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def getRandomLocation():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return {&apos;x&apos;: random.randint(0, CELLWIDTH - 1), &apos;y&apos;: random.randint(0, CELLHEIGHT - 1)}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def drawGrid():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;for x in range(0, WINDOWWIDTH, CELLSIZE): # draw vertical lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.draw.line(DISPLAYSURF, DARKGRAY, (x, 0), (x, WINDOWHEIGHT))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;for y in range(0, WINDOWHEIGHT, CELLSIZE): # draw horizontal lines&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.draw.line(DISPLAYSURF, DARKGRAY, (0, y), (WINDOWWIDTH, y))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def drawWorm(wormCoords):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;for coord in wormCoords:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;x = coord[&apos;x&apos;] * CELLSIZE&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;y = coord[&apos;y&apos;] * CELLSIZE&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wormSegmentRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.draw.rect(DISPLAYSURF, DARKGREEN, wormSegmentRect)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wormInnerSegmentRect = pygame.Rect(x + 4, y + 4, CELLSIZE - 8, CELLSIZE - 8)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.draw.rect(DISPLAYSURF, GREEN, wormInnerSegmentRect)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def drawApple(coord):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;x = coord[&apos;x&apos;] * CELLSIZE&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;y = coord[&apos;y&apos;] * CELLSIZE&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;appleRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.draw.rect(DISPLAYSURF, RED, appleRect)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def drawScore(score):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scoreSurf = BASICFONT.render(&apos;Score: %s&apos; % (score), True, WHITE)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scoreRect = scoreSurf.get_rect()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scoreRect.topleft = (WINDOWWIDTH - 120, 10)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DISPLAYSURF.blit(scoreSurf, scoreRect)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def showGameOverScreen():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gameOverFont = pygame.font.Font(&apos;freesansbold.ttf&apos;, 150)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gameSurf = gameOverFont.render(&apos;Game&apos;, True, WHITE)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;overSurf = gameOverFont.render(&apos;Over&apos;, True, WHITE)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gameRect = gameSurf.get_rect()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;overRect = overSurf.get_rect()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gameRect.midtop = (WINDOWWIDTH / 2, 10)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;overRect.midtop = (WINDOWWIDTH / 2, gameRect.height + 10 + 25)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DISPLAYSURF.blit(gameSurf, gameRect)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DISPLAYSURF.blit(overSurf, overRect)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;drawPressKeyMsg()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.display.update()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.time.wait(500)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;checkForKeyPress() # clear out any key presses in the event queue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;while True:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if checkForKeyPress():&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pygame.event.get() # clear event queue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if __name__ == &apos;__main__&apos;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;main()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;As you can see Aider is is simple to use and I suspect is way more powerful than I have shown here, I am going to spend some more time playing with it and see what else it can do.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;If you want to learn more about Aider Chat, you can more examples [here](https://aider.chat/examples/README.html) and a GitHub repo containing the code and full chat history I generated for this post [here](https://github.com/russmckendrick/aider-blog-post).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2023-07-15-playing-with-aider-chat.rHSgdyUK.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2023/07/15/playing-with-aider-chat-og.png</blog:ogImage><blog:readingTime>10</blog:readingTime><category>ai</category><category>code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Revisting Podman</title><link>https://www.russ.cloud/2023/06/14/revisting-podman/</link><guid isPermaLink="true">https://www.russ.cloud//2023/06/14/revisting-podman/</guid><description>Delve into the exciting features and benefits of Podman, a powerful alternative to traditional container technologies.</description><pubDate>Wed, 14 Jun 2023 08:12:54 GMT</pubDate><content:encoded>&lt;p&gt;Although it’s been some time since I’ve worked with containers on my local machine, I recently found myself intrigued to discover what’s new with &lt;a href=&quot;/glossary/podman/&quot;&gt;Podman&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Setting Up Podman&lt;/h2&gt;
&lt;p&gt;To kick things off, we’ll need to get Podman installed. As my development environment is running on a Mac M2, I’m going to employ Homebrew for this purpose:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install Podman&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; brew install podman podman-compose podman-desktop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;In addition to Podman itself, I&apos;ve also chosen to install Podman Compose and Podman Desktop. The former serves as a fitting replacement for Docker Compose, allowing for the definition and orchestration of multi-container applications within the Podman environment. On the other hand, Podman Desktop is a handy GUI for Podman that substitutes Docker Desktop.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Following the successful installation, you can get Podman up and running with the subsequent command:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Start Podman&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` shell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ podman machine init&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ podman machine start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This process will essentially download, install, and kickstart a qemu virtual machine designed to run Podman, mirroring the mechanism employed by Docker Desktop.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Upon successful installation, you should encounter an output akin to this:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Up and running&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Starting machine &quot;podman-machine-default&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Waiting for VM ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Mounting volume... /Users:/Users&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Mounting volume... /private:/private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Mounting volume... /var/folders:/var/folders&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This machine is currently configured in rootless mode. If your containers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;require root permissions (e.g. ports &amp;lt; 1024), or if you run into compatibility&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;issues with non-podman clients, you can switch using the following command:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;podman machine set --rootful&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;API forwarding listening on: /Users/russ.mckendrick/.local/share/containers/podman/machine/qemu/podman.sock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The system helper service is not installed; the default Docker API socket&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;address can&apos;t be used by podman. If you would like to install it run the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;following commands:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sudo /opt/homebrew/Cellar/podman/4.5.1/bin/podman-mac-helper install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;podman machine stop; podman machine start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;You can still connect Docker API clients by setting DOCKER_HOST using the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;following command in your terminal session:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;export DOCKER_HOST=&apos;unix:///Users/russ.mckendrick/.local/share/containers/podman/machine/qemu/podman.sock&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Machine &quot;podman-machine-default&quot; started successfully&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As observed, Podman operates in ‘rootless’ mode by default, signifying it’s running as a non-root user. This was a distinctive feature that originally set Podman apart from its Docker counterpart.&lt;/p&gt;
&lt;h2&gt;A Bit More Configuration&lt;/h2&gt;
&lt;p&gt;If you’ve been keen, you might have noticed from the previous output that the system helper is not yet installed. To rectify this, you’ll need to run the following commands:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Set DOCKER_HOST&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; sudo podman-mac-helper install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; podman machine stop&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;podman&lt;/span&gt;&lt;span&gt; machine start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Executing these commands will ensure a degree of compatibility with apps that typically interface with Docker. Although this step isn&apos;t strictly necessary, it certainly adds a convenient layer of flexibility.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Saying Hello to Podman&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Before we delve into Podman Desktop, let&apos;s kickstart our journey with the traditional Hello World container. To do this, execute the following command:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Running Hello World&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` shell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ podman run quay.io/podman/hello&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This command will fetch and run the Podman Hello World container. As a result, you should see an output somewhat like this:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Hello World output&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` shell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Trying to pull quay.io/podman/hello:latest...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Getting image source signatures&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Copying blob sha256:9bdf7ac1ed918fe590f80ad610afc6ef128f28c8b9e5ed27c428fa8df844b526&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Copying config sha256:464d54267dd11a4b67ebd8236ff83df0f710f40db23642c97456c45619eb4587&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Writing manifest to image destination&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Storing signatures&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;!... Hello Podman World ...!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.--&quot;--.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/ -     - \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/ (O)   (O) \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;~~~| -=(,Y,)=- |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.---. /`  \   |~~&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;~/  o  o \~~~~.----. ~~&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;| =(X)= |~  / (O (O) \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;~~~~~~~  ~| =(Y_)=-  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;~~~~    ~~~|   U      |~~&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Project:   https://github.com/containers/podman&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Website:   https://podman.io&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Documents: https://docs.podman.io&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Twitter:   @Podman_io&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;For now, we&apos;ll leave the container in its existing state, which is &apos;Exited&apos; (you can verify this by running `podman container ls -a`). We&apos;ll revisit this container shortly when we explore Podman Desktop.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Unveiling Podman Desktop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Now that we&apos;ve confirmed the proper functioning of Podman, it&apos;s time to explore Podman Desktop. As we&apos;ve already got Podman operational, upon opening Podman Desktop for the first time, you should encounter something like the following screens:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/01.png&quot; alt=&quot;First opening Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;After clicking on &quot;Go to Podman Desktop&quot;, you&apos;ll be navigated to the following screen:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/02.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Selecting the Containers icon, located second in the left-hand menu, will display the Hello World container we executed earlier:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/03.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;By clicking on the container, you&apos;ll access detailed information about the container, including its logs:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/04.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;### Building a Container Image&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;With everything in place and running smoothly, let&apos;s venture into creating a container image and deploying it using Podman Desktop. For this purpose, we&apos;ll need a `Dockerfile`. You can use my [sample Dockerfile available on GitHub](https://github.com/russmckendrick/dockerfile-example) - just download and save it to your local machine.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once you&apos;ve saved the `Dockerfile` locally, select the Images icon, which is the fourth icon down in the left-hand menu, and then click on the &quot;Build an Image&quot; button.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/05.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This will open a new window. Click on the &quot;Select File&quot; button and choose the `Dockerfile` that you saved earlier. After selection, click on the &quot;Build&quot; button.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/06.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This action will initiate the build process. Upon completion, you&apos;ll find your new image in the list.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/07.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;### Launching a Container&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;With our custom image ready, let&apos;s deploy it. Click on the play button icon next to the `my-custom-image`.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/08.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This action will open a dialog box, allowing you to enter various details to launch your container. For this demonstration, we&apos;ll stick with the default values and click on the &quot;Start container&quot; button.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/09.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Now, you should see your container up and running. By clicking on the container, you can view the container details, and clicking on the &quot;Open in browser&quot; button (fourth button from the left at the top right corner) will display the container in your browser.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{/* Old gallery removed - please add images manually if needed */}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/10.png&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/11.png&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/12.png&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/13.jpg&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Running the `podman container ls` command in your terminal will display your running container.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;podman container ls&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` shell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CONTAINER ID  IMAGE                                     COMMAND         CREATED        STATUS        PORTS                 NAMES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;c6c62f35150d  docker.io/library/my-custom-image:latest  -g daemon off;  9 minutes ago  Up 9 minutes  0.0.0.0:9000-&amp;gt;80/tcp  inspiring_tesla&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;To stop the container and remove it from Podman Desktop, click on the stop button icon next to the container. In the screen below, I have selected both containers and clicked on the &quot;Delete&quot; button to remove the now stopped containers.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/14.png&quot; alt=&quot;Podman Desktop&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Podman Compose&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Having explored Podman Desktop, let&apos;s turn our attention to Podman Compose. As mentioned earlier, Podman Compose is a tool for defining and running multi-container applications with Podman. As a replacement for Docker Compose, it utilizes the same `docker-compose.yml` file format.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```yaml frame=&quot;code&quot; title=&quot;docker-compose.yml (YAML)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```yaml {linenos=true}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;volumes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;db_data:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;services:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;wordpress:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;image: docker.io/library/wordpress:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ports:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- 8080:80&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;environment:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- WORDPRESS_DB_HOST=db&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- WORDPRESS_DB_USER=wordpress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- WORDPRESS_DB_PASSWORD=password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- WORDPRESS_DB_NAME=wordpress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;db:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;image: docker.io/library/mariadb:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;command: &apos;--default-authentication-plugin=mysql_native_password&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;volumes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- db_data:/var/lib/mysql&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;environment:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- MYSQL_ROOT_PASSWORD=somewordpress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- MYSQL_DATABASE=wordpress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- MYSQL_USER=wordpress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- MYSQL_PASSWORD=password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Save the above `docker-compose.yml` file to a folder called &apos;wordpress&apos; on your local machine. Then, execute the following command to start the containers:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;docker-compose up -d&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` shell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ podman-compose up -d&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once started, you should see a group of containers running, and visiting [http://localhost:8080](http://localhost:8080) in your browser will take you to the WordPress installation screen.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{/* Old gallery removed - please add images manually if needed */}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/15.png&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/16.png&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/17.png&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2023-06-14-revisiting-podman/images/18.png&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;When finished, you can execute the command below to halt the containers:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;docker-compose up -d&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` shell&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ podman-compose stop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Summing up&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;I must admit, I&apos;m impressed with Podman Desktop. It&apos;s a user-friendly GUI for Podman, and the compatibility with Docker is a significant advantage. While I might not utilize it consistently, having the option is beneficial.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The sentiment extends to Podman Compose too. Having a comprehensive Docker replacement available locally when the need to run containers arises is indeed a plus. Whether you&apos;re dabbling with containers out of curiosity or managing complex multi-container applications, Podman, Podman Desktop, and Podman Compose prove to be robust and reliable tools for the task.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2023-06-14-revisiting-podman.WxSmi337.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2023/06/14/revisting-podman-og.png</blog:ogImage><blog:readingTime>7</blog:readingTime><category>Podman</category><category>Docker</category><category>Containers</category><category>macOS</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Infrastructure as Code for Beginners is out now</title><link>https://www.russ.cloud/2023/06/04/infrastructure-as-code-for-beginners-is-out-now/</link><guid isPermaLink="true">https://www.russ.cloud//2023/06/04/infrastructure-as-code-for-beginners-is-out-now/</guid><description>My new book, Infrastructure as Code for Beginners, has been released and is available to buy now.</description><pubDate>Sun, 04 Jun 2023 09:56:17 GMT</pubDate><content:encoded>&lt;p&gt;I am pleased to announce that my latest book, “&lt;a href=&quot;/glossary/infrastructure-as-code/&quot;&gt;Infrastructure as Code&lt;/a&gt; for Beginners,” is now available for purchase.&lt;/p&gt;
&lt;p&gt;Over the last six months, I have fully dedicated myself to this project, channeling my experience and knowledge into providing a resource that simplifies the complexities of Infrastructure as Code (IaC).&lt;/p&gt;
&lt;p&gt;The importance of IaC cannot be overstated in today’s rapidly evolving technological landscape. IaC ensures consistent and repeatable deployment of cloud-based services, thereby saving time while delivering robust results. However, understanding and effectively implementing IaC can be a challenge for many, and that’s where this book comes in.&lt;/p&gt;
&lt;p&gt;“Infrastructure as Code for Beginners” is designed as a comprehensive guide to help you navigate the world of IaC. From understanding the foundations of IaC to making informed decisions when implementing it, this book covers it all. One of its unique aspects is the emphasis on practicality – there are step-by-step explanations and real-world examples that not only make the concepts easier to grasp but also showcase their application in your projects.&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2023-06-04-infrastructure-as-code-for-beginners-is-out-now/images/book.jpg&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2023-06-04-infrastructure-as-code-for-beginners-is-out-now/images/book.jpg&quot; alt=&quot;Infrastructure as Code for Beginners&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2250&quot; height=&quot;2775&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The book delves into popular IaC tools such as &lt;a href=&quot;/glossary/terraform/&quot;&gt;Terraform&lt;/a&gt; and &lt;a href=&quot;/glossary/ansible/&quot;&gt;Ansible&lt;/a&gt;, highlighting their pros, cons, and best practices. You’ll also get hands-on experience designing, deploying, and securing your infrastructure on two major cloud platforms, &lt;a href=&quot;/glossary/azure/&quot;&gt;Microsoft Azure&lt;/a&gt; and &lt;a href=&quot;/glossary/aws/&quot;&gt;Amazon Web Services&lt;/a&gt; (AWS).&lt;/p&gt;
&lt;p&gt;We don’t stop there – the book also explores other IaC tools like Pulumi, AWS CloudFormation, and Azure &lt;a href=&quot;/glossary/bicep/&quot;&gt;Bicep&lt;/a&gt;, broadening your knowledge and skillset.&lt;/p&gt;
&lt;p&gt;What sets “Infrastructure as Code for Beginners” apart is its easy-to-follow structure. The book is divided into nine chapters, each addressing a different aspect of IaC, beginning with fundamental concepts and progressing to more advanced topics. This logical structure makes it easy for readers to follow along and build their knowledge gradually.&lt;/p&gt;
&lt;p&gt;Writing “Infrastructure as Code for Beginners” has been a fulfilling journey for me, and I am hopeful that it will prove to be a valuable resource for anyone looking to delve into or refine their understanding of IaC.&lt;/p&gt;
&lt;p&gt;The book is now available for purchase at &lt;span&gt;&lt;a href=&quot;https://www.packtpub.com/product/infrastructure-as-code-for-beginners/9781837631636&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Packt Publishing&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href=&quot;https://www.amazon.co.uk/dp/B0C3CVRD86/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Amazon&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;. I look forward to hearing your thoughts and feedback on it.&lt;/p&gt;
&lt;p&gt;Happy reading! :books:&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2023-06-04-infrastructure-as-code-for-beginners-is-out-now.CtuZPilc.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2023/06/04/infrastructure-as-code-for-beginners-is-out-now-og.png</blog:ogImage><blog:readingTime>2</blog:readingTime><category>Book</category><category>Author</category><category>Life</category><category>Infrastructure as Code</category><category>Terraform</category><category>Ansible</category><category>AWS</category><category>Azure</category><category>GitHub</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Ansible setup with Azure on a new Mac</title><link>https://www.russ.cloud/2021/12/28/ansible-setup-with-azure-on-a-new-mac/</link><guid isPermaLink="true">https://www.russ.cloud//2021/12/28/ansible-setup-with-azure-on-a-new-mac/</guid><description>Learn how to set up Ansible with Azure on a new Mac, addressing common issues with cryptography and module dependencies.</description><pubDate>Tue, 28 Dec 2021 12:38:25 GMT</pubDate><content:encoded>&lt;p&gt;Following on from upgrading my Macs to all M1-based ones, I get around to finally needing to use &lt;a href=&quot;/glossary/ansible/&quot;&gt;Ansible&lt;/a&gt;; typically, I would install &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; using &lt;a href=&quot;/2021/10/30/managing-python-on-macos-monterey/&quot;&gt;PyEnv&lt;/a&gt; and then run the following …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;It should just work?!?!&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/ansible-collections/azure/blob/dev/requirements-azure.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible-galaxy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;collection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;azure.azcollection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;… and away we go, but this time I came across a few problems.&lt;/p&gt;
&lt;p&gt;The first was the following error when installing the requirements using the remote &lt;code&gt;requirements-azure.txt&lt;/code&gt; file:&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;Failed to build cryptography&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This was an easy one to fix; I just needed to run the following to get enough of the pre-requisites installed …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installation openssl &amp;amp; rust&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;openssl@1.1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rust&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once installed, I then needed to update the &lt;code&gt;pip&lt;/code&gt; command I was using, so it looks like this:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Installation openssl &amp;amp; rust&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LDFLAGS=&quot;-L&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--prefix&lt;/span&gt;&lt;span&gt; openssl@1.1&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;/lib&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CFLAGS=&quot;-I&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--prefix&lt;/span&gt;&lt;span&gt; openssl@1.1&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;/include&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/ansible-collections/azure/blob/dev/requirements-azure.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Hey presto, cryptography built and installed OK. However, things changed when running Ansible; I got the following error …&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Warning&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;ModuleNotFoundError: No module named ‘&lt;a href=&quot;/glossary/azure/&quot;&gt;azure&lt;/a&gt;.mgmt.monitor.version’&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Erm, how could that be? Everything was installed as expected with no errors after installing the correct version of OpenSSL and Rust. It turns out that my usual way of installing Ansible isn’t actually the recommended way, so let’s look at how I should have installed it using the following commands …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;It should just work, take two&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;openssl@1.1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rust&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LDFLAGS=&quot;-L&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--prefix&lt;/span&gt;&lt;span&gt; openssl@1.1&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;/lib&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CFLAGS=&quot;-I&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--prefix&lt;/span&gt;&lt;span&gt; openssl@1.1&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;/include&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cryptography&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible-galaxy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;collection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;azure.azcollection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ansible/collections/ansible_collections/azure/azcollection/requirements-azure.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you can see, first of all by installing the &lt;code&gt;cryptography&lt;/code&gt; module and its pre-requisites, then install Ansible and then the Azure collection. Once the Azure collection is installed, use the &lt;code&gt;requirements-azure.txt&lt;/code&gt; which ships with the collection rather than remotely using the one straight from &lt;a href=&quot;/glossary/github/&quot;&gt;GitHub&lt;/a&gt; - this is because the one in GitHub is way ahead of the one which is the current release of the Azure collection on &lt;span&gt;&lt;a href=&quot;https://galaxy.ansible.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ansible Galaxy&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;While things “may work” if there is active development going on which has not made its way into the Ansible Galaxy release, then you may have all sorts of compatibility issues, just like I did.&lt;/p&gt;
&lt;p&gt;Hopefully, this saves someone scratching their head for hours as I did as everything looked to have been correct, and the &lt;code&gt;azure.mgmt.monitor&lt;/code&gt; module was 100% installed, just not the version which the collection was expecting.&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2021-12-28-ansible-azure-setup-new-mac.BdSqZQ3w.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2021/12/28/ansible-setup-with-azure-on-a-new-mac-og.png</blog:ogImage><blog:readingTime>3</blog:readingTime><category>macos</category><category>azure</category><category>ansible</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>VSCode + Bicep on an M1 Mac</title><link>https://www.russ.cloud/2021/12/04/vscode-bicep-on-an-m1-mac/</link><guid isPermaLink="true">https://www.russ.cloud//2021/12/04/vscode-bicep-on-an-m1-mac/</guid><description>Learn how to run Bicep on an M1 Mac using VSCode, addressing the .Net 5.0 runtime issue with Rosetta 2 and Homebrew.</description><pubDate>Sat, 04 Dec 2021 16:26:55 GMT</pubDate><content:encoded>&lt;p&gt;Probably a little late to the party with this one, but I have recently upgraded to an M1 Pro MacBook Pro, for the most part, the problems with moving to arm64 are not as bad as I thought - most are annoying little things.&lt;/p&gt;
&lt;p&gt;However, the biggest one came when I decided to take a serious look at &lt;a href=&quot;/glossary/bicep/&quot;&gt;Bicep&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After installing the &lt;span&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-bicep&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Bicep VSCode extension &lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;and opening a test Bicep file, I was greeted with the following message …&lt;/p&gt;
&lt;div class=&quot;img-wrapper mb-1 flex justify-center&quot;&gt;&lt;div&gt;&lt;div class=&quot;lightgallery-component&quot;&gt;&lt;a href=&quot;/cdn-cgi/image/width=2048,quality=60,format=avif,fit=scale-down/assets/2021-12-04-vscode-bicep-on-an-m1-mac/images/01.png&quot;&gt;&lt;img src=&quot;/cdn-cgi/image/width=1200,quality=60,format=avif/assets/2021-12-04-vscode-bicep-on-an-m1-mac/images/01.png&quot; alt=&quot;The error&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1438&quot; height=&quot;793&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see, there are complaints about installing the .Net 5.0 runtime; this makes sense as there is no arm64 .Net 5.0 for macOS, .Net 6.0 will be fully supported - however, Bicep will not offer support for this until its 0.5 release, which is due at some point in January 2022 …&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://x.com/russmckendrick/status/1461124007441305601&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://x.com/russmckendrick/status/1461124007441305601&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;… so, how do we work around this?&lt;/p&gt;
&lt;p&gt;Apple provides &lt;span&gt;&lt;a href=&quot;https://support.apple.com/en-gb/HT211861&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Rosetta 2&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; to help you run Intel (or x86_64) binaries and applications on your M1 powered Mac - as I am a &lt;span&gt;&lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebrew&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; user, I thought it would be good to install the x86_64 version of .Net 5.0 using this.&lt;/p&gt;
&lt;p&gt;To do this, you will need to install the x86_64 of Homebrew itself which you can do by running the command below …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install the x86_64 version of Homebrew&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;arch&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-x86_64&lt;/span&gt;&lt;span&gt; /bin/bash &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-fsSL&lt;/span&gt;&lt;span&gt; https://raw.githubusercontent.com/Homebrew/install/master/install.sh&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;.. so far, so good - however, you now have two versions of Homebrew installed - this could get messy and confusing.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Open your `~/.zshrc` file and adding the following ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Add the Brow alias&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;alias brow=&apos;arch --x86_64 /usr/local/Homebrew/bin/brew&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;path=(&apos;/opt/homebrew/bin&apos; $path)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;export PATH&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When you open a fresh terminal session, you will have an alias called &lt;code&gt;brow&lt;/code&gt; which runs the x86_64 version of Homebrew which is found at &lt;code&gt;/usr/local/Homebrew/bin/brew&lt;/code&gt; and keep the &lt;code&gt;brew&lt;/code&gt; command as the arm64 version as &lt;code&gt;/opt/homebrew/bin/brew&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once installed, you can then invoke the x86_64 version of Homebrew and install .Net 5.0 using this command …&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install and unlink .Net 5.0&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brow&lt;/span&gt;&lt;span&gt; install dotnet&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brow&lt;/span&gt;&lt;span&gt; unlink dotnet&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;... you may have noticed that we have installed &amp;gt;net and then immediately unlinked it. We did this because we are now going to install the arm64 version of .Net 6.0, to do this run the following ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Install .Net 6.0&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew install --cask dotnet&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;So now we have two versions of .Net installed, we can test this by running the following command ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Testing .Net 6.0&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;dotnet --list-runtimes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Microsoft.NETCore.App 6.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Testing .Net 5.0&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/usr/local/opt/dotnet/libexec/dotnet --list-runtimes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Microsoft.AspNetCore.App 5.0.10 [/usr/local/Cellar/dotnet/5.0.207/libexec/shared/Microsoft.AspNetCore.App]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;As you can see from the output above, we are getting the expected versions returned, net we need to tell the Bicep VSCode extension to use the right version of .Net; to do this, enter the following in your settings ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;VSCode Bicep Settings&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;dotnetAcquisitionExtension.existingDotnetPath&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;extensionId&quot;: &quot;ms-azuretools.vscode-bicep&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;path&quot;: &quot;/usr/local/opt/dotnet/libexec/dotnet&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;... you can get to the settings page by press **Command + Shift + P** then searching for settings ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-12-04-vscode-bicep-on-an-m1-mac/images/02.png&quot; alt=&quot;The settings&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;... once the settings are in place, close your VSCode and reopen your Bicep file, and hey presto, the error is gone. The Bicep Language server has launched as expected, and you can start to add resources etc.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-12-04-vscode-bicep-on-an-m1-mac/images/03.png&quot; alt=&quot;All working&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;So what happens once Bicep update to version 0.5, which introduces support for .Net 6.0? Just delete the setting block we added and restart VSCode - this will remove the reference to the x86_64 version of .Net 5.0, and it will default to the arm64 version of .Net 6.0.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2021-12-04-vscode-bicep-on-an-m1-mac.BIsMcDRJ.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2021/12/04/vscode-bicep-on-an-m1-mac-og.png</blog:ogImage><blog:readingTime>4</blog:readingTime><category>macOS</category><category>DevOps</category><category>Azure</category><category>Infrastructure as Code</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Managing Python on macOS Monterey</title><link>https://www.russ.cloud/2021/10/30/managing-python-on-macos-monterey/</link><guid isPermaLink="true">https://www.russ.cloud//2021/10/30/managing-python-on-macos-monterey/</guid><description>Just installed macOS Monterey and Python on my MacBook Pro! Easy peasy with pyenv. Check out my experience!</description><pubDate>Sat, 30 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This week was not only the release of macOS Monterey, but it was also new Mac Book Pro week for a lot of people, including myself. Given that it was alot of effot to install &lt;a href=&quot;/glossary/python/&quot;&gt;Python&lt;/a&gt; on &lt;a href=&quot;/2021/01/10/managing-python-on-macos-big-sur/&quot;&gt;macOS Big Sur&lt;/a&gt; I was a little worried that it would take the same amount of effort.&lt;/p&gt;
&lt;p&gt;As I was starting a with a clean macOS Monterey installation and had already installed &lt;span&gt;&lt;a href=&quot;https://brew.sh&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebew&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; I just needed to run the following command to install &lt;span&gt;&lt;a href=&quot;https://github.com/pyenv/pyenv&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;pyenv&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install pyenv&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; install pyenv&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once installed, I ran the following to download and compile Python 3.10.0:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Install Python 3.10.0 using pyenv&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pyenv install 3.10.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pyenv global 3.10.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pyenv version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The last command should return something similar to the following output:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output of the command&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ pyenv version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;3.10.0 (set by /Users/russ.mckendrick/.pyenv/version)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now that Python is installed and we have set version 3.10.0 to the default Python install used by pyenv, we need to run the following command to ensure that our preferred version is used whenever we open a new terminal session.&lt;/p&gt;
&lt;aside&gt;&lt;div class=&quot;flex flex-col gap-1.5&quot;&gt;&lt;div class=&quot;callout-heading flex items-center gap-2&quot;&gt;&lt;span&gt;Info&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;callout-content not-prose text-base leading-relaxed&quot;&gt;&lt;p&gt;&lt;strong&gt;Please note:&lt;/strong&gt; The command below assumes that you are using &lt;span&gt;&lt;a href=&quot;https://zsh.sourceforge.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ZSH&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt;, if you are using a different shell please check the file path to your RC file is correct.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Make sure the pyenv version is used&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-e&lt;/span&gt;&lt;span&gt; $&apos;if command -v pyenv 1&amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then&lt;/span&gt;&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;n  export PYENV_ROOT=&quot;$HOME/.pyenv&quot;&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;n  export PATH=&quot;$PYENV_ROOT/bin:$PATH&quot;&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;n  eval &quot;$(pyenv init --path)&quot;&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;n  eval &quot;$(pyenv init -)&quot;&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;nfi&apos; &lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; ~/.zshrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Open a new shell and run:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Check the python version&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;python --version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;which python&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output of the command&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ python --version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Python 3.10.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ which python&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/Users/russ.mckendrick/.pyenv/shims/python&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The final step is to make sure that &lt;code&gt;pip&lt;/code&gt; is up-to-date, to do this run:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Update pip&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt;&lt;span&gt; install &lt;/span&gt;&lt;span&gt;--upgrade&lt;/span&gt;&lt;span&gt; pip&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Install Ansible&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` terminfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pip install --user ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible --version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;As you can see from the output below, Ansible was isntalled and using the correct version of Python:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output of the command&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;$ ansible --version&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ansible [core 2.11.6]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config file = None&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;configured module search path = [&apos;/Users/russ.mckendrick/.ansible/plugins/modules&apos;, &apos;/usr/share/ansible/plugins/modules&apos;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible python module location = /Users/russ.mckendrick/.local/lib/python3.10/site-packages/ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ansible collection location = /Users/russ.mckendrick/.ansible/collections:/usr/share/ansible/collections&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;executable location = /Users/russ.mckendrick/.local/bin/ansible&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;python version = 3.10.0 (default, Oct 26 2021, 20:43:52) [Clang 13.0.0 (clang-1300.0.29.3)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;jinja version = 3.0.2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;libyaml = True&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This was a massive improvement on last years experience as it was far from smooth on day one, so props to pyenv and Homebrew teams 👍&lt;/p&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2021-10-30-managing-python-on-macos-monterey.5oQXLvDl.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2021/10/30/managing-python-on-macos-monterey-og.png</blog:ogImage><blog:readingTime>3</blog:readingTime><category>macOS</category><category>Python</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item><item><title>Running Podman on macOS</title><link>https://www.russ.cloud/2021/09/12/running-podman-on-macos/</link><guid isPermaLink="true">https://www.russ.cloud//2021/09/12/running-podman-on-macos/</guid><description>Discover how to run Podman on macOS using a virtual machine, enabling Docker compatibility and container management.</description><pubDate>Sun, 12 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last weekend I posted about &lt;a href=&quot;/2021/09/05/docker-desktop-alternatives-for-macos/&quot;&gt;Docker Desktop Alternatives for macOS&lt;/a&gt;, and it looks like I missed one as the week before &lt;span&gt;&lt;a href=&quot;https://podman.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Podman&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; release version &lt;span&gt;&lt;a href=&quot;https://github.com/containers/podman/releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;v3.3.0&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; which introduced support for macOS by allowing you to launch a &lt;a href=&quot;/glossary/podman/&quot;&gt;Podman&lt;/a&gt; managed virtual machine.&lt;/p&gt;
&lt;p&gt;So, What is Podman?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Podman is a daemonless &lt;a href=&quot;/glossary/container/&quot;&gt;container&lt;/a&gt; engine for developing, managing, and running OCI Containers on your Linux System. Containers can either be run as root or in rootless mode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As it has been built to run and manage OCI compliant container images it means that any images want to run built using any other Container engine, like &lt;a href=&quot;/tags/docker/&quot;&gt;Docker&lt;/a&gt; or &lt;span&gt;&lt;a href=&quot;https://containerd.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;containerd&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; (which was covered in last weeks post using Lima) will also “just” work with Podman - to the point where the Podman developers are confident enough to claim that you add &lt;code&gt;alias docker=podman&lt;/code&gt; on your machine as running Podman is nearly indistinguishable from running Docker.&lt;/p&gt;
&lt;p&gt;Let’s put that to the test.&lt;/p&gt;
&lt;h2&gt;Installing Podman&lt;/h2&gt;
&lt;p&gt;As I am running macOS installing Podman using &lt;span&gt;&lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebrew&lt;/a&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/span&gt; is just a simple command:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Install Podman&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;brew&lt;/span&gt;&lt;span&gt; install podman&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Using Podman Machine&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once the `podman` client binaries are installed there isn&apos;t much you can do with it other than connect to remote instances of Podman, this is because macOS is not able to natively run containers so we need to download a Linux image to run a virtual machine, to do this simply run the following command:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Download the image&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman machine init&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;As you can see from the following output this downloads a custom Fedora image based on CoreOS:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Downloading VM image: fedora-coreos-34.20210904.1.0-qemu.x86_64.qcow2.xz: done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Extracting compressed file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Now that we have a copy of the image and the virtual machine has been created, we can now start the virtual machine by running:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Start the machine&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman machine start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This gives the following output, as you can see, there is a warning - but this release is hot off the press so we can forgive that:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;INFO[0000] waiting for clients...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;INFO[0000] listening tcp://0.0.0.0:7777&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Waiting for VM ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;INFO[0000] new connection from  to /var/folders/q8/dplpdx9n7lz1r3m282_vcz440000gn/T/podman/qemu_podman-machine-default.sock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;qemu-system-x86_64: warning: host doesn&apos;t support requested feature: CPUID.80000001H:ECX.svm [bit 2]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once the virtual machine is up and running you can run the following command to open a shell to it:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;SSH to the machine&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman machine ssh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Now we have a shell on the machine lets look at the operating system information:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Connecting to vm podman-machine-default. To close connection, use `~.` or `exit`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Warning: Permanently added &apos;[localhost]:50983&apos; (ECDSA) to the list of known hosts.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Fedora CoreOS 34.20210904.1.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Tracker: https://github.com/coreos/fedora-coreos-tracker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Discuss: https://discussion.fedoraproject.org/c/server/coreos/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[core@localhost ~]$ cat /etc/*release&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Fedora release 34 (Thirty Four)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;NAME=Fedora&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;VERSION=&quot;34.20210904.1.0 (CoreOS)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ID=fedora&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;VERSION_ID=34&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;VERSION_CODENAME=&quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PLATFORM_ID=&quot;platform:f34&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PRETTY_NAME=&quot;Fedora CoreOS 34.20210904.1.0&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ANSI_COLOR=&quot;0;38;2;60;110;180&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LOGO=fedora-logo-icon&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CPE_NAME=&quot;cpe:/o:fedoraproject:fedora:34&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;HOME_URL=&quot;https://getfedora.org/coreos/&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;DOCUMENTATION_URL=&quot;https://docs.fedoraproject.org/en-US/fedora-coreos/&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;SUPPORT_URL=&quot;https://github.com/coreos/fedora-coreos-tracker/&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;BUG_REPORT_URL=&quot;https://github.com/coreos/fedora-coreos-tracker/&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;REDHAT_BUGZILLA_PRODUCT=&quot;Fedora&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;REDHAT_BUGZILLA_PRODUCT_VERSION=34&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;REDHAT_SUPPORT_PRODUCT=&quot;Fedora&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;REDHAT_SUPPORT_PRODUCT_VERSION=34&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;PRIVACY_POLICY_URL=&quot;https://fedoraproject.org/wiki/Legal:PrivacyPolicy&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;VARIANT=&quot;CoreOS&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;VARIANT_ID=coreos&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;OSTREE_VERSION=&apos;34.20210904.1.0&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;DEFAULT_HOSTNAME=localhost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Fedora release 34 (Thirty Four)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Fedora release 34 (Thirty Four)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[core@localhost ~]$ exit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;logout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Connection to localhost closed.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;While you can open a shell to the machine it is not needed to run so let&apos;s run some containers using the `podman` client on our Mac.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Using Podman&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;So now we have everything up and running let&apos;s re-run the same command we used when [testing Lima in the last post](/2021/09/05/docker-desktop-alternatives-for-macos/#lima).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;### Hello World!!!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;First up is the `podman` equivalent of running `docker container run hello-world`, as we have already stated that you can simply replace Docker with Podman it will be no surprise that the command is:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Podman Hello World&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman container run hello-world&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;As expected, this connects straight do the [Docker Hub](https://hub.docker.com/_/hello-world), pulls and then runs the image:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Resolved &quot;hello-world&quot; as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Trying to pull docker.io/library/hello-world:latest...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Getting image source signatures&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Copying blob sha256:b8dfde127a2919ff59ad3fd4a0776de178a555a76fff77a506e128aea3ed41e3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Copying blob sha256:b8dfde127a2919ff59ad3fd4a0776de178a555a76fff77a506e128aea3ed41e3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Copying config sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Writing manifest to image destination&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Storing signatures&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Hello from Docker!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This message shows that your installation appears to be working correctly.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;To generate this message, Docker took the following steps:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1. The Docker client contacted the Docker daemon.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2. The Docker daemon pulled the &quot;hello-world&quot; image from the Docker Hub.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(amd64)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3. The Docker daemon created a new container from that image which runs the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;executable that produces the output you are currently reading.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4. The Docker daemon streamed that output to the Docker client, which sent it&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;to your terminal.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;To try something more ambitious, you can run an Ubuntu container with:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$ docker run -it ubuntu bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Share images, automate workflows, and more with a free Docker ID:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;https://hub.docker.com/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;For more examples and ideas, visit:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;https://docs.docker.com/get-started/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;So far, so good.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;### Building and running an Image&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Next, lets take the Dockerfile we built last time and try building it using Podman, as a reminder the Dockerfile looks like the following:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Build the image&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` dockerfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;FROM alpine:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LABEL maintainer=&quot;Russ McKendrick &amp;lt;russ@mckendrick.io&amp;gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LABEL description=&quot;This example Dockerfile installs NGINX.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;RUN apk add --update nginx &amp;amp;&amp;amp; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rm -rf /var/cache/apk/* &amp;amp;&amp;amp; \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mkdir -p /tmp/nginx/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;COPY files/nginx.conf /etc/nginx/nginx.conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;COPY files/default.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ADD files/html.tar.gz /usr/share/nginx/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;EXPOSE 80/tcp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ENTRYPOINT [&quot;nginx&quot;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CMD [&quot;-g&quot;, &quot;daemon off;&quot;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;To build the image you will [need to checkout this repo](https://github.com/russmckendrick/lima-dockerfile-example) and run the build command from the folder containing the `Dockerfile`. The command to build and tag the image is no different to the one which we would use in Docker:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Build the image&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman image build --tag local:podman-example .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The output of the build is below:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 1/10: FROM alpine:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Resolved &quot;alpine&quot; as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Trying to pull docker.io/library/alpine:latest...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Getting image source signatures&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Copying blob sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Copying blob sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Copying config sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Writing manifest to image destination&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Storing signatures&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 2/10: LABEL maintainer=&quot;Russ McKendrick &amp;lt;russ@mckendrick.io&amp;gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; 9e7e28e77e9&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 3/10: LABEL description=&quot;This example Dockerfile installs NGINX.&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; aa67eedf7b2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 4/10: RUN apk add --update nginx &amp;amp;&amp;amp;         rm -rf /var/cache/apk/* &amp;amp;&amp;amp;         mkdir -p /tmp/nginx/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;(1/2) Installing pcre (8.44-r0)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;(2/2) Installing nginx (1.20.1-r3)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Executing nginx-1.20.1-r3.pre-install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Executing nginx-1.20.1-r3.post-install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Executing busybox-1.33.1-r3.trigger&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;OK: 7 MiB in 16 packages&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; 6a61a4653e9&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 5/10: COPY files/nginx.conf /etc/nginx/nginx.conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; f3a1faa6b75&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 6/10: COPY files/default.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; 4a57c732f37&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 7/10: ADD files/html.tar.gz /usr/share/nginx/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; 2180b8083f4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 8/10: EXPOSE 80/tcp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; fe304c0d38d&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 9/10: ENTRYPOINT [&quot;nginx&quot;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; 4dda02ea2b7&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;STEP 10/10: CMD [&quot;-g&quot;, &quot;daemon off;&quot;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;COMMIT local:podman-example&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;--&amp;gt; 88a4d1cbc6d&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Successfully tagged localhost/local:podman-example&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;88a4d1cbc6dcf92cd4662da990e80125ad062c0ac99551e38c0a2e8eee9231f9&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once built you can use the following command to run the image:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Running the image&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman container run -d --name podman-example -p 8080:80 --network bridge local:podman-example&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once running, open a browser and goto **http://localhost:8080/** and you should be greeted by:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-09-12-podman-on-macos/images/01.png&quot; alt=&quot;You know the rules and so do I&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;InfoCallout title=&quot;Info&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Now the keen eyed amongst you may have noticed that the command to launch the container is a little slightly different than want we used in the last post, at the time of writing *--network bridge* is needed to map port **8080** from the virtual machine to **127.0.0.1** on the mac. The next release which is due in the next fortnight should resolve this.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/InfoCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;InfoCallout title=&quot;Info&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;**Update 10/10/2021** the update has been release and it works as expected, so you can remove the *--network bridge* from the command.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/InfoCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;### Other commands&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;From as you can see from the terminals below, other commands you would just run in Docker to manage your containers just work:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Listing the images&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman image ls&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;REPOSITORY                     TAG             IMAGE ID      CREATED         SIZE&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;localhost/local                podman-example  52add0470325  15 minutes ago  7.57 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker.io/library/alpine       latest          14119a10abf4  2 weeks ago     5.87 MB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;docker.io/library/hello-world  latest          d1165f221234  6 months ago    20.4 kB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Listing all the containers&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman container ls -a&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CONTAINER ID  IMAGE                                 COMMAND         CREATED         STATUS                     PORTS                 NAMES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;79e3825f608e  docker.io/library/hello-world:latest  /hello          37 minutes ago  Exited (0) 37 minutes ago                        lucid_chandrasekhar&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;8fd9aaa8febe  localhost/local:podman-example        -g daemon off;  9 seconds ago   Up 9 seconds ago           0.0.0.0:8080-&amp;gt;80/tcp  podman-example&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Stopping a container&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman container stop podman-example&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;ERRO[3081] accept tcp [::]:8080: use of closed network connection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman-example&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Removing a container&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman container rm podman-example&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Pruning the containers&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman container prune&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WARNING! This will remove all non running containers.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Are you sure you want to continue? [y/N] y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;79e3825f608e5f4a0ce8dd03849ecb55d5ff03eeff063a52c9e053bcde0460fc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Pruning the images&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman image prune&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WARNING! This will remove all dangling images.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Are you sure you want to continue? [y/N] y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Tidying up Podman&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once you have finished having a play with Podman you will need to stop the virtual machine, as you may have already guessed, to do this run the following command:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Stopping the machine&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman machine stop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once stopped, you can start it back up again - order to remove the Virtual Machine altogether run:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Stopping the machine&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` awk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;podman machine rm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This will give the following prompt:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;WarningCallout title=&quot;Warning&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;This will delete the virtual machine and everything on it, please make sure you really to do this before agreeing.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;/WarningCallout&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;```bash frame=&quot;terminal&quot; title=&quot;Output&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;``` plaintext&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The following files will be deleted:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/Users/russ.mckendrick/.ssh/podman-machine-default&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/Users/russ.mckendrick/.ssh/podman-machine-default.pub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/Users/russ.mckendrick/.config/containers/podman/machine/qemu/podman-machine-default.ign&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/Users/russ.mckendrick/.local/share/containers/podman/machine/qemu/podman-machine-default_fedora-coreos-34.20210904.1.0-qemu.x86_64.qcow2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/Users/russ.mckendrick/.config/containers/podman/machine/qemu/podman-machine-default.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Are you sure you want to continue? [y/N]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Once you have delete the virtual machine you will need to run `podman machine init` to recreate it again.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Bonus Desktop App&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;While currently there is not an official native Desktop app for Podman on macOS, Victor Gama has coded and released one which can be downloaded from [heyvito/podman-macos](https://github.com/heyvito/podman-macos), the screens below show the application in action ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-09-12-podman-on-macos/images/02.png&quot; alt=&quot;Installing&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-09-12-podman-on-macos/images/03.png&quot; alt=&quot;Welcome to Podman&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-09-12-podman-on-macos/images/04.png&quot; alt=&quot;Preparing ...&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-09-12-podman-on-macos/images/05.png&quot; alt=&quot;About ...&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-09-12-podman-on-macos/images/06.png&quot; alt=&quot;Checking out running containers&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&amp;lt;Img src=&quot;/assets/2021-09-12-podman-on-macos/images/07.png&quot; alt=&quot;Interacting with running containers&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;... at the time of writing of writing the application is less than a week or so old so I recommend keeping an eye on the repo for updates.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;## Summary&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Podman has long been used on Linux machines, and the developers (RedHat) have been working on the macOS integration long before Docker announced their changes to the licensing model for Docker Desktop.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;There are however some bugs, like the one mentioned, and also some functionality which needs to be in place - the biggest of which is the at the time of writing there is no native way to mount the filesystem from the host (macOS) into the virtual machine (Podman) which could be a deal breaker from some developers as this could block them from easily developing locally. However, the good thing is the Podman developers know all of this and are working hard to add the functionality on top of their already solid base.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded><blog:coverImage>https://www.russ.cloud/cdn-cgi/image/width=1600,quality=80,format=jpeg,fit=scale-down/_astro/blog-cover-2021-09-12-podman-on-macos.C3z2Kuu5.png</blog:coverImage><blog:ogImage>https://www.russ.cloud/2021/09/12/running-podman-on-macos-og.png</blog:ogImage><blog:readingTime>10</blog:readingTime><category>Docker</category><category>macOS</category><category>Podman</category><category>Containers</category><author>web.site@mckendrick.email (Russ McKendrick)</author></item></channel></rss>