<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Anton Babenko</title><description>Open-source, Terraform/AWS, compliance, serverless, AI agents, cloudy business, and other notes.</description><link>https://antonbabenko.com/</link><language>en</language><item><title>Deliberation Dashboard: see and analyze your consensus results</title><link>https://antonbabenko.com/deliberation-dashboard-see-and-analyze-your-consensus-results/</link><guid isPermaLink="true">https://antonbabenko.com/deliberation-dashboard-see-and-analyze-your-consensus-results/</guid><description>A local dashboard that draws every Deliberation run while it happens, an analyzer that says which model to drop and why, and a record of which config each run used.</description><pubDate>Thu, 08 Oct 2026 18:53:08 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I use Deliberation heavily every day, in the harnesses I picked for my own work: Claude Code, Codex and Antigravity, running in &lt;a href=&quot;https://t3.codes&quot;&gt;T3 Code&lt;/a&gt; and Claude Code on the web at the same time. It has been tested in real work much harder than the version I wrote about in June.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;In June I wrote that every model on a panel has to prove it earns its seat, and I shipped a debug log and an &lt;code&gt;analyze&lt;/code&gt; tool to do the proving. Both worked. The log on my laptop is 2.4 MB of JSONL today. Measuring was the easy part. A number I have to go and dig for is a number I do not act on.&lt;/p&gt;
&lt;p&gt;So most of the work since the &lt;a href=&quot;https://antonbabenko.com/meet-deliberation-400-models-is-easy-knowing-which-ones-earn-a-place-is-hard/&quot;&gt;June post&lt;/a&gt; went into making the panel visible: a local dashboard that draws every run while it happens, an analyzer that says which model to drop and why, and a record of which config each run used. Around that, a lot of smaller fixes for the ways a multi-model run goes wrong when nobody is watching. Deliberation went from v3.5.2 to v3.28.0 in that time.&lt;/p&gt;
&lt;p&gt;If you are new here: Deliberation is an open-source MCP server that lets the agent you already use (Claude Code, Codex, Cursor, Kiro, Antigravity, OpenCode) ask GPT, Gemini, Grok and any OpenRouter model for a second opinion, or make them argue until they agree. The June post explains the consensus loop. This one is about watching it.&lt;/p&gt;
&lt;p&gt;Install in Claude Code is still three lines:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;https://antonbabenko.com/_astro/ec.wshpv.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://antonbabenko.com/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;/plugin marketplace add antonbabenko/agent-plugins&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;/plugin install deliberation@antonbabenko&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;/deliberation: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 aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;/plugin marketplace add antonbabenko/agent-plugins/plugin install deliberation@antonbabenko/deliberation:setup&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Other hosts have their own guides in &lt;a href=&quot;https://github.com/antonbabenko/deliberation/tree/master/docs/hosts&quot;&gt;docs/hosts&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;you-cannot-fix-a-panel-you-cannot-see&quot;&gt;You cannot fix a panel you cannot see&lt;/h2&gt;
&lt;p&gt;Turn it on with one line in &lt;code&gt;~/.config/deliberation/config.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&gt;&quot;dashboard&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;--0:#7EE787&quot;&gt;&quot;enabled&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&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 aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;&amp;quot;dashboard&amp;quot;: { &amp;quot;enabled&amp;quot;: true }&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then run &lt;code&gt;/deliberation:dashboard&lt;/code&gt; (or &lt;code&gt;deliberation-mcp dashboard&lt;/code&gt; in a terminal). It prints a URL on &lt;code&gt;127.0.0.1&lt;/code&gt; and opens it. There are five tabs: Live, Runs, Config, Stats and Analyzer. I generated most of the dashboard UI in a single shot with &lt;a href=&quot;https://impeccable.style/&quot;&gt;Impeccable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Runs tab filtered to consensus runs&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/deliberation-dashboard-see-and-analyze-your-consensus-results/1-runs.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The Runs tab is the history. Every &lt;code&gt;/consensus&lt;/code&gt;, &lt;code&gt;/ask-all&lt;/code&gt; and &lt;code&gt;/ask-*&lt;/code&gt; call is one row with its status, the project it came from, the providers, rounds, tokens and wall time. You can filter by project, tool, provider, status and date. The yellow UNRESOLVED rows are the panel saying it could not agree in five rounds.&lt;/p&gt;
&lt;p&gt;Click a run and you get the part I wanted from the start:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;One consensus run: lanes per model, debate trajectory, provider latency&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/deliberation-dashboard-see-and-analyze-your-consensus-results/2-run-detail.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is a real &lt;code&gt;consensus-step&lt;/code&gt; run on the Deliberation repo itself, with Claude as the arbiter and five reviewers. Each model gets a lane, and the bars show when it was thinking and what it voted. The trajectory below tells the story of the run in three lines. Round 1: Codex and Grok asked for changes. Round 2: only Codex still objected. Round 3: all five approved, 21 minutes 46 seconds and 330k tokens after the start.&lt;/p&gt;
&lt;p&gt;The latency table under it is where the panel stops being abstract. Grok spent 14 minutes 04 seconds in total over three calls. Kimi K3 spent 1 minute 20 seconds and voted the same way every round. In a parallel fan-out the slowest member sets the clock, so that table is effectively a bill.&lt;/p&gt;
&lt;p&gt;Live shows the same graph while a run is still going, so you can see which lane a slow run is waiting on.&lt;/p&gt;
&lt;p&gt;The dashboard is read-only and it is built to stay on the machine. It listens on loopback only, accepts GET requests only, and the URL carries a token that becomes a cookie for that one browser. By default it records metadata only, without prompts or responses. If you switch &lt;code&gt;dashboard.capture&lt;/code&gt; to &lt;code&gt;content&lt;/code&gt;, secrets are scrubbed before anything is written and PII is redacted when a page is served, unless you turn on &lt;code&gt;dashboard.showPII&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-analyzer-tells-me-which-model-to-drop&quot;&gt;The analyzer tells me which model to drop&lt;/h2&gt;
&lt;p&gt;The June version of &lt;code&gt;analyze&lt;/code&gt; was a command that printed a report. It is still there (&lt;code&gt;/deliberation:analyze&lt;/code&gt;, now with a &lt;code&gt;since&lt;/code&gt; window and only the models you have configured), but the useful version is the Analyzer tab.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Analyzer: agreement vs findings, projects, request size vs latency&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/deliberation-dashboard-see-and-analyze-your-consensus-results/3-analyzer.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The first table answers the question from the June post: which model agrees more than it adds. It reads only consensus rounds, since those are the ones with verdicts. &quot;Adds nothing&quot; means every issue category the model raised in a round was also raised by another voice. A model becomes a drop candidate only when all of these hold: at least 10 rounds, adds nothing in 80% or more of them, is the lone objector in 10% or fewer, rarely raises an issue the arbiter accepts, and is either slow or errors a lot.&lt;/p&gt;
&lt;p&gt;My own numbers over the last 30 days, 388 runs: Gemini sat in 273 rounds, added nothing in 95% of them and was slower than the other responders&apos; median every time. Grok added nothing in 87%. Codex looks worse on paper at 85%, but it has the highest lone-dissent rate on the panel (15%), and the arbiter accepted its issue in all 6 rounds where a decision was recorded. The lone objector who is usually right is exactly the model you keep, and the table says &quot;keep&quot;.&lt;/p&gt;
&lt;p&gt;Gemini and Grok are marked UNCONFIRMED, with the config line that would turn them off. They are still on in my config. Six rounds with arbiter decisions is too few for me to trust the verdict, and the analyzer says the same thing by not calling them drop candidates yet.&lt;/p&gt;
&lt;p&gt;The second table groups runs by project (by git remote, so every clone and worktree of a repo lands on one row) with runs, calls, timeouts, errors, p50 and p95. The third one plots request size against latency per model. For Codex on text-only prompts the fit is about 5.5 seconds per thousand characters, and the advice column refuses to suggest a timeout until a bucket has 20 calls. For Kimi K3 with files, 1 of 9 calls timed out, and it already points at &lt;code&gt;models.kimi-k3.timeout&lt;/code&gt; as probably too low.&lt;/p&gt;
&lt;p&gt;To get these numbers, every call now records its request size, the attached file bytes, the timeout it was actually granted and which setting granted it, and every run records the project it came from.&lt;/p&gt;
&lt;h2 id=&quot;every-run-remembers-the-config-it-ran-with&quot;&gt;Every run remembers the config it ran with&lt;/h2&gt;
&lt;p&gt;In June, runs made under different configs all landed in the same log, with nothing saying which config produced which number. Now each run keeps a snapshot of the config it ran with, and the Stats tab compares configs side by side.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Stats: configuration comparison&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/deliberation-dashboard-see-and-analyze-your-consensus-results/4-stats.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Two rows from my own history: config &lt;code&gt;8e33794253ec&lt;/code&gt; had 68 runs, 23 errors and a p95 of 25 minutes 37 seconds. Config &lt;code&gt;dd82d2de6943&lt;/code&gt;, the one the run above used, has 20 runs, 1 error and a p95 of 7 minutes 14 seconds. I would not call that a controlled experiment, but it is the first time I could see that a config change did something.&lt;/p&gt;
&lt;p&gt;This works because more of the config is pinned in one file. GPT&apos;s model and reasoning effort (&lt;code&gt;providers.codex.model&lt;/code&gt;, &lt;code&gt;providers.codex.reasoningEffort&lt;/code&gt;), the Gemini and Grok models, a default OpenRouter model, per-provider timeouts and a separate reasoning effort for consensus rounds (&lt;code&gt;providers.&amp;lt;name&amp;gt;.consensusReasoningEffort&lt;/code&gt;) all live in &lt;code&gt;config.json&lt;/code&gt;. The Config tab shows the effective values and where each one came from.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Config: provider health&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/deliberation-dashboard-see-and-analyze-your-consensus-results/5-config.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;slow-is-fine-unbounded-is-not&quot;&gt;Slow is fine, unbounded is not&lt;/h2&gt;
&lt;p&gt;The June post said a full consensus loop can take minutes and that is fine for the right job. That held until one &lt;code&gt;/consensus&lt;/code&gt; run took more than 45 minutes, because a single Codex call inside it ran for about 38. Nothing was broken. It was just waiting.&lt;/p&gt;
&lt;p&gt;Since then a run has limits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;consensus.maxWallMs&lt;/code&gt; is a time budget for the whole run (default 30 minutes), and a peer that times out twice in a row is dropped from the remaining rounds.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;consensus.quorumFloor&lt;/code&gt; (default 2) means a run cannot converge when too few reviewers answered. Two models dropping out no longer leaves one model &quot;agreeing&quot; with the arbiter.&lt;/li&gt;
&lt;li&gt;Real disagreement resets the error streak, so a model that keeps objecting is never cut by the circuit breaker that is meant for broken providers.&lt;/li&gt;
&lt;li&gt;On rounds with dissent, the arbiter&apos;s adjudication and revision overlap instead of running one after another.&lt;/li&gt;
&lt;li&gt;Later rounds get a digest of earlier ones instead of the full history, and static files go first in the prompt so OpenRouter can cache them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The other half is about calls that look like answers and are not. A 429 is retried once, honoring &lt;code&gt;Retry-After&lt;/code&gt;. A reply like &quot;I will begin by reviewing the files...&quot; counts as a failure instead of a vote. One broken provider no longer costs the whole round. And timeouts are per provider now, after I watched a DeepSeek call succeed at 250 seconds while its three siblings were killed at 180.&lt;/p&gt;
&lt;h2 id=&quot;models-argue-about-today-not-their-training-cutoff&quot;&gt;Models argue about today, not their training cutoff&lt;/h2&gt;
&lt;p&gt;Every delegate prompt now starts with today&apos;s UTC date and one rule: when a model does not recognize something (a new model name, a recent release, a CLI flag), it marks the claim &lt;code&gt;[unverified]&lt;/code&gt; instead of saying it does not exist.&lt;/p&gt;
&lt;h2 id=&quot;smaller-things-that-removed-friction&quot;&gt;Smaller things that removed friction&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Claude Code on the web works: every call fits under the host&apos;s 60-second tool-call cap, and the plugin declares a 30-minute per-server timeout where the host allows it.&lt;/li&gt;
&lt;li&gt;Codex login is a device login (&lt;code&gt;/deliberation:codex-login&lt;/code&gt;): it shows a link and a code in about a second, prefers your ChatGPT login and never uses &lt;code&gt;OPENAI_API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Antigravity is a native host, and Codex and Gemini CLIs start on Windows.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/deliberation:help&lt;/code&gt; shows real prompts you can paste, &lt;code&gt;/deliberation:doctor&lt;/code&gt; checks config, CLIs and paths without changing anything, and &lt;code&gt;/deliberation:reload-mcp&lt;/code&gt; cycles the server after an update.&lt;/li&gt;
&lt;li&gt;Read-only is enforced for advisory calls: Codex runs with &lt;code&gt;--sandbox read-only&lt;/code&gt;, Gemini under a read-only sandbox.&lt;/li&gt;
&lt;li&gt;Secret scrubbing understands syntax now: private key blocks, JWTs, API key formats and assignments in JSON, shell and YAML are removed before anything is written.&lt;/li&gt;
&lt;li&gt;Grok and OpenRouter cannot read your repo, so Deliberation can attach a small orientation bundle (&lt;code&gt;orientation.enabled&lt;/code&gt;). It has a byte budget now (&lt;code&gt;orientation.maxBytes&lt;/code&gt;, 16 KB by default), which cut that part of each call from about 26K tokens to about 4K in my runs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-i-would-still-not-trust-it-with&quot;&gt;What I would still not trust it with&lt;/h2&gt;
&lt;p&gt;The dashboard is for the machine it runs on. There is no hosted version and no team view.&lt;/p&gt;
&lt;p&gt;The analyzer works on coarse issue categories and needs volume. With a few dozen consensus rounds it will mostly say UNCONFIRMED, and that is the correct answer. Treat it as advice, as the page itself says.&lt;/p&gt;
&lt;p&gt;And agreement is still not truth. The dashboard makes it much easier to see when five models agree. It does not make them right.&lt;/p&gt;
&lt;h2 id=&quot;try-it&quot;&gt;Try it&lt;/h2&gt;
&lt;p&gt;Update the plugin, add &lt;code&gt;&quot;dashboard&quot;: { &quot;enabled&quot;: true }&lt;/code&gt; to your config, run a couple of &lt;code&gt;/ask-all&lt;/code&gt; or &lt;code&gt;/consensus&lt;/code&gt; calls, then &lt;code&gt;/deliberation:dashboard&lt;/code&gt;. The Analyzer tab gets useful after a week of real runs.&lt;/p&gt;
&lt;p&gt;Source, issues and the star button are at &lt;a href=&quot;https://github.com/antonbabenko/deliberation&quot;&gt;github.com/antonbabenko/deliberation&lt;/a&gt;. I want to hear the feedback (good and bad), especially from anyone running a panel I have not tried.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Also published on &lt;a href=&quot;https://dev.to/aws-heroes/deliberation-dashboard-see-and-analyze-your-consensus-results-ejf&quot;&gt;dev.to&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>ai</category><category>opensource</category><category>automation</category><category>productivity</category></item><item><title>Meet Deliberation: 400+ models is easy, knowing which ones earn a place is hard.</title><link>https://antonbabenko.com/meet-deliberation-400-models-is-easy-knowing-which-ones-earn-a-place-is-hard/</link><guid isPermaLink="true">https://antonbabenko.com/meet-deliberation-400-models-is-easy-knowing-which-ones-earn-a-place-is-hard/</guid><description>How a three-model consensus tool grew into a configurable, measurable panel - and why I now make every model prove it pays for its slot.</description><pubDate>Wed, 03 Jun 2026 08:54:04 GMT</pubDate><content:encoded>&lt;p&gt;It is open source and ready to use today: &lt;strong&gt;&lt;a href=&quot;https://github.com/antonbabenko/deliberation&quot;&gt;github.com/antonbabenko/deliberation&lt;/a&gt;&lt;/strong&gt;. If you only do one thing, star the repo and install it in your own agent - it takes about two minutes, and the install section below has the exact command for Claude Code, Codex, Cursor, Kiro, and OpenCode.&lt;/p&gt;
&lt;p&gt;Here is the part that surprises people: you are probably already paying to access a few models. A ChatGPT subscription includes Codex which allows you to use models like &lt;code&gt;gpt-5.5&lt;/code&gt; and &lt;code&gt;gpt-5.3-codex&lt;/code&gt; from Codex CLI. A Claude subscription includes Claude Code. So you can wire GPT and Claude to review each other right now, at no extra cost, and add Gemini, Grok, or any OpenRouter model when you want a third opinion.&lt;/p&gt;
&lt;p&gt;A few weeks ago I wrote that one model is a guess and three that agree is a plan. I still believe it. But it skipped the next problem, the one I hit the moment the trick worked: once you can ask three models, you can ask thirty seven. And more voices is not the same as more signal. A slow model that always agrees with a faster one is not a second opinion. It is a bill.&lt;/p&gt;
&lt;p&gt;So the project grew up, and it got a name: &lt;strong&gt;Deliberation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The word fits. Deliberation is the slow, careful weighing of a decision - the thing a jury or a council does before it returns a verdict. That is exactly the job here: a panel of models that weighs one artifact and argues its way to a call, instead of one model blurting the first thing that sounds right. It answers a different question than the first post did. The first post asked &lt;em&gt;should you make models argue?&lt;/em&gt; This one is about &lt;em&gt;which models, under what rules, and how do you know any of it was worth the wait?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It still runs the everyday work of &lt;a href=&quot;https://compliance.tf&quot;&gt;compliance.tf&lt;/a&gt;, same as before. Most of what follows came from that: not from theory, from watching real runs and asking why one took four minutes to tell me what another said in twenty seconds.&lt;/p&gt;
&lt;h2 id=&quot;a-few-words-first&quot;&gt;A few words first&lt;/h2&gt;
&lt;p&gt;If you are new to this, here are the only terms you need. Plain version, with examples.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Agent host.&lt;/strong&gt; The tool you already code in: Claude Code, the Codex CLI, Cursor, Kiro, OpenCode. Deliberation plugs into all of them and behaves the same way in each.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP.&lt;/strong&gt; A standard plug that lets your agent host talk to outside tools. Deliberation is one MCP server, so any host that speaks MCP can use it. You install it once; you do not write glue code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provider / model.&lt;/strong&gt; A provider is a company (OpenAI, Google, xAI, OpenRouter). A model is one brain from that provider (GPT, Gemini, Grok, or any of OpenRouter&apos;s 400-plus, like Qwen, DeepSeek, or Kimi).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Panel.&lt;/strong&gt; The set of models you ask at once. You choose who is on it. Example: a panel of GPT + Gemini + Grok.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expert (or persona).&lt;/strong&gt; A hat a model wears for one job: Architect, Security Analyst, Code Reviewer, Plan Reviewer, and three more. The same model reviews differently depending on the hat.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Arbiter.&lt;/strong&gt; The one who reads everybody&apos;s answers and makes the call. It can be a model, or it can be your own agent (you).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is the whole vocabulary. The rest is just rules for how the panel talks.&lt;/p&gt;
&lt;h2 id=&quot;install-it-now&quot;&gt;Install it now&lt;/h2&gt;
&lt;p&gt;Deliberation is one MCP server with native packaging for each host. Set only the providers you use - a missing key just turns that one provider off. Full per-host guides: &lt;strong&gt;&lt;a href=&quot;https://github.com/antonbabenko/deliberation/tree/master/public-docs/hosts&quot;&gt;public-docs/hosts&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude Code:&lt;/strong&gt; add the marketplace, install the plugin, run setup.
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;https://antonbabenko.com/_astro/ec.wshpv.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://antonbabenko.com/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame is-terminal&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;span class=&quot;title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sr-only&quot;&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;/plugin&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&gt;marketplace&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&gt;antonbabenko/agent-plugins&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;/plugin&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&gt;install&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&gt;deliberation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;/deliberation: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 aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;/plugin marketplace add antonbabenko/agent-plugins/plugin install deliberation/deliberation:setup&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codex CLI:&lt;/strong&gt; &lt;code&gt;codex plugin marketplace add antonbabenko/deliberation&lt;/code&gt;, then install &lt;strong&gt;deliberation&lt;/strong&gt; from &lt;code&gt;/plugins&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cursor:&lt;/strong&gt; drop in the rule file and use the one-click MCP deeplink (&lt;a href=&quot;https://github.com/antonbabenko/deliberation/blob/master/public-docs/hosts/cursor.md&quot;&gt;guide&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kiro:&lt;/strong&gt; &quot;Import power from GitHub&quot; (&lt;a href=&quot;https://github.com/antonbabenko/deliberation/blob/master/public-docs/hosts/kiro.md&quot;&gt;guide&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenCode:&lt;/strong&gt; add the &lt;code&gt;opencode.json&lt;/code&gt; MCP snippet (&lt;a href=&quot;https://github.com/antonbabenko/deliberation/blob/master/public-docs/hosts/opencode.md&quot;&gt;guide&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Credentials come from your environment: GPT uses your Codex/OpenAI login (run &lt;code&gt;codex login&lt;/code&gt;), Gemini signs in once through its new Antigravity CLI (run &lt;code&gt;agy&lt;/code&gt;), Grok reads &lt;code&gt;XAI_API_KEY&lt;/code&gt;, OpenRouter reads &lt;code&gt;OPENROUTER_API_KEY&lt;/code&gt;. Start with GPT and Claude, since you likely already pay for both.&lt;/p&gt;
&lt;h2 id=&quot;what-actually-changed&quot;&gt;What actually changed&lt;/h2&gt;
&lt;p&gt;Three things.&lt;/p&gt;
&lt;p&gt;It is one MCP server now, not a Claude-only plugin. It works in Claude Code, Cursor, Codex, Kiro, OpenCode - anything that speaks MCP. Your primary agent stays in charge and calls the panel when a decision is worth a second look.&lt;/p&gt;
&lt;p&gt;The panel opened up. It used to be three fixed externals: GPT, Gemini, Grok. Those are still the built-in voices, but you can now add any of OpenRouter&apos;s 400-plus models (including Qwen, DeepSeek, Kimi, and others) as named records in a config file. You pick them. You say which ones join a quick fan-out, which ones sit on the consensus panel, and which expert hat each one is allowed to wear - not all are equally good for all tasks.&lt;/p&gt;
&lt;p&gt;And the whole thing learned to &lt;strong&gt;measure itself&lt;/strong&gt;. That last part is the real subject of this post.&lt;/p&gt;
&lt;p&gt;I will walk through it as five categories. Each one has a switch you flip in config, and each one votes - or refuses to - in a specific way.&lt;/p&gt;
&lt;h2 id=&quot;quick-one-offs-just-ask&quot;&gt;Quick one-offs: just ask&lt;/h2&gt;
&lt;p&gt;Before the heavy machinery, the everyday move. Most of the time you do not want a five-round debate - you want a fast second opinion from someone who is not the model you have been talking to all session.&lt;/p&gt;
&lt;p&gt;So you just ask, in plain words: &quot;Ask Grok and Gemini whether this retry loop can deadlock.&quot; Your agent routes that to the right voices and brings back the answers. Or use the explicit commands when you know who you want:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;/ask-gpt does this IAM policy grant more than it should?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;/ask-grok poke holes in this rollback 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;span style=&quot;--0:#e6edf3&quot;&gt;/ask-gemini summarize the risk in this migration in 5 bullets&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;/ask-gpt does this IAM policy grant more than it should?/ask-grok poke holes in this rollback plan/ask-gemini summarize the risk in this migration in 5 bullets&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;These are single-shot and advisory: one question, one answer, no loop, no context contamination from your session. The exact same commands work in Claude Code, Codex, Cursor, Kiro, and OpenCode - the server is the same everywhere, so a prompt you learn in one host transfers to the rest unchanged.&lt;/p&gt;
&lt;p&gt;And none of this is a separate ritual you have to schedule. Call &lt;code&gt;/ask-all&lt;/code&gt; or &lt;code&gt;/consensus&lt;/code&gt; at any point in the work - while you are still scoping a feature, halfway through writing a plan, or in the middle of a &lt;a href=&quot;https://github.com/mattpocock/skills/blob/main/skills/productivity/grill-me/SKILL.md&quot;&gt;&lt;code&gt;/grill-me&lt;/code&gt;&lt;/a&gt; session when you want a real outside voice in the room instead of arguing with only yourself. The panel is available the whole time, not just at the review at the end. The earlier you pull in a dissenting model, the cheaper the disagreement is to act on.&lt;/p&gt;
&lt;h2 id=&quot;1-fan-out-with-no-cross-talk&quot;&gt;1. Fan-out with no cross-talk&lt;/h2&gt;
&lt;p&gt;The next cheapest move: ask several models the same question at once and read the answers side by side. Nobody sees anyone else&apos;s reply, so you get real independence instead of an echo.&lt;/p&gt;
&lt;p&gt;Two examples where this works the best:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;/ask-all can this Terraform state migration be rolled back safely, and what breaks if it cannot?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;/ask-all what are the failure modes of switching this table&apos;s primary key on a live database?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;/ask-all can this Terraform state migration be rolled back safely, and what breaks if it cannot?/ask-all what are the failure modes of switching this table&amp;#x27;s primary key on a live database?&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;/ask-all&lt;/code&gt; runs the parallel version. &lt;code&gt;panel&lt;/code&gt; tells you exactly who &lt;em&gt;would&lt;/em&gt; answer for your current config before you spend a token. &lt;code&gt;ask-one&lt;/code&gt; lets you fire each member yourself, so each answer lands on screen as it finishes instead of waiting on the slowest one.&lt;/p&gt;
&lt;p&gt;Switches: &lt;code&gt;routing.maxFanout&lt;/code&gt; caps how many OpenRouter models join (default 3), and each model record has an &lt;code&gt;askAll&lt;/code&gt; flag to opt in or out.&lt;/p&gt;
&lt;p&gt;There is no voting here. It is parallel sampling. You and your agent (Claude Code, Codex, or anything else) get all the answers and decide.&lt;/p&gt;
&lt;h2 id=&quot;2-the-consensus-loop-where-the-voting-lives&quot;&gt;2. The consensus loop, where the voting lives&lt;/h2&gt;
&lt;p&gt;This is the heart of it, and it is a state machine, not a vibe. One round goes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Blind pre-commit.&lt;/strong&gt; The arbiter writes down its own verdict - approve, request changes, or reject - before any other model sees the work. In writing, first. So its judgment cannot quietly drift to match the crowd later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parallel peer review.&lt;/strong&gt; The panel reviews the same artifact, each in a fresh thread, none seeing another&apos;s review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blind cross-review.&lt;/strong&gt; Each model then rates the others&apos; answers with names stripped off (to avoid bias and deference). A &quot;not viable&quot; vote becomes a candidate problem the arbiter has to deal with. This catches the case where everyone looked like they agreed but were each walking past the same hole. (Pattern borrowed from &lt;a href=&quot;https://github.com/karpathy/llm-council&quot;&gt;karpathy/llm-council&lt;/a&gt;.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adjudication.&lt;/strong&gt; The arbiter goes through every objection and accepts it, dismisses it with a written reason, or defers it. Then it revises the artifact and the round runs again.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It converges only when every responding model approves and the arbiter&apos;s pre-committed verdict agrees with them. If they cannot get there inside the round cap, it returns UNRESOLVED and says so. It does not fake a number to look finished.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antonbabenko/deliberation/blob/master/assets/consensus-flow.png&quot;&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;The three-stage consensus loop&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/meet-deliberation-400-models-is-easy-knowing-which-ones-earn-a-place-is-hard/1-anenkxya8hrvlpk3alhw.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Click for the detailed diagram with the bias guards and per-model flow.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The answers are not free text the arbiter has to guess at. Every voice returns a structured opinion, and the engine parses it into fixed fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;recommendation&lt;/strong&gt; (the actual call),&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;confidence&lt;/strong&gt; label, so a weak &quot;maybe&quot; does not count the same as a firm &quot;yes&quot;,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dissent points&lt;/strong&gt;, &lt;strong&gt;assumptions&lt;/strong&gt;, and &lt;strong&gt;tradeoffs&lt;/strong&gt; as separate lists, so a disagreement is attached to a reason instead of buried in prose.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reviews add a &lt;strong&gt;verdict&lt;/strong&gt; (approve / request changes / reject) and a list of &lt;strong&gt;critical issues&lt;/strong&gt;, each sorted into a closed six-category taxonomy - so &quot;three reviewers, nine objections&quot; collapses into a clean, deduplicated set the arbiter can act on. Models are asked to emit this as JSON; the parser is best-effort and never throws, so if a model returns slightly malformed JSON or plain text, the content is salvaged and tagged with how it was read (clean parse vs recovered). Structured where it can be, never brittle.&lt;/p&gt;
&lt;p&gt;Switches: &lt;code&gt;consensus.arbiter&lt;/code&gt; (auto, the host model, a named provider, or a dedicated model record), &lt;code&gt;consensus.blindVote&lt;/code&gt; (add the blind pre-vote on the synthesis path), &lt;code&gt;consensus.maxRounds&lt;/code&gt; (default 5, capped at 50), and a per-model &lt;code&gt;consensus&lt;/code&gt; flag for who sits on the panel.&lt;/p&gt;
&lt;p&gt;So there are four distinct votes in play: the arbiter&apos;s blind pre-commit, each peer&apos;s independent verdict, the anonymized cross-review rating, and the final convergence check. They are doing different jobs on purpose.&lt;/p&gt;
&lt;p&gt;One honest note, since a reviewer pushed me on it: that convergence rule is a heuristic, not a proof. Unanimous approval means nobody in the room objected. It does not mean the answer is right. More on that below.&lt;/p&gt;
&lt;h2 id=&quot;3-synthesis-when-there-is-no-verdict-to-give&quot;&gt;3. Synthesis, when there is no verdict to give&lt;/h2&gt;
&lt;p&gt;Not everything is approve-or-reject. &quot;Which of these two designs should we pick?&quot; has no yes or no. Flip &lt;code&gt;synthesizeAlways: true&lt;/code&gt; and instead of the loop you get a single arbiter pass that reads every opinion and writes one combined answer - free text, no verdict, no rounds. Use the loop for go/no-go calls. Use synthesis for open questions. Same panel, two shapes.&lt;/p&gt;
&lt;h2 id=&quot;4-two-drivers-one-rulebook&quot;&gt;4. Two drivers, one rulebook&lt;/h2&gt;
&lt;p&gt;The loop logic lives in one place - a single state machine - and two things drive it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;consensus&lt;/code&gt; runs the whole loop server-side with a model as the arbiter and hands you back the result in one call. &lt;code&gt;consensus-step&lt;/code&gt; lets your own agent be the arbiter and drive the loop one step at a time, so every move shows up in your transcript where you can see it.&lt;/p&gt;
&lt;p&gt;Two entry points is more surface area than one, and they do not behave identically - one is visible step by step, one is a single call. The win is that the &lt;em&gt;rules&lt;/em&gt; (how rounds count, when it converges) are written once and shared, so the two paths cannot drift apart on the part that matters. That was worth the extra work.&lt;/p&gt;
&lt;h2 id=&quot;5-make-the-panel-stay-accountable&quot;&gt;5. Make the panel stay accountable&lt;/h2&gt;
&lt;p&gt;In a parallel fan-out, your wall-clock time is the slowest model, not the average. One slow member that rarely says anything new sets the clock for everyone. You will not notice by feel. You need numbers.&lt;/p&gt;
&lt;p&gt;So there is an opt-in debug log. Turn on &lt;code&gt;debug.enabled&lt;/code&gt; and it writes one line per model call and per round: latency (p50, p95, max), token counts, error rate, the reasoning effort used. It never records your prompts, the responses, or the issue text - only the timing and the outcome of each vote.&lt;/p&gt;
&lt;p&gt;Turn on &lt;code&gt;sessions.persist&lt;/code&gt; and runs are saved too, so you can ask how often a given model&apos;s verdict matched the final call - and, just as useful, how often it was the lone voice that caught something the others missed, or the lone voice that was simply wrong. A model that always agrees adds cost; a model that disagrees and is usually right is the one you keep.&lt;/p&gt;
&lt;p&gt;Then an &lt;code&gt;analyze&lt;/code&gt; tool reads both back and tells you, in plain terms, who is slow, who is redundant, and which config line to change. From my own panel last week: one model sat at a 200-second p95 while another finished in 15 (Grok 4.3 is often the fastest). &lt;code&gt;analyze&lt;/code&gt; flagged it and suggested the one-line switch to drop it from the default fan-out. I had been waiting on that model for almost a week without realizing it.&lt;/p&gt;
&lt;p&gt;There is also a small dedup cache, so an identical advisory question inside one session returns instantly instead of paying twice. Well, in tool-heavy work the prompts vary, so it hits less than you would hope.&lt;/p&gt;
&lt;p&gt;This is the whole shift from the first post. Consensus was the idea. Making the panel prove it earns its seat is the engineering!&lt;/p&gt;
&lt;h2 id=&quot;the-parts-i-will-not-oversell&quot;&gt;The parts I will not oversell&lt;/h2&gt;
&lt;p&gt;Multi-model review has real failure modes, and a post that hides them deserves the distrust it gets. So:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Agreement is not truth.&lt;/strong&gt; Three models can be confidently, unanimously wrong - especially when they were trained on overlapping data and share the same blind spot. Consensus lowers the odds of a &lt;em&gt;lone&lt;/em&gt; model&apos;s odd mistake. It does not discover facts. Treat it as risk reduction, not a truth oracle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pick models that actually differ.&lt;/strong&gt; A panel of three close cousins is one model agreeing with itself in three accents. The value comes from genuinely different models arguing, which is exactly why the config lets you choose them by hand.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UNRESOLVED is a feature.&lt;/strong&gt; When the room cannot agree, the honest output is &quot;we could not.&quot; That is a signal to slow down and look, not a bug to smooth out. If you wire this into CI, decide on purpose what a deadlock means there - it should probably stop the line, not wave it through.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It is slow, and that is fine for the right job.&lt;/strong&gt; A full consensus loop can take minutes. Run it on plans, designs, and reviews in the background. For a fast second opinion, use single-shot fan-out or synthesis. Do not put a five-round loop in an interactive path.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mind where the text goes.&lt;/strong&gt; Fanning a compliance artifact out to 400 third-party models is a data-boundary decision, not a free upgrade. The config defaults the long tail to off for exactly this reason. Turn models on deliberately.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this is specific to Terraform, AWS, or even to code. The loop runs on anything you can put in text - a plan, a runbook, a decision memo. That generality is the point. It just happens to have been built running a compliance product, which is a good place to learn that &quot;looks done&quot; and &quot;is done&quot; are different claims.&lt;/p&gt;
&lt;h2 id=&quot;the-takeaway&quot;&gt;The takeaway&lt;/h2&gt;
&lt;p&gt;The first lesson was: do not trust one confident model, make a few argue. The second one is harder and more useful: adding voices is easy, and most of them will not earn their seat. Measure the panel like you would measure any other service or people, and cut what does not pay.&lt;/p&gt;
&lt;p&gt;Deliberation is open source, it works in the agent you already use, and most of the models are ones you already pay for. It ships in the &lt;a href=&quot;https://github.com/antonbabenko/agent-plugins&quot;&gt;agent-plugins&lt;/a&gt; marketplace and as a standalone MCP server on npm (&lt;code&gt;@antonbabenko/deliberation-mcp&lt;/code&gt;). Source, and a star button I would genuinely appreciate, are at &lt;a href=&quot;https://github.com/antonbabenko/deliberation&quot;&gt;github.com/antonbabenko/deliberation&lt;/a&gt;. Install it, point GPT and Claude at each other, and let me know where it breaks. The cheapest review still happens before you execute - just make sure every reviewer in the room is worth the wait and the cost.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Also published on &lt;a href=&quot;https://dev.to/aws-heroes/meet-deliberation-400-models-is-easy-knowing-which-ones-earn-a-place-is-hard-4o5g&quot;&gt;dev.to&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>ai</category><category>llm</category><category>council</category><category>deliberation</category></item><item><title>One model is a guess. Three that agree is a plan.</title><link>https://antonbabenko.com/one-model-is-a-guess-three-that-agree-is-a-plan/</link><guid isPermaLink="true">https://antonbabenko.com/one-model-is-a-guess-three-that-agree-is-a-plan/</guid><description>Why I shipped multi-model consensus as a plugin, plus two quieter tools that keep agents honest.</description><pubDate>Mon, 18 May 2026 08:04:46 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Updated (28.5.2026): &lt;code&gt;/consensus&lt;/code&gt; is now a 3-stage loop. Stage 2 adds a blind cross-review where each external rates the others&apos; answers, anonymized, before Claude adjudicates. Pattern from &lt;a href=&quot;https://github.com/karpathy/llm-council&quot;&gt;karpathy/llm-council&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Updated (26.5.2026): Grok (xAI) joins GPT and Gemini as the third external provider, Gemini 3 is the default via Google&apos;s Antigravity CLI, two new experts (Researcher and Debugger) bring the count to seven, reviews are severity-graded, and &lt;code&gt;/consensus&lt;/code&gt; now forces Claude to commit a blind verdict in the transcript before dispatching - arbiter-mediated, not pure democracy.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ask one model to plan something hard - a migration, a refactor, a cutover - and you get a fluent, confident answer. Fluency is not correctness. A single model is articulate and alone, and being alone is the problem: nothing in the loop disagrees with it, so it rationalizes its first guess into a plan.&lt;/p&gt;
&lt;p&gt;The expensive failures with coding agents are almost never syntax. They are plans that read well and were wrong: the wrong abstraction, the missed blast radius, the migration step that bricks state. You find out three hours into execution, not at review time.&lt;/p&gt;
&lt;p&gt;I have been running the fix for the last few months - on Terraform modules, and on the everyday work of running &lt;a href=&quot;https://compliance.tf&quot;&gt;compliance.tf&lt;/a&gt;, not only on code. It is not a bigger model. It is making models disagree on purpose and then forcing them to resolve it. That is what &lt;code&gt;consensus&lt;/code&gt; does, and it is the reason the &lt;a href=&quot;https://github.com/antonbabenko/agent-plugins&quot;&gt;&lt;code&gt;agent-plugins&lt;/code&gt;&lt;/a&gt; repo exists. Two other tools ship with it; they are narrower, and I will get to them.&lt;/p&gt;
&lt;h2 id=&quot;one-model-is-a-guess&quot;&gt;One model is a guess&lt;/h2&gt;
&lt;p&gt;A single model samples one distribution with no adversary in the room. Two independent models rarely make the same mistake on a plan. Where they diverge is, almost exactly, the risky part of the plan - the assumption nobody checked. A consensus loop turns that disagreement from noise into a signal you can act on.&lt;/p&gt;
&lt;h2 id=&quot;what-consensus-actually-does&quot;&gt;What &lt;code&gt;/consensus&lt;/code&gt; actually does&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;All input is important&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/one-model-is-a-guess-three-that-agree-is-a-plan/1-jldhv7ayzq2e016btiwd.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/consensus&lt;/code&gt; runs GPT (via Codex), Gemini 3 (via Antigravity), and Grok (via the xAI API) against the same artifact, with Claude as the arbiter. Each round has three stages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stage 1 - parallel verdicts.&lt;/strong&gt; Claude posts its own verdict (APPROVE / REQUEST CHANGES / REJECT) into the transcript first, blind, before any external sees the work. The pre-commitment sits there in writing, so Claude&apos;s judgment cannot drift later to match what the others say. Then GPT, Gemini, and Grok review the artifact in parallel, each in a fresh thread, single-shot. None sees another&apos;s review.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stage 2 - blind cross-review.&lt;/strong&gt; Each external then rates the OTHER externals&apos; answers, identity stripped best-effort. Votes of &quot;not viable&quot; become candidate critical issues the arbiter has to weigh. This catches the case where Stage 1 looks like agreement but is really three reviewers each rationalizing past the same hole. Pattern adapted from &lt;a href=&quot;https://github.com/karpathy/llm-council&quot;&gt;karpathy/llm-council&lt;/a&gt;. Stage 2 fires every round 1, and after that only when Stage 1 disagreed or the previous Stage 2 surfaced an accepted issue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stage 3 - arbiter adjudication.&lt;/strong&gt; Claude reconciles the Stage 1 verdicts, the Stage 2 candidate issues, and its own blind verdict. Every objection is accepted, dismissed with a recorded reason, or deferred. Claude revises the artifact and the loop runs again, up to five rounds. It converges only when every responding external approves and Claude&apos;s pre-committed verdict agrees, with reasons on both sides where it walked back. If the group cannot agree, it says so plainly instead of faking it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antonbabenko/claude-delegator/blob/master/assets/consensus-flow.png&quot;&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;/consensus 3-stage flow&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/one-model-is-a-guess-three-that-agree-is-a-plan/2-consensus-flow-simple.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Click for the detailed diagram with bias guards and per-model flow.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Independence is not only about which model. It is sharper when each reviewer wears a different hat. &lt;code&gt;claude-delegator&lt;/code&gt; ships seven expert profiles - Architect, Plan Reviewer, Scope Analyst, Code Reviewer, Security Analyst, Researcher, and Debugger.&lt;/p&gt;
&lt;p&gt;Combine the axes. A Security Analyst on Gemini and an Architect on GPT fight about different things than one model reviewing twice. Different profiles catch different &lt;em&gt;categories&lt;/em&gt; of mistake.&lt;/p&gt;
&lt;p&gt;For a migration plan I run the Plan Reviewer broadly and add a Security Analyst pass on top. &quot;Is this safe to run&quot; and &quot;is this the right shape&quot; get argued by separate reviewers, not averaged into one bland verdict.&lt;/p&gt;
&lt;p&gt;There is a second kind of contamination worth naming: me. In &lt;code&gt;consensus&lt;/code&gt;, every round sends the reviewers the same artifact text, cold, in a fresh thread. They never see my triage, my running verdict, or how I framed the previous round - only the artifact and bounded round metadata. My judgment is applied after they report, not baked into what they receive.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ask-*&lt;/code&gt; commands are the opposite by design: the expert gets exactly the prompt I assembled from the conversation - what I chose to hand it. Fast for a second opinion, but the input is mine, not independent. &lt;code&gt;consensus&lt;/code&gt; keeps the input independent and pays for it in extra rounds.&lt;/p&gt;
&lt;p&gt;It does not have to be a plan. The loop runs on anything you can put in text - a design, a runbook, a decision memo, a spec. Plans are simply where I reach for it most, and where it converges fastest. The looser and fuzzier the input, the more rounds it takes to agree, so non-plan runs tend to run longer - worth it when the answer matters, overkill for a quick lookup. For that, single-shot &lt;code&gt;ask-gpt&lt;/code&gt;, &lt;code&gt;ask-gemini&lt;/code&gt;, &lt;code&gt;ask-grok&lt;/code&gt;, and &lt;code&gt;ask-all&lt;/code&gt; (which fans out to all three in parallel) are right there. &lt;code&gt;consensus&lt;/code&gt; is for when it has to be right.&lt;/p&gt;
&lt;p&gt;Nothing about this is Terraform, or even code. That generality is why it is the headline.&lt;/p&gt;
&lt;h2 id=&quot;the-quieter-two&quot;&gt;The quieter two&lt;/h2&gt;
&lt;p&gt;Same release, narrower scope:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/antonbabenko/agent-plugins&quot;&gt;code-intelligence&lt;/a&gt;&lt;/strong&gt; - a language-agnostic skill. Agents grab text &lt;code&gt;grep&lt;/code&gt; when they should ask the language server, and silently swap tools when one is missing, then report &quot;found all references.&quot; This encodes search precedence: the language server (LSP) for symbols, &lt;code&gt;rg&lt;/code&gt;/&lt;code&gt;ripgrep&lt;/code&gt; for exact text, an embedding/semantic grep (such as &lt;a href=&quot;https://github.com/mixedbread-ai/mgrep&quot;&gt;&lt;code&gt;mgrep&lt;/code&gt;&lt;/a&gt;) for fuzzy discovery - and a hard rule to disclose any substitution on the first line of the reply. You learn the moment coverage drops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/antonbabenko/terraform-skill&quot;&gt;terraform-skill&lt;/a&gt;&lt;/strong&gt; - routes a Terraform/OpenTofu request to its real failure mode (identity churn, blast radius, state corruption) before emitting HCL. It is &lt;code&gt;terraform-ls&lt;/code&gt; aware: it knows the language server has no rename provider, so it runs the safe manual reference workflow instead of a blind find-replace. It is approaching 2,000 GitHub stars - the part of this post I am quietly proud of.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those two are discipline for the agent&apos;s hands. Consensus is discipline for its judgment, and judgment generalizes further.&lt;/p&gt;
&lt;h2 id=&quot;a-note-on-claude-delegator&quot;&gt;A note on claude-delegator&lt;/h2&gt;
&lt;p&gt;I did not invent the delegation layer. &lt;a href=&quot;https://github.com/antonbabenko/claude-delegator&quot;&gt;&lt;code&gt;claude-delegator&lt;/code&gt;&lt;/a&gt; is a fork of &lt;a href=&quot;https://github.com/jarrodwatts/claude-delegator&quot;&gt;Jarrod Watts&apos; original&lt;/a&gt; (MIT, upstream currently quiet), fully based on his design - I kept the structure and the license.&lt;/p&gt;
&lt;p&gt;What I added is what months of daily use exposed: a Gemini bridge that wraps Google&apos;s Antigravity CLI (&lt;code&gt;agy&lt;/code&gt;) with &lt;code&gt;auto-gemini-3&lt;/code&gt; as the routing default and recovers an answer the CLI flushed to disk after a soft timeout instead of failing the call; a fresh Grok bridge over the xAI API that is advisory-only but reads attached files via the xAI Files API (with TTL-based cleanup); two more experts (Researcher and Debugger) on top of the original five; severity-graded reviews so three parallel reports merge cleanly; and a hardened &lt;code&gt;/consensus&lt;/code&gt; loop where Claude pre-commits a blind verdict before any external sees the artifact, with a Stage 2 blind cross-review on top (adapted from karpathy/llm-council). Plus the bundled &lt;code&gt;ask-gpt&lt;/code&gt; / &lt;code&gt;ask-gemini&lt;/code&gt; / &lt;code&gt;ask-grok&lt;/code&gt; / &lt;code&gt;ask-all&lt;/code&gt; / &lt;code&gt;consensus&lt;/code&gt; commands, so the workflow ships with the plugin instead of living in my dotfiles. The seven expert prompts borrow from &lt;code&gt;oh-my-openagent&lt;/code&gt; and &lt;code&gt;PAL&lt;/code&gt;; both credited in the README.&lt;/p&gt;
&lt;p&gt;Credit for the foundation is his; the bug-fixing scar tissue is mine.&lt;/p&gt;
&lt;h2 id=&quot;why-a-plugin-not-a-blog-post&quot;&gt;Why a plugin, not a blog post&lt;/h2&gt;
&lt;p&gt;One honest caveat, because I have hit it myself: skills are model-triggered, which makes them soft. Packaging this as a plugin improves reuse and discoverability. It does not guarantee the agent obeys every time - hard enforcement (a real pre-tool gate) is a separate, still-open problem.&lt;/p&gt;
&lt;p&gt;I keep finding and fixing bugs in all three. That constant repair is the only reason I trust them enough to write this - and I would rather say so than oversell the fix.&lt;/p&gt;
&lt;h2 id=&quot;the-takeaway&quot;&gt;The takeaway&lt;/h2&gt;
&lt;p&gt;Stop accepting the first confident plan from one model. Make them argue, and only move when they stop. The release is at &lt;a href=&quot;https://github.com/antonbabenko/agent-plugins&quot;&gt;github.com/antonbabenko/agent-plugins&lt;/a&gt;; &lt;code&gt;consensus&lt;/code&gt; ships in &lt;a href=&quot;https://github.com/antonbabenko/claude-delegator&quot;&gt;&lt;code&gt;claude-delegator&lt;/code&gt;&lt;/a&gt; from the same marketplace. The cheapest review is the one that happens before you execute.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Also published on &lt;a href=&quot;https://dev.to/aws-heroes/one-model-is-a-guess-three-that-agree-is-a-plan-1po3&quot;&gt;dev.to&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>ai</category><category>llm</category><category>productivity</category><category>agents</category></item><item><title>Your Weekly Dose of Terraform — Live Streams</title><link>https://antonbabenko.com/your-weekly-dose-of-terraform-live-streams/</link><guid isPermaLink="true">https://antonbabenko.com/your-weekly-dose-of-terraform-live-streams/</guid><description>I do live streams talking about things I enjoy working with - IaC, DevOps, Terraform, and AWS.</description><pubDate>Sat, 26 Sep 2020 17:35:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;i-do-live-streams-talking-about-things-i-enjoy-working-withiac-devops-terraform-and-aws&quot;&gt;I do live streams talking about things I enjoy working with — IaC, DevOps, Terraform, and AWS.&lt;/h2&gt;
&lt;p&gt;The primary focus is on Terraform — what’s new, tools reviews, live-coding, do interviews with creators and community members, answer questions live, and so on.&lt;/p&gt;
&lt;p&gt;Here you can see previous streams — &lt;a href=&quot;https://www.youtube.com/channel/UCGH0yYPvlCN1VjSFMGVmFgQ&quot;&gt;https://www.youtube.com/channel/UCGH0yYPvlCN1VjSFMGVmFgQ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Or, if you want to see just Terraform tools reviews:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;checkov —Static code analysis for Terraform/CloudFormation/etc — &lt;a href=&quot;https://www.youtube.com/watch?v=KdH-7_vpFag&quot;&gt;https://www.youtube.com/watch?v=KdH-7_vpFag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;tflint — Linting Terraform configurations for best-practices — &lt;a href=&quot;https://www.youtube.com/watch?v=6w_f3MOVxBI&quot;&gt;https://www.youtube.com/watch?v=6w_f3MOVxBI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Terragrunt — &lt;a href=&quot;https://www.youtube.com/watch?v=ILKJ3IuKCAo&quot;&gt;https://www.youtube.com/watch?v=ILKJ3IuKCAo&lt;/a&gt; — to be presented on the 2nd of October 2020.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are learning Terraform or want to stay informed on what is going on in the Terraform world — subscribe and join live-streams on YouTube, Twitch, and Periscope (Twitter).&lt;/p&gt;
&lt;p&gt;YouTube: &lt;a href=&quot;https://www.youtube.com/channel/UCGH0yYPvlCN1VjSFMGVmFgQ&quot;&gt;https://www.youtube.com/channel/UCGH0yYPvlCN1VjSFMGVmFgQ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Twitch: &lt;a href=&quot;https://www.twitch.tv/antonbabenko&quot;&gt;https://www.twitch.tv/antonbabenko&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Periscope: &lt;a href=&quot;https://periscope.tv/antonbabenko&quot;&gt;https://periscope.tv/antonbabenko&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>terraform</category><category>interview</category><category>aws</category></item><item><title>Anonymized, Secure, and Free Terraform Cost Estimation</title><link>https://antonbabenko.com/terraform-cost-estimation/</link><guid isPermaLink="true">https://antonbabenko.com/terraform-cost-estimation/</guid><description>There are some things you should know about infrastructure costs: How much does my infrastructure is going to cost before create?</description><pubDate>Thu, 14 May 2020 14:42:00 GMT</pubDate><content:encoded>&lt;p&gt;There are some things you should know about infrastructure costs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How much does my infrastructure is going to cost &lt;strong&gt;before&lt;/strong&gt; create?&lt;/li&gt;
&lt;li&gt;How much does my infrastructure cost &lt;strong&gt;after&lt;/strong&gt; it is created (based on Terraform state)?&lt;/li&gt;
&lt;li&gt;What is the &lt;strong&gt;difference in the price&lt;/strong&gt; comparing to the current infrastructure (based on Terraform plan)?&lt;/li&gt;
&lt;li&gt;Can I have cost estimation based on Terraform 0.7 state files? Yes, any version of Terraform state files is supported!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is exactly why I started &lt;a href=&quot;https://github.com/antonbabenko/terraform-cost-estimation&quot;&gt;terraform-cost-estimation&lt;/a&gt; project while working on bigger &lt;a href=&quot;https://modules.tf/&quot;&gt;modules.tf&lt;/a&gt;!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Anonymized, secure and free Terraform cost estimation based on Terraform plan (0.12+) or Terraform state (any version) – &lt;a href=&quot;https://github.com/antonbabenko/terraform-cost-estimation&quot;&gt;https://github.com/antonbabenko/terraform-cost-estimation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In a nutshell, infrastructure developer posts some input data to &lt;code&gt;cost.modules.tf&lt;/code&gt; endpoint and receives a response containing costs summary:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;https://antonbabenko.com/_astro/ec.wshpv.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://antonbabenko.com/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;$ terraform state 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 style=&quot;--0:#e6edf3&quot;&gt;curl -s -X POST -H &quot;Content-Type: application/json&quot; -d @- https://cost.modules.tf/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;{&quot;hourly&quot;: &quot;0.01&quot;, &quot;monthly&quot;: &quot;9.07&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 aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;$ terraform state pull | \curl -s -X POST -H &amp;quot;Content-Type: application/json&amp;quot; -d @- https://cost.modules.tf/{&amp;quot;hourly&amp;quot;: &amp;quot;0.01&amp;quot;, &amp;quot;monthly&amp;quot;: &amp;quot;9.07&amp;quot;}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;These are the supported input types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Terraform state file (as JSON)&lt;/li&gt;
&lt;li&gt;Terraform plan (as JSON)&lt;/li&gt;
&lt;li&gt;List of cost keys extracted from Terraform state or plan locally&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;i-dont-want-to-send-my-terraform-data-because-i-have-secrets-there&quot;&gt;I don&apos;t want to send my Terraform data, because I have secrets there!&lt;/h2&gt;
&lt;p&gt;I know you have them there, and there is a solution for this which I call &lt;em&gt;client-side filtering using JQ&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It is the most secure way and it works like this – pass Terraform state or plan file through &lt;a href=&quot;https://github.com/antonbabenko/terraform-cost-estimation/blob/master/terraform.jq&quot;&gt;terraform.jq&lt;/a&gt; filter to extract so-called &lt;em&gt;cost keys&lt;/em&gt;, and then submit them to get cost estimation without need to reveal any of your Terraform files.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;$ curl -sLO https://raw.githubusercontent.com/antonbabenko/terraform-cost-estimation/master/terraform.jq&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;$ terraform state pull | jq -cf terraform.jq | \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;curl -s -X POST -H &quot;Content-Type: application/json&quot; -d @- https://cost.modules.tf/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;$ curl -sLO https://raw.githubusercontent.com/antonbabenko/terraform-cost-estimation/master/terraform.jq$ terraform state pull | jq -cf terraform.jq | \curl -s -X POST -H &amp;quot;Content-Type: application/json&amp;quot; -d @- https://cost.modules.tf/&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Cost keys do not contain any customer secrets or sensitive information (like AWS accounts, AWS keys, tags, names, AMI ID, IP addresses, etc). Cost keys are used only to get cost information.&lt;/p&gt;
&lt;p&gt;In the following example cost keys for single EC2 instance, NAT Gateway and an Application Load Balancer in &lt;code&gt;eu-west-1&lt;/code&gt; region were extracted:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;{&quot;keys&quot;:[&quot;ec2#eu-west-1#t3.nano#shared#linux&quot;, &quot;ec2#eu-west-1#nat&quot;, &quot;ec2#eu-west-1#alb&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 aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{&amp;quot;keys&amp;quot;:[&amp;quot;ec2#eu-west-1#t3.nano#shared#linux&amp;quot;, &amp;quot;ec2#eu-west-1#nat&amp;quot;, &amp;quot;ec2#eu-west-1#alb&amp;quot;]}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You can see complete examples and list of supported services here - &lt;a href=&quot;https://github.com/antonbabenko/terraform-cost-estimation&quot;&gt;https://github.com/antonbabenko/terraform-cost-estimation&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;bonus-run-terraform-cost-estimation-from-the-browser&quot;&gt;Bonus: Run Terraform Cost Estimation from the browser&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://terraform-cost-estimation.com/&quot;&gt;Terraform Cost Estimation&lt;/a&gt; - Anonymized, secure, and free Terraform cost estimation based on Terraform plan (0.12+) or Terraform state (any version)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remember to star&amp;amp;watch this project on GitHub, and share this project with your connections!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antonbabenko/terraform-cost-estimation&quot;&gt;antonbabenko/terraform-cost-estimation&lt;/a&gt; - Anonymized, secure and free Terraform cost estimation based on Terraform plan (0.12+) or Terraform state (any version) - antonbabenko/terraform-cost-estimation&lt;/p&gt;
</content:encoded><category>terraform</category></item><item><title>&quot;Terraform AWS modules&quot; as a code</title><link>https://antonbabenko.com/terraform-aws-modules-as-a-code/</link><guid isPermaLink="true">https://antonbabenko.com/terraform-aws-modules-as-a-code/</guid><description>After several years of maintaining Terraform AWS modules on GitHub and making constant improvements in processes I decided to spend more time and improve</description><pubDate>Fri, 20 Mar 2020 08:52:46 GMT</pubDate><content:encoded>&lt;p&gt;After several years of maintaining &lt;a href=&quot;https://github.com/terraform-aws-modules&quot;&gt;Terraform AWS modules on GitHub&lt;/a&gt; and making constant improvements in processes I decided to spend more time and improve things even further.&lt;/p&gt;
&lt;p&gt;I want other people to be involved and recognized also while I spend more time on other projects (eg, &lt;a href=&quot;https://modules.tf&quot;&gt;modules.tf&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The main motivation for doing changes is that I want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recognize individuals who help triaging and fixing issues. This is by far the most time-consuming activity in this project.&lt;/li&gt;
&lt;li&gt;Recognize contribution of everyone involved in the process (write code, answer issues, update documentation, etc).&lt;/li&gt;
&lt;li&gt;Standardize releasing, updating, testing, documenting and some other procedures across all Terraform modules.&lt;/li&gt;
&lt;li&gt;Create and manage many new Terraform AWS modules which satisfy the same quality requirements as other modules.&lt;/li&gt;
&lt;li&gt;Integrate more Terraform modules into &lt;a href=&quot;https://modules.tf&quot;&gt;modules.tf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From now on, the configuration of all &lt;code&gt;terraform-aws-modules&lt;/code&gt; repositories and common files (not related to the module itself, but more related to the management procedures) is available in this repository - &lt;a href=&quot;https://github.com/terraform-aws-modules/meta&quot;&gt;https://github.com/terraform-aws-modules/meta&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feedback and questions are welcome!&lt;/p&gt;
</content:encoded><category>terraform</category><category>aws</category></item><item><title>My Terraform AWS journey — HashiTimes Interview</title><link>https://antonbabenko.com/my-terraform-aws-journey-june-2019/</link><guid isPermaLink="true">https://antonbabenko.com/my-terraform-aws-journey-june-2019/</guid><description>I&apos;d like to share an extended interview which I gave to HashiTimes (newsletter curated by the community and not affiliated with HashiCorp) in June 2019.</description><pubDate>Thu, 13 Jun 2019 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’d like to share an extended interview which I gave to HashiTimes (newsletter curated by the community and not affiliated with HashiCorp) in June 2019. I added some more additional links and information.&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;In this week’s HashiTimes Interview, we sat down with Anton Babenko, a Developer, Public Speaker, and Evangelist based in Oslo, Norway.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;tell-us-a-little-about-yourself-your-background-around-hashicorp-and-what-do-you-do-professionally&quot;&gt;&lt;strong&gt;Tell us a little about yourself, your background around HashiCorp, and what do you do professionally?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;I started working with Terraform and Packer in 2015. In December 2015, I gave a talk about how to use Terraform with AWS at a local &lt;a href=&quot;https://www.meetup.com/AWS-User-Group-Norway/&quot;&gt;AWS User Group meetup in Oslo&lt;/a&gt; because I wanted to share how awesome Terraform was (and still is). You can see a video recording &lt;a href=&quot;https://www.youtube.com/watch?v=rgzzkP2L1k8&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Around the same time, I requested access to push &lt;a href=&quot;https://github.com/terraform-community-modules/tf_aws_bastion_s3_keys&quot;&gt;my first Terraform module&lt;/a&gt; and got access to manage all other repositories under &lt;a href=&quot;https://github.com/terraform-community-modules&quot;&gt;terraform-community-modules organization&lt;/a&gt;. This was the starting point for my open-source participation within Terraform. I got full access, and soon I started answering questions about other modules even without using them myself. I was a little uncertain if I was doing it right by assuming positive intent, and soon, I realized that other contributors liked the way I act and joined the organization and submitted their modules.&lt;/p&gt;
&lt;p&gt;Fast-forward to 2017, I cleaned up existing modules, added many missing features and rewrote the most popular AWS modules including VPC, RDS, Autoscaling, ELB, EC2 instance, and EC2 security group. These modules went live as verified Terraform AWS modules during HashiConf 2017 in Austin together with the announcement of the &lt;a href=&quot;https://registry.terraform.io&quot;&gt;Terraform Registry&lt;/a&gt;. Right after the announcement at HashiConf during a break, some people were joking “why the VPC module is such a monster and creates so much…”. Well, because this is a powerful infrastructure module…&lt;/p&gt;
&lt;p&gt;Since then I maintain &lt;a href=&quot;https://github.com/terraform-aws-modules&quot;&gt;terraform-aws-modules&lt;/a&gt; used by hundreds of companies worldwide, reviewing code, answering questions, adding new features, collecting use-cases from users, giving talks and workshops. There were more than 1K of pull-requests and issues open &amp;amp; closed in all modules I maintain, and more than 4 million downloads since the release of the &lt;a href=&quot;https://registry.terraform.io&quot;&gt;Terraform Registry&lt;/a&gt;.Terraform AWS modules are now 0.12 ready!&lt;/p&gt;
&lt;p&gt;In June 2019 Amazon recognized my work in the community and called me an &lt;a href=&quot;https://aws.amazon.com/developer/community/heroes/anton-babenko/&quot;&gt;AWS Community Hero&lt;/a&gt;. It took me several years of doing the things I enjoy.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/my-terraform-aws-journey-june-2019/1-6b6qsH3cOAvVvGk-BMRPxw.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Today, for the living I provide consulting services and workshops for companies who want to use Terraform and AWS. Most of my customers are using open-source modules and tools, as well as contribute back to open-source (few Terraform modules and projects were released like that, few more — in progress).&lt;/p&gt;
&lt;h2 id=&quot;anton-you-have-been-a-very-active-member-in-the-hashicorp-dev-community-with-some-of-your-open-source-projects-terraform-modules-and-other-tools-available-today-can-you-share-with-the-readers-what-modulestf-does-terraform-docs-as-pdf-and-how-these-tools-can-enable-them-as-developers&quot;&gt;&lt;strong&gt;Anton, you have been a very active member in the HashiCorp Dev Community with some of your open source projects, Terraform Modules, and other tools available today. Can you share with the readers what modules.tf does, terraform docs as pdf and how these tools can enable them as Developers?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/terraform-aws-modules&quot;&gt;Terraform AWS modules&lt;/a&gt; were the starting point. Almost immediately after I started maintaining a couple of them (back in 2016) I have been looking into tools which could simplify the review, update, test, release process, and allow to scale up (in quality and in complexity). My ultimate goal is to make a system which will handle hundreds of high-quality modules with fewer efforts on the developer’s side. The first prototype called “terrapin” (see &lt;a href=&quot;https://github.com/antonbabenko/terrapin&quot;&gt;source code&lt;/a&gt;) I made in 2017. It was good and working as I wanted, but a bit too over-engineered according to myself after I came back from vacation and looked at it again soberly.&lt;/p&gt;
&lt;p&gt;I like to make things which can impact developers or just fun to work on, learn from it, and go on to the next.&lt;/p&gt;
&lt;p&gt;At some point, when I already realized that modules in Terraform are the building blocks of infrastructure, I wanted to figure out how to simplify infrastructure as code more and how to make it available for a wider group of people. I bought a start-up ticket to &lt;a href=&quot;https://websummit.com&quot;&gt;Web Summit&lt;/a&gt; 2017 in Lisbon, made a website to illustrate that infrastructure code can be generated by completing easy forms and stayed there for one day pitching my ideas and asking potential users. It turned out that many people didn’t understand the point, they didn’t have the problem I anticipated. They liked nice diagrams created using cloudcraft.co for AWS infrastructure. They liked visual, and I wanted to push Terraform. This was the beginning of modules.tf in the way it is now. &lt;a href=&quot;https://modules.tf/&quot;&gt;Modules.tf&lt;/a&gt; — is an open-source project which allows converting visual diagrams created with &lt;a href=&quot;https://cloudcraft.co/&quot;&gt;cloudcraft.co&lt;/a&gt; into infrastructure configurations using Terraform.&lt;/p&gt;
&lt;p&gt;As a developer myself I wanted users of modules.tf to think about it as an opinionated (yet, powerful) starting point for AWS projects. The project is still early in the development phase. The source code is available &lt;a href=&quot;https://github.com/antonbabenko/modules.tf-lambda&quot;&gt;here&lt;/a&gt;. It is using Serverless Framework with Python 3 and running on AWS Lambda.&lt;/p&gt;
&lt;p&gt;While working on modules.tf I needed to connect values in tfvars defined in different directories/stacks/layers dynamically so that outputs of one terraform module can be passed to another terraform module transparently. “Update values in terraform.tfvars using annotations” — this is &lt;a href=&quot;https://github.com/antonbabenko/tfvars-annotations&quot;&gt;tfvars-annotations&lt;/a&gt; is about. It is currently not complete but already usable for my cases in modules.tf. I also wanted to learn Go language better by doing something small and real.&lt;/p&gt;
&lt;p&gt;In 2016, I released &lt;a href=&quot;https://github.com/antonbabenko/pre-commit-terraform&quot;&gt;pre-commit-terraform&lt;/a&gt; — a collection of pre-commit git hooks to take care of Terraform configurations. Those hooks can automatically format, validate, generate documentation for Terraform code. I am glad to see more and more developers are using it in their work.&lt;/p&gt;
&lt;p&gt;During my work, I develop various solutions and tools which I prefer to share with the hope that someone will find it useful and begin to cooperate (open issue, give feedback, write code, update documentation, etc). Often people find them useful because it saves them time and makes them more productive. I think this is the reason why some people appreciate &lt;a href=&quot;https://github.com/antonbabenko/terraform-docs-as-pdf&quot;&gt;“Terraform docs as pdf”&lt;/a&gt; I made after I didn’t have Terraform documentation during the cross-Atlantic flight.&lt;/p&gt;
&lt;p&gt;After being in the community for some time I’ve gathered enough do and don’ts from real projects and use-cases, so I published them as &lt;a href=&quot;https://www.terraform-best-practices.com/&quot;&gt;Terraform Best Practices&lt;/a&gt; (&lt;a href=&quot;https://github.com/antonbabenko/terraform-best-practices&quot;&gt;source code&lt;/a&gt;). Some things are incomplete there, and people still ask me “how to structure my infrastructure as code?” even after I pointed them to this website. For me, this means that I need to find time to update it significantly.&lt;/p&gt;
&lt;p&gt;Another piece of educational content I have created recently is — &lt;a href=&quot;https://github.com/antonbabenko/terraform-best-practices-workshop&quot;&gt;Terraform best practices workshop&lt;/a&gt; materials. I run various Terraform AWS workshops on-site for companies and at community events like DevOpsDays and AWS Community Summits.&lt;/p&gt;
&lt;p&gt;In May 2019, I decided to share my knowledge regarding &lt;a href=&quot;https://github.com/gruntwork-io/terragrunt&quot;&gt;Terragrunt&lt;/a&gt; and published real code with some of the important points highlighted as &lt;a href=&quot;https://github.com/antonbabenko/terragrunt-reference-architecture&quot;&gt;Terragrunt Reference Architecture&lt;/a&gt;. I believe this will be relevant to people who want to write clean code and use Terraform modules in the most efficient ways.&lt;/p&gt;
&lt;h2 id=&quot;you-were-instrumental-in-launching-the-hashicorp-user-groups-program-along-with-kate-reese--hashicorp-for-those-in-the-community-who-have-not-attended-an-emea-region-hug-before-could-you-share-the-success-you-have-seen-in-the-emea-region-growing-the-hug-community&quot;&gt;&lt;strong&gt;You were instrumental in launching the HashiCorp User Groups program along with Kate Reese @ HashiCorp. For those in the community who have not attended an EMEA region HUG before, could you share the success you have seen in the EMEA region growing the HUG community?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;There are more than 30 HashiCorp user groups in EMEA with several thousands of active members. The number of meetups and quality of talks is increasing. Few organizers are trying out different formats for their events and aiming meetups for different audiences.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.meetup.com/Hamburg-HashiCorp-User-Group/&quot;&gt;HUG in Hamburg&lt;/a&gt;, for example, there was a workshop with a dedicated group of people per product. Some HUGs are mixing advanced and beginner talks during one meetup to satisfy a wider audience. &lt;a href=&quot;https://www.meetup.com/Zurich-HashiCorp-User-Group/&quot;&gt;HUG in Zürich&lt;/a&gt; is doing a great job by having at least 2–4 talks during a meetup, while most meetups have one talk because it is hard to find good speakers.&lt;/p&gt;
&lt;p&gt;When I started the &lt;a href=&quot;https://www.meetup.com/Oslo-HashiCorp-User-Group/&quot;&gt;HUG in Oslo&lt;/a&gt;, I wanted to have meetups about HashiCorp products and have speakers share their real stories and challenges. I’ve been co-organizing &lt;a href=&quot;https://www.meetup.com/AWS-User-Group-Norway/&quot;&gt;AWS User Group in Oslo&lt;/a&gt; since 2015 and already knew that it was very helpful and interesting to learn &amp;amp; share experiences with like-minded individuals. To make it more interesting, I decided to have meetups where attendees can learn about real and advanced use-cases. I don’t want us to repeat official documentation and well-known information which can be found easily online. In general, I want to avoid impractical talks.&lt;/p&gt;
&lt;p&gt;Being a smaller community is actually good. The amount of generic DevOps meetups is decreasing, and attendees prefer to have specialization by product and by technologies (CNCF, K8S, Serverless) where they can dive deeper. Additionally, HUG meetups are often relevant to AWS or Google Cloud user groups as long as they touch those services. Win-win, and cross-links :)&lt;/p&gt;
&lt;p&gt;In 2018, I proposed to HashiCorp a new format for the event where anyone in the HashiCorp community around the globe can participate and share whatever is interesting for the HUG community worldwide. Each speaker had 30 minutes; the whole event was virtual and lasted 24 hours to cover all time zones. This way &lt;a href=&quot;https://hashicorpalldayhashitalks.splashthat.com/&quot;&gt;HashiCorp All-Day HashiTalks&lt;/a&gt; was organized in February 2019.&lt;/p&gt;
&lt;p&gt;Finally, If you are still reading this and you want to organize &lt;a href=&quot;https://www.meetup.com/pro/hugs/&quot;&gt;HashiCorp User Group in your area&lt;/a&gt;, please contact Katie Reese to help you get started.&lt;/p&gt;
&lt;h2 id=&quot;finally-how-do-you-believe-the-hashicorp-community-plays-a-part-in-the-overall-success-of-hashicorp-as-a-company&quot;&gt;&lt;strong&gt;Finally, how do you believe the HashiCorp Community plays a part in the overall success of HashiCorp as a Company?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The HashiCorp community is definitely playing a significant role in company success. Open-source versions of their products are driving most of the roadmaps of the individual projects. This is why I always recommend people to open issues if they found bugs or want some feature requests to be considered.&lt;/p&gt;
&lt;p&gt;The community is also growing pretty nicely with lots of tools being created to integrate and extend the functionality provided by HashiCorp tools, as well as lots of Slack groups (eg, &lt;a href=&quot;https://sweetops.com/slack/&quot;&gt;SweetOps&lt;/a&gt; and &lt;a href=&quot;https://signup.hangops.com/&quot;&gt;hangops&lt;/a&gt;) where a couple of thousand users are online daily. Some of the tools are being updated to support Terraform 0.12 and HCL2 syntax, while some people in the community discover short-term hacks to adopt Terraform 0.12 and its pretty long list of features as soon as possible. The community is certainly busier now more a month ago :)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;PS: I’ve recently studied &lt;a href=&quot;https://www.amazon.jobs/en/principles&quot;&gt;Amazon Leadership Principles&lt;/a&gt; and realized that I apply these principles on a daily basis: “Invent And Simplify”, “Customer Obsession”, “Ownership”, “Are Right, A Lot”, “Learn and Be Curious”, “Insist on the Highest Standards”, “Think Big”, “Bias for Action”, “Frugality”, “Earn Trust”, “Dive Deep”, “Have Backbone; Disagree and Commit”, “Deliver Results”. It didn’t give me a job at AWS if you wonder. :)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;please-share-this-article-on-your-social-networks-and-feel-free-to-connect-with-me&quot;&gt;Please share this article on your social networks and feel free to connect with me:&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antonbabenko&quot;&gt;https://github.com/antonbabenko&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/antonbabenko&quot;&gt;https://twitter.com/antonbabenko&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com/in/antonbabenko&quot;&gt;https://www.linkedin.com/in/antonbabenko&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@anton.babenko&quot;&gt;https://medium.com/@anton.babenko&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>interview</category></item><item><title>Terraform documentation as PDF files</title><link>https://antonbabenko.com/terraform-documentation-as-pdf-files/</link><guid isPermaLink="true">https://antonbabenko.com/terraform-documentation-as-pdf-files/</guid><description>I like to search in it and access it offline (during a flight, for example). I&apos;ve made a repository where documentation for Terraform core and all official</description><pubDate>Tue, 08 Jan 2019 23:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I like to search in it and access it offline (during a flight, for example).&lt;/p&gt;
&lt;p&gt;I’ve made a repository where documentation for Terraform core and all official Terraform providers automatically updates every night — &lt;a href=&quot;https://github.com/antonbabenko/terraform-docs-as-pdf&quot;&gt;https://github.com/antonbabenko/terraform-docs-as-pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hope you find it useful.&lt;/p&gt;
&lt;p&gt;Follow me on GitHub to see what I am working on next— &lt;a href=&quot;https://github.com/antonbabenko&quot;&gt;https://github.com/antonbabenko&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>terraform</category></item><item><title>AWS Console — Browse public S3 bucket (without asking for listing permission)</title><link>https://antonbabenko.com/aws-console-browse-public-s3-bucket-without-asking-for-listing-permission/</link><guid isPermaLink="true">https://antonbabenko.com/aws-console-browse-public-s3-bucket-without-asking-for-listing-permission/</guid><description>If you want to browse public S3 bucket to list the content of it and download files.</description><pubDate>Sat, 22 Dec 2018 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you want to browse public S3 bucket to list the content of it and download files. You can do so by just logging in to your AWS account and putting a bucket name after &lt;a href=&quot;https://s3.console.aws.amazon.com/s3/buckets/&quot;&gt;https://s3.console.aws.amazon.com/s3/buckets/&lt;/a&gt;, to be like this: &lt;a href=&quot;https://s3.console.aws.amazon.com/s3/buckets/ryft-public-sample-data/&quot;&gt;https://s3.console.aws.amazon.com/s3/buckets/ryft-public-sample-data/&lt;/a&gt; (random bucket name I found online)&lt;/p&gt;
&lt;p&gt;This is pretty useful if you don’t know exact filename you want to download from the bucket.&lt;/p&gt;
&lt;p&gt;You won’t be able to see bucket policy or change anything sensitive there, which is good. (“You don’t have access to view this configuration. Contact your account administrator to request access.”)&lt;/p&gt;
&lt;h2 id=&quot;read-more&quot;&gt;Read more&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/user-guide/block-public-access-account.html&quot;&gt;How Do I Edit Public Access Settings for All the S3 Buckets in an AWS Account?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html&quot;&gt;Using Amazon S3 Block Public Access&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Merry Christmas :)&lt;/p&gt;
</content:encoded><category>aws</category></item><item><title>modules.tf — Convert visual AWS diagram into Terraform configuration</title><link>https://antonbabenko.com/modules-tf-convert-visual-aws-diagram-into-terraform-configuration/</link><guid isPermaLink="true">https://antonbabenko.com/modules-tf-convert-visual-aws-diagram-into-terraform-configuration/</guid><description>I am excited to announce the public-release of modules.tf, which allows a conversion from visual AWS diagrams using cloudcraft.co to infrastructure code</description><pubDate>Mon, 19 Nov 2018 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I am excited to announce the public-release of modules.tf, which allows a conversion from visual AWS diagrams using &lt;a href=&quot;https://cloudcraft.co&quot;&gt;cloudcraft.co&lt;/a&gt; to infrastructure code using Terraform.&lt;/p&gt;
&lt;p&gt;See this in action (added 21.3.2019):&lt;/p&gt;
&lt;figure&gt;
&lt;iframe width=&quot;480&quot; height=&quot;270&quot; src=&quot;https://www.youtube.com/embed/F1Ax1zfZbiY?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; title=&quot;modules.tf demo — from visual diagrams (cloudcraft.co) to infrastructure as code using Terraform&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;modules.tf demo — from visual diagrams (cloudcraft.co) to infrastructure as code using Terraform&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;features&quot;&gt;Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Resources with arguments once specified during creation of diagrams are present in infrastructure code.&lt;/li&gt;
&lt;li&gt;Most popular EC2 types of resources are supported, more will be added in the future. VPC, Security group, Autoscaling Group, RDS, ELB, ALB, SNS, SQS, EC2 instances are implemented already.&lt;/li&gt;
&lt;li&gt;Many of relations between resources and their dependencies are respected in infrastructure code. For example, ELB will be created before Autoscaling Group, VPC will be created before Autoscaling Group and ELB.&lt;/li&gt;
&lt;li&gt;Incorrect relations are ignored. For example, ELB can’t depend on RDS even if a user draws a connection in a diagram.&lt;/li&gt;
&lt;li&gt;The orchestration of infrastructure modules is supported. Thanks to built-in features of Terragrunt.&lt;/li&gt;
&lt;li&gt;Dynamic values in &lt;code&gt;terraform.tfvars&lt;/code&gt; are supported! Dependent arguments are updated by a link. (Hack or know-how — you decide)&lt;/li&gt;
&lt;li&gt;The generated code is good for bootstrap.&lt;/li&gt;
&lt;li&gt;The generated code is not a boilerplate which you should rewrite entirely — it is a working solution for small and medium infrastructures.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Excited? Let’s walk through some components to see how to convert your AWS diagram to Terraform infrastructure code.&lt;/p&gt;
&lt;h3 id=&quot;first-cloudcraftco&quot;&gt;First, &lt;a href=&quot;https://cloudcraft.co/&quot;&gt;Cloudcraft.co&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Here are visual diagrams for 2-tier web-applications hosted on AWS:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Typical 2-tier web-application hosted on AWS may use a couple of auto-scaling groups, a couple of ELBs, RDS instances, CloudFront (CDN) and Route53 (DNS).&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/modules-tf-convert-visual-aws-diagram-into-terraform-configuration/0-IS7EXkVYzlGXW23P.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Typical 2-tier web-application hosted on AWS may use a couple of auto-scaling groups, a couple of ELBs, RDS instances, CloudFront (CDN) and Route53 (DNS).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Same 2-tier web-application with VPC and security groups added to the picture.&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/modules-tf-convert-visual-aws-diagram-into-terraform-configuration/1-cvX6Q1u8UGV_Z9pLl0i3xg.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Same 2-tier web-application with VPC and security groups added to the picture.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;terraform&quot;&gt;&lt;a href=&quot;https://www.terraform.io/&quot;&gt;Terraform&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Terraform is a well-known (and my favorite) tool to write, plan and create infrastructure as code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NB: modules.tf&lt;/strong&gt; generates infrastructure code and relies on &lt;a href=&quot;https://github.com/gruntwork-io/terragrunt&quot;&gt;Terragrunt&lt;/a&gt; for heavy-lifting. Terragrunt is used as a wrapper for Terraform modules, allows orchestration of dependencies, updates of arguments dynamically and keeps infrastructure configurations &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;DRY&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;terraform-aws-modules&quot;&gt;&lt;a href=&quot;https://github.com/terraform-aws-modules&quot;&gt;&lt;strong&gt;Terraform AWS modules&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/terraform-aws-modules&quot;&gt;https://github.com/terraform-aws-modules&lt;/a&gt; — these building blocks of AWS infrastructure are used by thousands of people around the globe. As a core maintainer, it was natural for me to bring them as first-class citizens to &lt;strong&gt;modules.tf.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/modules-tf-convert-visual-aws-diagram-into-terraform-configuration/1-ad4DtVUeNybkiQd5ysP-bQ.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;lets-try-it&quot;&gt;Let’s try it!&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;../../assets/blog/modules-tf-convert-visual-aws-diagram-into-terraform-configuration/1-ngOCiJBA2oISTa49_V7eYA.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Once you draw AWS infrastructure, which can be a single resource or several interconnected resources (as shown above), you should click “Export” and then “Terraform code export” in top-right corner.&lt;/li&gt;
&lt;li&gt;Zip-archive with all configurations and extra files will be generated dynamically.&lt;/li&gt;
&lt;li&gt;Extract content of this archive into your working directory and read &lt;code&gt;README.md&lt;/code&gt; which is inside. It contains all information about dependencies, and how to create the desired infrastructure.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;modules.tf is open-source, MIT licensed. Source code: &lt;a href=&quot;https://github.com/antonbabenko/modules.tf-lambda&quot;&gt;https://github.com/antonbabenko/modules.tf-lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;modules.tf is using AWS Lambda and running Serverless framework with Python 3.6&lt;/li&gt;
&lt;li&gt;modules.tf is free for all. It is available for all types of users on &lt;a href=&quot;https://cloudcraft.co/&quot;&gt;cloudcraft.co&lt;/a&gt; (including free users)&lt;/li&gt;
&lt;li&gt;modules.tf has support for a limited amount of AWS resources, because we don’t have Terraform AWS modules for all of them (yet).&lt;/li&gt;
&lt;li&gt;This is the first iteration of public release, and definitely contain bugs. I couldn’t imagine what kind of scenarios people were building, so PLEASE report problems on &lt;a href=&quot;https://github.com/antonbabenko/modules.tf-lambda/issues&quot;&gt;GitHub issues&lt;/a&gt;. I am especially interested in learning about any usability issues or problems with the developer experience. What makes you stuck? What is not logical? What is simply missing? What is useless? What would be AMAZING?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://modules.tf/&quot;&gt;modules.tf&lt;/a&gt; — is also a static web-site, but I have not updated it yet :)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Go to &lt;a href=&quot;https://cloudcraft.co&quot;&gt;cloudcraft.co&lt;/a&gt;, open an account (free is fine, too), draw your AWS infrastructure, click “Export” then “Terraform code export”, download and unzip archive, read &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Atlantis + Terragrunt = ?</title><link>https://antonbabenko.com/atlantis-terragrunt-february-2018/</link><guid isPermaLink="true">https://antonbabenko.com/atlantis-terragrunt-february-2018/</guid><description>TLDR; Does Atlantis work with Terragrunt? The short answer - pretty much, yes (updated 18.9.2018).</description><pubDate>Wed, 21 Feb 2018 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TLDR; Does Atlantis work with Terragrunt? The short answer — pretty much, yes (updated 18.9.2018).&lt;/p&gt;
&lt;p&gt;UPD (7.5.2019): Published new repository &lt;a href=&quot;https://github.com/antonbabenko/terragrunt-reference-architecture&quot;&gt;Terragrunt Reference Architecture&lt;/a&gt; where the very real code is published. Check it out if you want to see Terragrunt in action.&lt;/p&gt;
&lt;p&gt;UPD (18.9.2018): Updated blog post, added a link to &lt;a href=&quot;https://www.runatlantis.io/guide/atlantis-yaml-use-cases.html#terragrunt&quot;&gt;Terragrunt use-case&lt;/a&gt; in Atlantis documentation.&lt;/p&gt;
&lt;p&gt;UPD (25.5.2018): I have just published Terraform module for running Atlantis on AWS Fargate — &lt;a href=&quot;https://github.com/terraform-aws-modules/terraform-aws-atlantis&quot;&gt;https://github.com/terraform-aws-modules/terraform-aws-atlantis&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;When I wrote this blog post in February 2018, Atlantis didn’t have proper integration with Terragrunt and a lot of things have changed since that time. Please check &lt;a href=&quot;https://www.runatlantis.io/guide/atlantis-yaml-use-cases.html#terragrunt&quot;&gt;Terragrunt use-case&lt;/a&gt; in Atlantis documentation because some of the following may be outdated.&lt;/p&gt;
&lt;p&gt;I go through my experience of integrating these two tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/gruntwork-io/terragrunt&quot;&gt;Terragrunt&lt;/a&gt; — Thin wrapper for Terraform that provides extra tools for working with multiple Terraform modules.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.runatlantis.io&quot;&gt;Atlantis&lt;/a&gt; — A unified workflow for collaborating on Terraform through GitHub and GitLab.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both of these projects are open-source and have decent community (1000 stars and 39 contributors — Terragrunt; 600 stars and 13 contributors — Atlantis).&lt;/p&gt;
&lt;h2 id=&quot;terragrunt&quot;&gt;Terragrunt&lt;/h2&gt;
&lt;p&gt;I use Terragrunt because I like the way my Terraform code is structured and I’d like to keep it DRY. I use &lt;a href=&quot;https://registry.terraform.io/modules/terraform-aws-modules/&quot;&gt;Terraform AWS modules&lt;/a&gt; and promote reusability whenever possible (internally — with private infrastructure modules shared between teams; externally — with resource modules published in &lt;a href=&quot;https://github.com/terraform-aws-modules/&quot;&gt;terraform-aws-modules&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Most of experienced users I know tend to not mix Terraform configurations (&lt;em&gt;*.tf&lt;/em&gt;) with the actual values (&lt;em&gt;*.tfvars&lt;/em&gt;) when working on medium/large setups. &lt;strong&gt;&lt;em&gt;I like to hide Terraform code from users&lt;/em&gt;&lt;/strong&gt; by just giving them access to values they should control (of course, they contribute to Terraform configurations, but it happens not in the same place they play with values). Terragrunt helps with this quite a lot.&lt;/p&gt;
&lt;h3 id=&quot;atlantis&quot;&gt;Atlantis&lt;/h3&gt;
&lt;p&gt;Once code is pushed, developer opens a pull-request and pull-request reviewers can see Terraform plan before merge. This is the simplest functionality I wanted to achieve with Atlantis.&lt;/p&gt;
&lt;p&gt;Now, let me explain a project structure and see whether Atlantis can help.&lt;/p&gt;
&lt;h3 id=&quot;project-setup&quot;&gt;Project setup:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Different projects are located in directories like &lt;em&gt;project-env/region/layer&lt;/em&gt;. For example: &lt;em&gt;user-stage/eu-west-1/vpc&lt;/em&gt; — has code for VPC &lt;strong&gt;&lt;em&gt;layer&lt;/em&gt;&lt;/strong&gt; deployed in eu-west-1 &lt;strong&gt;&lt;em&gt;region&lt;/em&gt;&lt;/strong&gt; used by user &lt;strong&gt;&lt;em&gt;project&lt;/em&gt;&lt;/strong&gt; in staging &lt;strong&gt;&lt;em&gt;environment&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Each project has its own set of AWS IAM roles which should be assumed to be able to change infrastructure (readonly role — to do &lt;strong&gt;&lt;em&gt;terragrunt plan&lt;/em&gt;&lt;/strong&gt;, power-user role — to do &lt;strong&gt;&lt;em&gt;terragrunt apply&lt;/em&gt;&lt;/strong&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;lets-get-it-working-together&quot;&gt;Let’s get it working together…&lt;/h2&gt;
&lt;p&gt;Following setup instructions described on Atlantis project page was pretty easy to get a Terraform project of &lt;strong&gt;&lt;em&gt;supported&lt;/em&gt;&lt;/strong&gt; structure to work. My project structure was not supported out of the box, and in order to make Atlantis to work with my structure I used to come up with some hacks.&lt;/p&gt;
&lt;h3 id=&quot;which-binary-to-run&quot;&gt;Which binary to run?&lt;/h3&gt;
&lt;p&gt;It is possible to specify version of Terraform using &lt;strong&gt;&lt;em&gt;terraform_version&lt;/em&gt;&lt;/strong&gt; parameter in configuration file (&lt;em&gt;atlantis.yaml&lt;/em&gt;), but not complete path to a binary or shell wrapper, for my case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution #1&lt;/strong&gt;— make &lt;strong&gt;terraform&lt;/strong&gt; an alias to &lt;strong&gt;terragrunt&lt;/strong&gt; wrapper script and tell wrapper script where correct terraform actually is:&lt;em&gt;alias&lt;/em&gt; terraform=/usr/local/bin/terragrunt.sh&lt;/p&gt;
&lt;p&gt;# terragrunt.sh&lt;br&gt;
&lt;em&gt;export&lt;/em&gt; TERRAGRUNT_TFPATH=/usr/local/bin/terraform&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution #2&lt;/strong&gt; — change &lt;strong&gt;&lt;em&gt;PATH&lt;/em&gt;&lt;/strong&gt; environment variable to resolve a &lt;strong&gt;terragrunt&lt;/strong&gt; shell wrapper instead of the correct terraform binary and rename shell wrapper to &lt;strong&gt;terraform&lt;/strong&gt;&lt;em&gt;.mv&lt;/em&gt; /usr/local/bin/terragrunt.sh /usr/local/my-bin/terraform&lt;br&gt;
&lt;em&gt;export PATH=&lt;/em&gt;/usr/local/my-bin:$PATH&lt;/p&gt;
&lt;p&gt;# terragrunt.sh&lt;br&gt;
&lt;em&gt;export&lt;/em&gt; TERRAGRUNT_TFPATH=/usr/local/bin/terraform&lt;/p&gt;
&lt;p&gt;I leave to you to decide which solution is less ugly :)&lt;/p&gt;
&lt;p&gt;Ideally, there should be a good way to specify path to a binary through configuration.&lt;/p&gt;
&lt;h3 id=&quot;workspaces-no-please&quot;&gt;Workspaces? No, please!&lt;/h3&gt;
&lt;p&gt;I don’t use &lt;a href=&quot;https://www.terraform.io/docs/state/workspaces.html&quot;&gt;Terraform workspaces&lt;/a&gt; and don’t want any tool to decide this for me. This should be controllable and should be disabled by default on my opinion. In Atlantis it is not possible to disable this.&lt;/p&gt;
&lt;h3 id=&quot;arguments-are-appended&quot;&gt;Arguments are appended&lt;/h3&gt;
&lt;p&gt;Similar to workspaces, Atlantis assumes I use terraform and appends some arguments to commands it run (eg, &lt;strong&gt;&lt;em&gt;-no-color&lt;/em&gt;&lt;/strong&gt;). In some cases it works well, but it expects a single string as an output of &lt;strong&gt;&lt;em&gt;terraform version&lt;/em&gt;&lt;/strong&gt; so I used to make another hack in my shell wrapper to make Atlantis happy:# terraform version should return single string, but terragrunt outputs a bit more&lt;br&gt;
&lt;strong&gt;if [[&lt;/strong&gt; &quot;$1&quot; &lt;strong&gt;==&lt;/strong&gt; &quot;version&quot; &lt;strong&gt;]]&lt;/strong&gt;; &lt;strong&gt;then&lt;/strong&gt;&lt;br&gt;
 &lt;em&gt;echo&lt;/em&gt; &quot;Terraform v0.11.3&quot;&lt;br&gt;
 &lt;em&gt;exit&lt;/em&gt; 0&lt;br&gt;
&lt;strong&gt;fi&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;configuration-file&quot;&gt;Configuration file&lt;/h3&gt;
&lt;p&gt;This is the main show stopper. Atlantis expects &lt;em&gt;atlantis.yaml&lt;/em&gt; to be placed in a project root. In a single repository we have dozens of projects at the same time, so there should be at least the same amount of configuration files, which is simply not acceptable for us. 100% of my projects could be served with the same &lt;em&gt;atlantis.yaml&lt;/em&gt; file. Remember, Terragrunt helps to make infrastructure configurations DRY.&lt;/p&gt;
&lt;p&gt;It would be good to support some inheritance of configuration file, or have an environment variable, or command line argument to specify the configuration file to use.&lt;/p&gt;
&lt;h3 id=&quot;project-structure&quot;&gt;Project structure&lt;/h3&gt;
&lt;p&gt;Atlantis supports project structures described &lt;a href=&quot;https://github.com/runatlantis/atlantis#project-structure&quot;&gt;here&lt;/a&gt;. I have opened an &lt;a href=&quot;https://github.com/hootsuite/atlantis-tests/issues/523#issue-296007866&quot;&gt;issue 523&lt;/a&gt; which describes desired project structure and will be glad to elaborate as time allows.&lt;/p&gt;
&lt;p&gt;I think Atlantis should have a list of &lt;em&gt;whitelisted&lt;/em&gt; and &lt;em&gt;blacklisted&lt;/em&gt; directories where Atlantis has to check for changes and run commands.&lt;/p&gt;
&lt;p&gt;Wouldn’t it be great to be able to write &lt;strong&gt;&lt;em&gt;atlantis apply user-stage/eu-west-1&lt;/em&gt;&lt;/strong&gt; it a pull-request comment and it understands what is &lt;strong&gt;&lt;em&gt;a project&lt;/em&gt;&lt;/strong&gt; and make things happen. I think it is an awesome goal.&lt;/p&gt;
&lt;h3 id=&quot;more-points-and-ideas-for-another-time&quot;&gt;More points and ideas for another time:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Expose terraform execution via API to be able to trigger same commands using other webhooks (eg, in Slack messages — &lt;strong&gt;/atlantis apply user-stage/eu-west-1&lt;/strong&gt; or &lt;strong&gt;/atlantis output user-stage/eu-west-1&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Assume role support is tightly connected to Terraform at the moment and does not work like this with Terragrunt which support CLI argument and environment variables.&lt;/li&gt;
&lt;li&gt;Assume role with MFA is impossible at the moment. Add integration with Slack or Twilio to send request to a person who has requested &lt;strong&gt;atlantis apply&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Reformat output of &lt;em&gt;terraform plan&lt;/em&gt; which is pasted as a comment into collapsable list (using &lt;strong&gt;“&amp;lt;details&amp;gt;&amp;lt;summary&amp;gt;Summary&amp;lt;/summary&amp;gt;Details…&amp;lt;/details&amp;gt;”&lt;/strong&gt; tags)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I want to say that &lt;a href=&quot;https://www.runatlantis.io&quot;&gt;Atlantis&lt;/a&gt; is a great young project which is very much needed in the Terraform community. Let’s contribute our time to it (by trying it at least) and make it even better— &lt;a href=&quot;https://github.com/runatlantis/atlantis&quot;&gt;https://github.com/runatlantis/atlantis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Stay in touch: &lt;a href=&quot;https://twitter.com/antonbabenko&quot;&gt;https://twitter.com/antonbabenko&lt;/a&gt;at&lt;/p&gt;
</content:encoded><category>terraform</category></item><item><title>Make Terraform faster — skip unnecessary checks (AWS and S3)</title><link>https://antonbabenko.com/make-terraform-faster-skip-unnecessary-checks-aws-s3/</link><guid isPermaLink="true">https://antonbabenko.com/make-terraform-faster-skip-unnecessary-checks-aws-s3/</guid><description>Working with Terraform on a daily basis I feel it can be a good idea to share some small tips I have. Let me start with this one.</description><pubDate>Sat, 27 Jan 2018 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Working with Terraform on a daily basis I feel it can be a good idea to share some small tips I have. Let me start with this one.&lt;/p&gt;
&lt;p&gt;Initializing remote state S3 backend is pretty fast operation, but it can be even faster if you disable all unnecessary checks:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;https://antonbabenko.com/_astro/ec.wshpv.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://antonbabenko.com/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;hcl&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;terraform&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;backend&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&gt;&quot;s3&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;encrypt&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;region&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;         &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;            &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;bucket&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;         &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;dynamodb_table&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&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;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;skip_metadata_api_check&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;     &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;skip_region_validation&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;skip_credentials_validation&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;provider&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&gt;&quot;aws&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;region&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;skip_requesting_account_id&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#8B949E&quot;&gt;# this can be tricky&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;skip_get_ec2_platforms&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;skip_metadata_api_check&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;     &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;skip_region_validation&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt;      &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&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 class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3&quot;&gt;skip_credentials_validation&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#FFA657&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF&quot;&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 style=&quot;--0:#E6EDF3&quot;&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 aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;terraform {  backend &amp;quot;s3&amp;quot; {    encrypt        = true    region         = &amp;quot;...&amp;quot;    key            = &amp;quot;...&amp;quot;    bucket         = &amp;quot;...&amp;quot;    dynamodb_table = &amp;quot;...&amp;quot;    skip_metadata_api_check     = true    skip_region_validation      = true    skip_credentials_validation = true  }}provider &amp;quot;aws&amp;quot; {  region = &amp;quot;...&amp;quot;  skip_requesting_account_id  = true # this can be tricky  skip_get_ec2_platforms      = true  skip_metadata_api_check     = true  skip_region_validation      = true  skip_credentials_validation = true}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Skipping region validation on S3 backends became available in Terraform 0.11.2 (see &lt;a href=&quot;https://github.com/hashicorp/terraform/pull/16757&quot;&gt;PR&lt;/a&gt;): &lt;code&gt;skip_region_validation = true&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;I didn’t spend too much time measuring time difference, but it certainly reduces time required for initializing S3 backend by 20%&lt;/strong&gt;. This is not a big deal if you have just a few remote states and you run &lt;em&gt;terraform init&lt;/em&gt; seldom.&lt;/p&gt;
&lt;p&gt;I usually use Terragrunt with lots of isolated state files and run &lt;em&gt;terragrunt validate-all&lt;/em&gt; on CI. It saves second here, second there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;More importantly skipping these checks will allow you to run &lt;em&gt;terraform plan&lt;/em&gt; without having internet access (on the plane, for eg).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Follow me on Twitter — &lt;a href=&quot;https://twitter.com/antonbabenko&quot;&gt;@antonbabenko&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Read more:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.terraform.io/docs/providers/aws/index.html#argument-reference&quot;&gt;AWS provider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.terraform.io/docs/backends/types/s3.html#configuration-variables&quot;&gt;S3 backend configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded><category>terraform</category><category>aws</category></item><item><title>Terrapin: making good Terraform modules</title><link>https://antonbabenko.com/terrapin-making-good-terraform-modules/</link><guid isPermaLink="true">https://antonbabenko.com/terrapin-making-good-terraform-modules/</guid><description>You are reading the second blog post in the series about using Terraform continuously. In the previous one I outlined common traits in Terraform modules.</description><pubDate>Sat, 15 Jul 2017 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You are reading the second blog post in the series about using Terraform continuously. In the previous one I outlined &lt;a href=&quot;https://antonbabenko.com/using-terraform-continuously-common-traits-in-modules/&quot;&gt;common traits in Terraform modules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once more, the list of keywords describing common traits is: not clean code, inextensible module features, tests, defaults, examples, documentation, lifecycle, versioning, security.&lt;/p&gt;
&lt;p&gt;Today, let me introduce a new open-source project called &lt;a href=&quot;https://github.com/antonbabenko/terrapin&quot;&gt;Terrapin&lt;/a&gt;. In a nutshell Terrapin is a &lt;strong&gt;Terraform modules generator.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The ultimate goal of Terrapin is to automatically generate structured, tested, easy-extendable and well-documented Terraform modules with predictable versioning strategy.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Being a new project now it can only generate structured, extendable, documented and testable code (all with &lt;em&gt;varying level of success&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;There is already a &lt;a href=&quot;https://github.com/antonbabenko/terrapin/blob/master/README.md&quot;&gt;documentation&lt;/a&gt; and &lt;a href=&quot;https://github.com/antonbabenko/terrapin/tree/master/modules/aws_bastion_s3_keys&quot;&gt;sample module&lt;/a&gt; available.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antonbabenko/terrapin/issues/new&quot;&gt;Open an issue&lt;/a&gt; on github, if there is anything you want to ask or leave a feedback.&lt;/p&gt;
&lt;p&gt;Thank you!&lt;/p&gt;
</content:encoded><category>terraform</category></item><item><title>Using Terraform continuously — Common traits in modules</title><link>https://antonbabenko.com/using-terraform-continuously-common-traits-in-modules/</link><guid isPermaLink="true">https://antonbabenko.com/using-terraform-continuously-common-traits-in-modules/</guid><description>This is the first blog post in the series about using Terraform continuously, which I am going to publish over the next few weeks.</description><pubDate>Thu, 08 Jun 2017 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is the first blog post in the series about using Terraform continuously, which I am going to publish over the next few weeks.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;UPD (15.07.2017): I wrote another blog post in the series —&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;&lt;a href=&quot;https://antonbabenko.com/terrapin-making-good-terraform-modules/&quot;&gt;Terrapin: making good Terraform modules&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am &lt;a href=&quot;https://www.linkedin.com/in/antonbabenko/&quot;&gt;Anton Babenko&lt;/a&gt; (&lt;a href=&quot;https://github.com/antonbabenko&quot;&gt;github.com/antonbabenko&lt;/a&gt;) one of the active maintainers and contributors of several &lt;a href=&quot;https://github.com/terraform-community-modules/&quot;&gt;terraform-community-modules&lt;/a&gt;. Since I started using Terraform back in mid-2015, I have talked to many people (online and during conferences) who are using Terraform to build their infrastructures, so it feels natural to write some blog posts about what I am experiencing.&lt;/p&gt;
&lt;p&gt;I want to hear feedback, so please leave a comment below if you have something to say (or agree, or disagree). You can also send me an email and I will reply to you.&lt;/p&gt;
&lt;p&gt;Let’s start with important things first — &lt;strong&gt;Terraform modules&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;what-is-terraform-module&quot;&gt;What is Terraform module?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Modules in Terraform are self-contained packages of Terraform configurations that are managed as a group. Modules are used to create reusable components, improve organization, and to treat pieces of infrastructure as a black box.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;(via&lt;/em&gt; &lt;a href=&quot;https://www.terraform.io/intro/getting-started/modules.html&quot;&gt;&lt;em&gt;https://www.terraform.io/intro/getting-started/modules.html&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Overall there are around 15 actively used Terraform community modules and lots of custom built modules (often niche one’s). Those modules were created to solve common scenarios — create network components (VPC, subnets, NAT gateways, etc) and various types of resources (RDS, ECS, Elasticache, etc).&lt;/p&gt;
&lt;h2 id=&quot;common-traits-in-terraform-modules&quot;&gt;Common traits in Terraform modules&lt;/h2&gt;
&lt;h3 id=&quot;not-clean-code&quot;&gt;Not clean code&lt;/h3&gt;
&lt;p&gt;Quote by Nick Dellamaggiore, Infrastructure Lead at Coursera, taken from the book «&lt;a href=&quot;https://www.amazon.com/dp/1491977086/ref=cm_sw_r_cp_dp_T2_3KzozbQBG6TKR&quot;&gt;Terraform: Up &amp;amp; Running&lt;/a&gt;» by &lt;a href=&quot;https://blog.gruntwork.io/@brikis98&quot;&gt;Yevgeniy Brikman&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If I look at a single file and it’s written by 10 different engineers, it should be almost indistinguishable which part was written by which person. To me, that is clean code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In collaborative environment following coding style, naming conventions and agreed best-practices is important, because it lets others contribute faster.&lt;/p&gt;
&lt;p&gt;Clean code is also aggregative term and is related to other traits listed below.&lt;/p&gt;
&lt;p&gt;There is no single set of Terraform configurations best-practices yet, which most of Terraform users know and follow regularly. List of traits below may initiate discussions, which may turn into statements, which may turn into “Terraform modules best-practices”. Who knows?&lt;/p&gt;
&lt;h3 id=&quot;module-features&quot;&gt;Module features&lt;/h3&gt;
&lt;p&gt;Modules should be &lt;em&gt;extensible&lt;/em&gt;. Often developers are taking single-purpose design principle too literally, which leads to not-extensible set of features which makes it impossible to extend without forking it (for example, there are 128 forks of very popular &lt;a href=&quot;https://github.com/terraform-community-modules/tf_aws_vpc&quot;&gt;terraform-community-modules/tf_aws_vpc&lt;/a&gt; module). Good modules are as flexible as Terraform allows us to do without complicated hacks. For example, if there is a security group in the module, then it should be created as two resources — &lt;em&gt;aws_security_group&lt;/em&gt; and &lt;em&gt;aws_security_group_rule&lt;/em&gt;, so that user can append some rules, if necessary. One of the limitation which is still hard to do nicely is related to resource tags.&lt;/p&gt;
&lt;h3 id=&quot;tests&quot;&gt;&lt;strong&gt;Tests&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;It is good practice in software development to include tests for your code and run them often. In my opinion it is still not well-covered area when it comes to Terraform configurations even though there are several tools which can help doing this. At the very least you should have a CI system run “&lt;em&gt;terraform plan&lt;/em&gt;” with sane defaults. Apply code to create resources and then destroy them is more tricky, and it is &lt;em&gt;fine&lt;/em&gt; to do it less often, but still automatically.&lt;/p&gt;
&lt;p&gt;Sometimes there are syntax errors in completed code and in pull requests, which basic “&lt;em&gt;terraform plan”&lt;/em&gt; can catch.&lt;/p&gt;
&lt;h3 id=&quot;defaults&quot;&gt;&lt;strong&gt;Defaults&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Modules should have &lt;em&gt;sane&lt;/em&gt; defaults. By sane I mean that it should be possible to use the module providing just essentially important arguments and use module’s defaults for the rest.&lt;/p&gt;
&lt;h3 id=&quot;examples&quot;&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;It is helpful for the users of the module to be able to see complete examples of the module and be able to run plan and apply on it effortlessly.&lt;/p&gt;
&lt;h3 id=&quot;documentation&quot;&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;…and deficit of it. Documentation should outline the purpose of a module, which resources it creates, how to use the module, required version of Terraform, how to report issues, etc.&lt;/p&gt;
&lt;h3 id=&quot;lifecycle&quot;&gt;&lt;strong&gt;Lifecycle&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Module resources lifecycle should implement &lt;em&gt;create&lt;/em&gt;, &lt;em&gt;read&lt;/em&gt;, &lt;em&gt;update&lt;/em&gt; and &lt;em&gt;destroy&lt;/em&gt; functions. Resources creation is easy to handle in code, and this is often the main reason why the module was created. Read and destroy functions are also easy, because once created you should be able to read and destroy resources (bear with me “easy” is subjective). Update function is the most complicated, because of variety of resources and ways how they can be updated (replace in place, replace gradually, replace all at once, blue-green, etc). Modules are almost never implemented with a clear path through updates.&lt;/p&gt;
&lt;h3 id=&quot;versioning&quot;&gt;&lt;strong&gt;Versioning&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Infrastructure as code should always be versioned. Versioning strategy and available releases should be documented. It is handy to be able to use specific release instead of using “master” branch for your critical infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;security&quot;&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;You should always attempt to use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Principle_of_least_privilege&quot;&gt;principle of least privilege&lt;/a&gt; when working with infrastructure. It is especially important when multiple operators handle infrastructure in self-service way. Module should specify set of least privileges it uses.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For now this is it, and in the next blog post I want to propose some ideas to the points listed above.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://www.linkedin.com/in/tfhartmann/&quot;&gt;Tim Hartmann&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/shuff/&quot;&gt;Steve Huff&lt;/a&gt; and &lt;a href=&quot;https://www.linkedin.com/in/alexmatsuk/&quot;&gt;Alex Matsuk&lt;/a&gt; for initial review and proof-reading.&lt;/p&gt;
</content:encoded><category>terraform</category></item><item><title>Running &quot;terraform fmt&quot; as pre-commit git hook [archived]</title><link>https://antonbabenko.com/terraform-fmt-pre-commit-git-hook-2016-archived/</link><guid isPermaLink="true">https://antonbabenko.com/terraform-fmt-pre-commit-git-hook-2016-archived/</guid><description>This is my second blog post related to Terraform. Previous one was about how I structure Terraform configuration, which got some interesting feedback on</description><pubDate>Tue, 27 Sep 2016 15:10:00 GMT</pubDate><content:encoded>&lt;p&gt;This is my second blog post related to Terraform. Previous one was about &lt;a href=&quot;https://antonbabenko.com/how-i-structure-terraform-configurations-2016-archived/&quot;&gt;how I structure Terraform configuration&lt;/a&gt;, which got some interesting feedback on &lt;a href=&quot;https://www.reddit.com/r/devops/comments/53sijz/how_do_you_structure_terraform_configurations/&quot;&gt;reddit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This time I want to share &lt;a href=&quot;http://pre-commit.com/&quot;&gt;pre-commit&lt;/a&gt; hook which runs &quot;&lt;em&gt;terraform fmt&quot;&lt;/em&gt; on your Terraform configuration files. Nothing more, nothing less.&lt;/p&gt;
&lt;p&gt;Code is here - &lt;a href=&quot;https://github.com/antonbabenko/pre-commit-terraform&quot;&gt;antonbabenko/pre-commit-terraform&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>pre-commit</category><category>terraform</category><category>Getting Started</category></item><item><title>How I structure Terraform configurations? [archived]</title><link>https://antonbabenko.com/how-i-structure-terraform-configurations-2016-archived/</link><guid isPermaLink="true">https://antonbabenko.com/how-i-structure-terraform-configurations-2016-archived/</guid><description>Note that this blog post was originally written in September 2016, and many things has been changed since then.</description><pubDate>Wed, 21 Sep 2016 17:05:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Note that this blog post was originally written in September 2016, and many things has been changed since then.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this blog post I want to describe one of the ways I like to structure&lt;br&gt;
Terraform configurations which can be used to manage medium and large&lt;br&gt;
infrastructures with resources span multiple AWS accounts (for eg,&lt;br&gt;
development, staging, production) and multiple regions.&lt;/p&gt;
&lt;p&gt;Here I follow so called &quot;layered infrastructure principle&quot;.&lt;/p&gt;
&lt;p&gt;There was very interesting talk about running &lt;a href=&quot;https://www.youtube.com/watch?v=RldRDryLiXs&quot;&gt;Terraform At Scale&lt;/a&gt; by&lt;br&gt;
Calvin French-Owen (Co-Founder and CTO at Segment) at HashiConf 2016,&lt;br&gt;
where he was describing same types of challenges I was solving in my setup.&lt;/p&gt;
&lt;h2 id=&quot;infrastructure-review&quot;&gt;Infrastructure review:&lt;/h2&gt;
&lt;p&gt;Resources are grouped into layers, where each layer consists only of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;global resources (IAM, Cloudfront, Cloudtrail, Config, Route53 zones)&lt;/li&gt;
&lt;li&gt;or regional resources (EC2, VPC, Codedeploy, Cloudformation)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Developer can interact with a single layer, which is identifiable by combination of arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html&quot;&gt;AWS account alias&lt;/a&gt; (&lt;code&gt;company-dev&lt;/code&gt;, for example)&lt;/li&gt;
&lt;li&gt;Region name (&lt;code&gt;eu-west-1&lt;/code&gt;, for example)&lt;/li&gt;
&lt;li&gt;Layer name (&lt;code&gt;global&lt;/code&gt;, &lt;code&gt;shared&lt;/code&gt;, &lt;code&gt;application&lt;/code&gt;, for example)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;directory-structure&quot;&gt;Directory structure:&lt;/h2&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;https://antonbabenko.com/_astro/ec.wshpv.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://antonbabenko.com/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;├── accounts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── company-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 style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── application.eu-west-1.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── global.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── shared.eu-west-1.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── company-cicd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── cicd.eu-west-1.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── global.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── company-legacy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── legacy.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── company-prod&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── application.eu-west-1.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── global.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── shared.eu-west-1.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   └── company-staging&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       ├── application.eu-west-1.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       ├── global.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       └── shared.eu-west-1.tfvars&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&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 style=&quot;--0:#e6edf3&quot;&gt;│   ├── company-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 style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── application&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── global&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── shared&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │       ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │       ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │       └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── company-infra&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── cicd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   ├── iam.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   │   └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── global -&amp;gt; ../../company-dev/global&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── company-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 style=&quot;--0:#e6edf3&quot;&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 style=&quot;--0:#e6edf3&quot;&gt;│   │       ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │       ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │       └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── company-prod&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── application -&amp;gt; ../../company-staging/application&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── global -&amp;gt; ../../company-staging/global&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── shared -&amp;gt; ../../company-staging/shared&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   └── company-staging&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       ├── application&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       │   ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       │   ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       │   └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       ├── global&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       │   ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       │   ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       │   └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       └── shared&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│           ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│           ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│           └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;├── modules&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── application&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── codedeploy.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── global&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── cloudtrail.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── iam.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── main.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   ├── shared&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── bastion.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── networking.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── route53.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   ├── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   │   └── vpc_peering.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│   └── typical_service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       ├── codedeploy.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       ├── output.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       ├── service.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;│       └── variables.tf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3&quot;&gt;└── terraform.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 aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;.├── accounts│   ├── company-dev│   │   ├── application.eu-west-1.tfvars│   │   ├── global.tfvars│   │   └── shared.eu-west-1.tfvars│   ├── company-cicd│   │   ├── cicd.eu-west-1.tfvars│   │   └── global.tfvars│   ├── company-legacy│   │   └── legacy.tfvars│   ├── company-prod│   │   ├── application.eu-west-1.tfvars│   │   ├── global.tfvars│   │   └── shared.eu-west-1.tfvars│   └── company-staging│       ├── application.eu-west-1.tfvars│       ├── global.tfvars│       └── shared.eu-west-1.tfvars├── layers│   ├── company-dev│   │   ├── application│   │   │   ├── main.tf│   │   │   ├── output.tf│   │   │   └── variables.tf│   │   ├── global│   │   │   ├── main.tf│   │   │   ├── output.tf│   │   │   └── variables.tf│   │   └── shared│   │       ├── main.tf│   │       ├── output.tf│   │       └── variables.tf│   ├── company-infra│   │   ├── cicd│   │   │   ├── iam.tf│   │   │   ├── main.tf│   │   │   ├── output.tf│   │   │   └── variables.tf│   │   └── global -&gt; ../../company-dev/global│   ├── company-static│   │   └── static│   │       ├── main.tf│   │       ├── output.tf│   │       └── variables.tf│   ├── company-prod│   │   ├── application -&gt; ../../company-staging/application│   │   ├── global -&gt; ../../company-staging/global│   │   └── shared -&gt; ../../company-staging/shared│   └── company-staging│       ├── application│       │   ├── main.tf│       │   ├── output.tf│       │   └── variables.tf│       ├── global│       │   ├── main.tf│       │   ├── output.tf│       │   └── variables.tf│       └── shared│           ├── main.tf│           ├── output.tf│           └── variables.tf├── modules│   ├── application│   │   ├── codedeploy.tf│   │   ├── output.tf│   │   └── variables.tf│   ├── global│   │   ├── cloudtrail.tf│   │   ├── iam.tf│   │   ├── main.tf│   │   ├── output.tf│   │   └── variables.tf│   ├── shared│   │   ├── bastion.tf│   │   ├── networking.tf│   │   ├── output.tf│   │   ├── route53.tf│   │   ├── variables.tf│   │   └── vpc_peering.tf│   └── typical_service│       ├── codedeploy.tf│       ├── output.tf│       ├── service.tf│       └── variables.tf└── terraform.sh&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;notes&quot;&gt;Notes:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;accounts/{aws_alias}/*.tfvars&lt;/code&gt; - values&lt;/li&gt;
&lt;li&gt;&lt;code&gt;layers/{aws_alias}/{layer_name}/*.tf&lt;/code&gt; - Terraform configurations. Normally it contains &lt;code&gt;module&lt;/code&gt; invocations and &lt;code&gt;terraform_remote_state&lt;/code&gt; configurations to higher layers (global and shared).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;layers/company-prod/*&lt;/code&gt; - is a symlink to &lt;code&gt;layers/company-staging/*&lt;/code&gt; to reduce chance of configuration drift&lt;/li&gt;
&lt;li&gt;&lt;code&gt;modules&lt;/code&gt; - Terraform modules (typically invoked from multiple AWS accounts with various values)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform.sh&lt;/code&gt; - Terraform wrapper script (or &lt;a href=&quot;https://github.com/gruntwork-io/terragrunt&quot;&gt;Terragrunt&lt;/a&gt;), which sets correct working directory and load correct &lt;code&gt;tfvars&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;pros&quot;&gt;Pros:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Easy to navigate infrastructure as code&lt;/li&gt;
&lt;li&gt;Outputs of higher level layers are available as read-only values to layers below&lt;/li&gt;
&lt;li&gt;Full set of Terraform features (commands and arguments) is supported&lt;/li&gt;
&lt;li&gt;Versioned layers based on same &lt;code&gt;tfvars&lt;/code&gt; can be implemented (A/B testing, blue-green deployment)&lt;/li&gt;
&lt;li&gt;Very easy to integrate with CI pipeline&lt;/li&gt;
&lt;li&gt;Locking is implemented using &lt;a href=&quot;https://github.com/gruntwork-io/terragrunt&quot;&gt;Terragrunt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cons&quot;&gt;Cons:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;May be hard to limit permissions for Terraform users between state files inside one AWS account (all states files are readable by users in that account, by default)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>terraform</category></item><item><title>Lessons learned from AWS and Varnish [archived]</title><link>https://antonbabenko.com/lessons-learned-from-aws-varnish-2016-archived/</link><guid isPermaLink="true">https://antonbabenko.com/lessons-learned-from-aws-varnish-2016-archived/</guid><description>In this post I want to share a few thoughts I figured out during my latest assignment where I used to design and then implement architecture for e-commerce</description><pubDate>Mon, 09 Mar 2015 09:18:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post I want to share a few thoughts I figured out during my latest assignment where I used to design and then implement architecture for e-commerce web-site for pet owners.&lt;/p&gt;
&lt;p&gt;The project was built using Symfony2 PHP framework with rather high demands on code quality as well as performance for end-users.&lt;/p&gt;
&lt;p&gt;There was &lt;a href=&quot;https://en.wikipedia.org/wiki/Multitier_architecture#Three-tier_architecture&quot;&gt;multi-tier architecture&lt;/a&gt; consisting of several Amazon web-services like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Route 53 (DNS)&lt;/li&gt;
&lt;li&gt;Cloudfront (CDN)&lt;/li&gt;
&lt;li&gt;Elastic load-balancer&lt;/li&gt;
&lt;li&gt;Front-caching layer (EC2 instances with Varnish)&lt;/li&gt;
&lt;li&gt;Internal elastic load-balancer&lt;/li&gt;
&lt;li&gt;Application layer (auto-scalling group of stateless web-servers)&lt;/li&gt;
&lt;li&gt;RDS, Elasticache, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The architecture looks rather common and there is a lot of information already online, so I won&apos;t go in details here. An interesting and challenging part in the implementation of that architecture was related to the ELB service and how exactly TCP connections handled by AWS services (ELB and Cloudfront).&lt;/p&gt;
&lt;h2 id=&quot;error-502-bad-gateway&quot;&gt;Error &quot;502 Bad Gateway&quot;&lt;/h2&gt;
&lt;p&gt;The error I was struggling was &quot;502 Bad Gateway - CloudFront attempted to establish a connection with the origin, but either the attempt failed or the origin closed the connection&quot;.&lt;/p&gt;
&lt;p&gt;Even after reading such detailed error message I could not find the right combination of values for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ELB timeouts and draining connection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timeout_idle&lt;/code&gt; for Varnish&lt;/li&gt;
&lt;li&gt;nginx keep-alive setting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After some time AWS support gave a clear explanation for the problem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Varnish apparently sitting with a timeout_idle of 20 seconds, means that Varnish will at times close the TCP connection that the ELB has established to it, while the ELB will continue to think that this connection is still available. If the ELB then tries to send a client request down this connection at a later stage, it will receive an exception, and this will cause the ELB to close the client-side connection with no response.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sounds clear, I modified timeouts according to their suggestions (most critical piece was to increase timeout_idle in Varnish config from 20 to 60 seconds and it all just works).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BUT!&lt;/strong&gt; I would not write this post, if it would be just one line fix... :)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; Why there is even a connection between ELB and the backend, if I don&apos;t make any requests ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ELB pre-opens TCP connections to the back-ends, even when it does not have a request to dispatch on them. It does this as a performance optimization to reduce latency when a request is eventually available to be dispatched along the TCP connection. The ELB assumed that any TCP connection it has between itself and your back-ends can be left idle for the idle timeout and that the back-end will not close it before this time.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This answer put everything together and it explained why I didn&apos;t see an issue while debugging it with ELB excluded.&lt;/p&gt;
&lt;p&gt;Also, worth mentioning, Cloudfront on some requests may return cached data completely from the cache, and then ELB is not contacted and therefore backend connection was not established in advance. So, part of our sites were never affected. No ELB = no problem, hehe.&lt;/p&gt;
&lt;h2 id=&quot;cloudfront-tips&quot;&gt;Cloudfront tips&lt;/h2&gt;
&lt;p&gt;There are few behavior settings (like headers, cookies and query string forwarding) which can dramatically decrease amount of requests to the backend. If misconfigured then you may serve wrong content to users. Enabled &quot;Query string forwarding&quot; together with Varnish&apos;s &lt;code&gt;sub vcl_hash { hash_data(std.querysort(req.http.x-url)); }&lt;/code&gt; keeps single cached copy in Varnish cache, while bypassing Cloudfront.&lt;/p&gt;
&lt;p&gt;Since most of end-users were located in Sweden, where AWS has edge location, by just adding Cloudfront after Route 53 we got average time to connect decreased 80-100ms comparing to time when Route 53 was pointing to ELB. Minor improvement, and for free.&lt;/p&gt;
&lt;h2 id=&quot;varnish-tips&quot;&gt;Varnish tips&lt;/h2&gt;
&lt;p&gt;The biggest performance improvement happened after I revised parts of Varnish configs regarding cookies and ESI, so that average page load time decreased from 1 second per page to 150ms.&lt;/p&gt;
&lt;p&gt;Few points on that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Try to make an application to serve as much content as possible independently of cookies. Criticize your work!&lt;/li&gt;
&lt;li&gt;Whitelist just accepted cookie names (should be always in-sync with developers and marketing people who like to add something with Google Tag Manager)&lt;/li&gt;
&lt;li&gt;When your application depends on some cookies then consider to put that part of your application info light-weight ESI call, which will do just a little cookie-dependent-magic, while the rest of the page is cookie agnostic (see point 1)&lt;/li&gt;
&lt;li&gt;When your application is setting cookies you ideally should know which urls to allow to set cookies and whitelist them (also require developers awareness)&lt;/li&gt;
&lt;li&gt;If performance is very important, then use &lt;a href=&quot;https://github.com/lkarsten/libvmod-cookie&quot;&gt;libvmob-cookie&lt;/a&gt; instead of modifying headers with regexps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use something what provides an integration with caching proxy, so that developers can control large part of ESI and cookies magic themselves - for PHP there is &lt;a href=&quot;https://github.com/FriendsOfSymfony/FOSHttpCache&quot;&gt;FOSHttpCache&lt;/a&gt;, which is awesome and it can save a lot of time.&lt;/p&gt;
</content:encoded><category>aws</category></item></channel></rss>