<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog</id>
    <title>atmos Blog</title>
    <updated>2026-10-08T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog"/>
    <subtitle>atmos Blog</subtitle>
    <icon>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/img/atmos-logo.png</icon>
    <entry>
        <title type="html"><![CDATA[Start an Atmos Project from Any Example Directory]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/initialize-from-examples</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/initialize-from-examples"/>
        <updated>2026-10-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Trying Atmos for the first time should be easy. Install it, check out an example, and run it. Now you can with atmos init.]]></summary>
        <content type="html"><![CDATA[<p>Trying Atmos for the first time should be easy. Install it, check out an example, and run it. Now you can with <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init</code></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/initialize-from-examples#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Someone new to Atmos should be able to kick the tires almost immediately. A working example provides the configuration, stacks, and components to start exploring with just a few commands.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/initialize-from-examples#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Start with any of the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/examples">official Atmos examples</a>. Atmos copies the example verbatim, including examples that contain a scaffold manifest.</p>
<p>The same command also works with directories in other Git repositories, supported remote sources, and local directories. These sources use scaffold generation when a root manifest exists and copy their contents otherwise.</p>
<p>Git sources use a single shallow fetch to avoid downloading repository history. Configure <code>init.depth</code> for any repository: use <code>1</code> for the latest revision or <code>0</code> for full history. A source URL's explicit <code>depth</code> parameter takes precedence. The CLI prints the actual destination and displays the example's README when present.</p>
<p>Copies preserve literal template content, dotfiles, executable permissions, and empty directories while excluding Git metadata. Existing template names and interactive selection continue to work.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/initialize-from-examples#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>After installing Atmos, initialize the quick-start example and list its stacks:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos init github.com/cloudposse/atmos//examples/quick-start-simple</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">cd</span><span class="token plain"> quick-start-simple</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos list stacks</span><br></div></code></pre></div></div>
<p>The destination defaults to the source directory's name. Supply a second argument to choose another destination, or use the shorthand for the default repository:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos init examples/quick-start-simple ./my-project</span><br></div></code></pre></div></div>
<p>Configure <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init#examples-and-source-directories">initialization defaults</a> in <code>atmos.yaml</code> to use your team's repository and revision:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">init</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">repository</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> github.com/acme/starters</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">ref</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> main</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">depth</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">git</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span></span><br></div></code></pre></div></div>
<p>The repository defaults to <code>github.com/cloudposse/atmos</code>. Git initialization and an initial commit are enabled by default; set <code>git: false</code> or pass <code>--no-git</code> to skip them. Explicit source URLs bypass the default repository, and a revision embedded in the source takes precedence over <code>--ref</code> and configuration.</p>
<p>Use <code>--copy</code> to copy another source verbatim even when it contains a scaffold manifest. Copy mode skips template rendering and scaffold hooks, requires an empty destination unless <code>--force</code> is supplied, and does not support scaffold updates or template variables. Read the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init">init reference</a> for source formats, precedence, and overwrite behavior.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/initialize-from-examples#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Explore the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/examples">examples</a> and share your experience in <a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a>.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="DX" term="DX"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Choose Which Components Run in CI with Tags and Labels]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ci-component-selectors</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ci-component-selectors"/>
        <updated>2026-10-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[atmos describe affected now supports --tags and --labels, so you can choose which affected components enter your CI job matrix directly from stack metadata.]]></summary>
        <content type="html"><![CDATA[<p><code>atmos describe affected</code> now supports <code>--tags</code> and <code>--labels</code>, so you can choose which affected components enter your CI job matrix directly from stack metadata.</p>
<p>Some components need privileged credentials or manual approval. Others need network access that the CI runner lacks: a private VPC, a peering connection, or access to an internal service. Selectors let you leave those components out of a workflow and run them where the required access is available.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="select-components-from-stack-metadata">Select Components from Stack Metadata<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ci-component-selectors#select-components-from-stack-metadata" class="hash-link" aria-label="Direct link to Select Components from Stack Metadata" title="Direct link to Select Components from Stack Metadata" translate="no">​</a></h2>
<p>Give components a default label in your shared stack defaults, then override it for components that need a different execution environment:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">stacks/orgs/acme/_defaults.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">labels</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">ci</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> auto</span></span><br></div></code></pre></div></div>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">stacks/catalog/private-service.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">private-service</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">labels</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">ci</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> manual</span></span><br></div></code></pre></div></div>
<p>Build the matrix using that label:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos describe affected </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--format</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">matrix </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--labels</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">ci:auto</span><br></div></code></pre></div></div>
<p><code>--labels=ci=auto</code> is equivalent. The command includes affected components labeled <code>ci: auto</code> and leaves out <code>private-service</code>. The names <code>ci</code>, <code>auto</code>, and <code>manual</code> are user-defined; <code>manual</code> means excluded from this automated workflow, not a built-in execution mode. Run excluded components manually or through a separate workflow with the appropriate credentials and network connectivity.</p>
<p>Labels match all supplied pairs; tags match any supplied tag, such as <code>--tags=production,tier-1</code>. When you combine them, components must satisfy both filters. Components missing the requested metadata do not match, so the shared default makes the selection explicit across your stacks. Selectors work with every output format, including JSON, YAML, and CI matrices. You can also plan the selected components directly with <code>atmos terraform plan --affected --labels=ci:auto</code>, using the same labels in local and automated runs.</p>
<p>The legacy Atmos GitHub Actions used <code>settings.github.actions_enabled</code> for component opt-outs. Native CI does not read that setting. Labels provide an explicit selection pattern when <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/ci/migrate-legacy-actions#opting-components-out-of-ci">migrating to native CI</a>, without post-processing the matrix in workflow YAML.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="include-matching-dependents">Include Matching Dependents<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ci-component-selectors#include-matching-dependents" class="hash-link" aria-label="Direct link to Include Matching Dependents" title="Direct link to Include Matching Dependents" translate="no">​</a></h2>
<p>With <code>--include-dependents</code>, matching dependents remain in the result even when their parent is filtered out. Add <code>--flatten</code> to make each remaining dependent a separate matrix entry. Terraform also honors tags and labels when including dependents; prerequisites added with <code>--include-dependencies</code> still run regardless of those selectors. See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/describe/affected#filtering-by-tags-and-labels">selector reference</a> for details.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="configure-your-workflow">Configure Your Workflow<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ci-component-selectors#configure-your-workflow" class="hash-link" aria-label="Direct link to Configure Your Workflow" title="Direct link to Configure Your Workflow" translate="no">​</a></h2>
<p>Labels express workflow selection; they do not detect connectivity or enforce access. The <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/ci#keeping-privileged-components-out-of-ci">native CI guide</a> covers setup and an optional OPA policy for components prohibited from running in GitHub Actions. Components that only need a different runner can use a separate workflow instead.</p>
<p>For workflow configuration, see the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/integrations/github-actions/deploy-affected">Deploy Affected example</a>, including its empty-matrix guard, and the command reference for <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/describe/affected#selector-environment-and-uploads">environment variables and uploads</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ci-component-selectors#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Have a workflow this pattern does not cover? <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">Open an issue</a> and share your use case.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="DX" term="DX"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Reliable Atmos Pro Uploads for Large Repositories]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/pro-upload-byte-packing</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/pro-upload-byte-packing"/>
        <updated>2026-10-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Large infrastructure repositories can contain a few components with much larger]]></summary>
        <content type="html"><![CDATA[<p>Large infrastructure repositories can contain a few components with much larger
settings than the rest. Grouping uploads by component count could still produce
an oversized request and fail with HTTP 413. Atmos now sizes affected-stack and
instances upload batches by their actual serialized bytes and recovers with
smaller requests when the server rejects a batch.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/pro-upload-byte-packing#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>An average component size cannot predict the size of a batch containing several
large components. A rejected request could also return plain text, which appeared
as an unexpected API response format instead of identifying the size limit.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/pro-upload-byte-packing#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>The <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/settings/pro">Atmos Pro upload configuration</a> now defaults
to a 3 MiB request budget, down from 4 MiB, leaving more room below the server limit.
Uploads preserve component order and account for the request metadata as well as
the component data.</p>
<p>If an affected-stack or instances upload receives HTTP 413, Atmos restarts the
complete batch with a smaller budget, up to three times. A component that is too
large even by itself produces an error naming its stack, component, and serialized
size, with guidance to reduce the uploaded data. Small uploads keep their existing
single-request behavior, and no server change is required for these two upload paths.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/pro-upload-byte-packing#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Continue running <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/describe/affected">affected-stack uploads</a> or
<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/list/list-instances">instances uploads</a> as before:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos describe affected </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--upload</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos list instances </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--upload</span><br></div></code></pre></div></div>
<p>No configuration change is required. An explicit positive
<code>settings.pro.max_payload_bytes</code> still controls the initial request budget;
automatic recovery only reduces it for the current upload. Omitted or nonpositive
values now select 3 MiB, regardless of an edition pin, because this is a runtime
fallback rather than a stored configuration default.</p>
<p>The shared threshold also moves execution metadata to its existing out-of-band
upload path sooner. Large execution-data objects still require the coordinated
protocol work described in <a href="https://github.com/cloudposse/atmos/issues/3313" target="_blank" rel="noopener noreferrer" class="">issue #3313</a>;
this release makes their HTTP 413 errors clear but does not add multipart object
uploads.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/pro-upload-byte-packing#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Report upload failures in <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">GitHub issues</a>,
including the Atmos version and error message with sensitive data removed.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Bug Fix" term="Bug Fix"/>
        <category label="Atmos Pro" term="Atmos Pro"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Per-file template delimiters for scaffold templates]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-per-file-delimiters</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-per-file-delimiters"/>
        <updated>2026-10-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A Helm chart is full of {{ }} expressions, and so is a GitHub Actions workflow with its]]></summary>
        <content type="html"><![CDATA[<p>A Helm chart is full of <code>{{ }}</code> expressions, and so is a GitHub Actions workflow with its
<code>${{ github.sha }}</code>. Put either one in a scaffold template that also uses <code>{{ }}</code> for its own
variables, and the generator tries to render the chart's and the workflow's expressions too. A
template could pick one delimiter pair for every file it generates, so mixing a chart or a
workflow with ordinary files meant escaping every Helm and Actions expression by hand.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-per-file-delimiters#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Scaffold templates render with <code>{{ }}</code> by default, and <code>spec.delimiters</code> can switch the whole
template to a different pair. That works while every file in the template agrees on one pair. It
breaks down as soon as a single template generates files that already contain <code>{{ }}</code> of their own:</p>
<ul>
<li class="">A Helm chart needs its <code>{{ .Values.image }}</code> expressions to reach the generated chart untouched,
so Helm can render them later.</li>
<li class="">A GitHub Actions workflow needs <code>${{ github.sha }}</code> to stay literal for the same reason.</li>
<li class="">The rest of the project, such as stack files and READMEs, still wants the convenient default.</li>
</ul>
<p>Switching the whole template to <code>[[ ]]</code> fixes the chart but forces every other file to change
syntax with it. Keeping <code>{{ }}</code> means escaping every Helm and Actions expression by hand.</p>
<p>Custom delimiters also had a second gap. When Atmos checked a generated file path for unrendered
template markers, it always looked for <code>{{</code> and <code>}}</code>, no matter which delimiters the template
used. A path that legitimately contained a literal <code>{{</code>, such as one produced under a <code>[[ ]]</code>
template, was rejected as if a variable had been left unrendered.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-per-file-delimiters#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>An entry in <code>spec.files</code> can now carry its own <code>delimiters</code>, which override the template-wide
pair for every file the entry matches. The pair covers everything Atmos renders for those files:
their content, their paths, <code>target</code>, and any <code>matrix</code> axis written as a Go template expression.
The <code>when</code> condition is a CEL expression, so it never uses delimiters.</p>
<p>Atmos picks a file's delimiters in this order:</p>
<ol>
<li class="">The <code>delimiters</code> of the matching <code>spec.files</code> entry. When several entries match the same file,
the last one wins, as it does for <code>path</code> and <code>when</code>.</li>
<li class=""><code>spec.delimiters</code>.</li>
<li class=""><code>{{</code> and <code>}}</code>.</li>
</ol>
<p>Path validation now follows the same pair, so a literal <code>{{</code> or <code>${{</code> in a path is accepted under
<code>[[ ]]</code> while a forgotten <code>[[ .Config.name ]]</code> is still caught. Both <code>atmos scaffold generate</code> and
<code>atmos init</code> honor per-file delimiters, and so do <code>--dry-run</code> previews and <code>--update</code> merges. An
update keeps your edits to a chart file while Helm's own expressions stay literal.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-per-file-delimiters#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Declare the template-wide default once, then override it for the files that need something else:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">scaffold.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">delimiters</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(173, 219, 103)">"{{"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"}}"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain">          </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># template-wide default (optional)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"charts/**"</span><span class="token plain">             </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Helm templates use {{ }} themselves</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">delimiters</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(173, 219, 103)">"[["</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"]]"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">".github/workflows/*.yml.tmpl"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">delimiters</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(173, 219, 103)">"&lt;&lt;"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"&gt;&gt;"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain">      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># keeps ${{ github.sha }} literal</span></span><br></div></code></pre></div></div>
<p>A chart template can then mix both syntaxes. Atmos fills in the <code>[[ ]]</code> expression and leaves the
Helm expression for Helm:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">charts/app/templates/deployment.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># atmos:template</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain"></span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain"> .Config.name </span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain"></span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"{{ .Values.image }}"</span></span><br></div></code></pre></div></div>
<p>Each pair must be exactly two non-empty strings, and <code>atmos scaffold validate</code> rejects anything
else. See <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/usage#template-delimiters">Template Delimiters</a> for the full
rules, and the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#template-delimiters"><code>atmos scaffold generate</code></a>
reference for how delimiters combine with <code>target</code> and <code>matrix</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-per-file-delimiters#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try it on a template that generates a chart or a workflow, and
<a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with feedback or edge cases you run
into.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Labels and expiration dates for Azure Key Vault store secrets]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-key-vault-store-tags-expiration</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-key-vault-store-tags-expiration"/>
        <updated>2026-10-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Many organizations enforce an Azure Policy that rejects any Key Vault secret created without an expiration date and a set of tags. It is a sensible governance rule, and it blocks every write that doesn't comply. Until now, that ruled out using Azure Key Vault as an Atmos store in those environments: Atmos wrote the secret value and nothing else, so the policy denied the request.]]></summary>
        <content type="html"><![CDATA[<p>Many organizations enforce an Azure Policy that rejects any Key Vault secret created without an expiration date and a set of tags. It is a sensible governance rule, and it blocks every write that doesn't comply. Until now, that ruled out using Azure Key Vault as an Atmos store in those environments: Atmos wrote the secret value and nothing else, so the policy denied the request.</p>
<p>The Azure Key Vault store now accepts <code>labels</code> and <code>expires</code> options, and applies them to every secret Atmos writes.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-key-vault-store-tags-expiration#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Azure Key Vault lets administrators require metadata on every secret through Azure Policy, for example "all secrets must have an expiration date" and "all secrets must carry an owner tag". The Atmos <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/stores">Azure Key Vault store</a> created secrets with only a name and a value, so under such a policy <code>atmos terraform apply</code> hooks, <code>!store</code> writes, and <code>atmos secret set</code> all failed with a policy violation.</p>
<p>The only workaround was to pre-create every secret outside Atmos with the right metadata, which defeats the point of letting Atmos manage the store.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-key-vault-store-tags-expiration#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Two new options on the store apply metadata at write time:</p>
<ul>
<li class=""><code>labels</code> sets key-value metadata on every secret Atmos writes. Labels are applied as Azure Key Vault secret tags.</li>
<li class=""><code>expires</code> sets the expiration. It accepts either a duration or a specific date, so you don't need separate fields for each style.</li>
</ul>
<p>Reads, existence checks, listing, and deletion are unchanged, and stores that set neither option behave exactly as before.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-key-vault-store-tags-expiration#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Configure both options in the store's <code>options</code> block in <code>atmos.yaml</code>:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">stores</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">prod/azure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure/keyvault</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">vault_url</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"https://my-keyvault.vault.azure.net/"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">labels</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">managed-by</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">environment</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> prod</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">expires</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 90d</span></span><br></div></code></pre></div></div>
<p><code>expires</code> accepts these forms:</p>
<table><thead><tr><th>Value</th><th>Meaning</th></tr></thead><tbody><tr><td><code>90d</code></td><td>A relative expiration: now plus 90 days, recalculated on every write</td></tr><tr><td><code>2160h</code></td><td>Go durations such as <code>720h30m</code> also work</td></tr><tr><td><code>2027-01-01T00:00:00Z</code></td><td>An absolute RFC 3339 timestamp</td></tr><tr><td><code>2027-01-01</code></td><td>A date only, interpreted as midnight UTC</td></tr></tbody></table>
<p>Prefer a relative duration for long-lived stores. A fixed date eventually passes, and from then on every new secret is created already expired or is rejected by policy. A relative value never goes stale, and because it's recalculated on each write, rewriting a secret renews its expiration. A fixed date that is already in the past is accepted, but Atmos logs a warning because Key Vault or your policy may reject it.</p>
<p>A duration must be at least one second, because Key Vault stores expiration in whole seconds. Invalid values, such as <code>0d</code>, <code>500ms</code>, or <code>soon</code>, fail when the store is created rather than on the first write. Label values must be strings, so quote values like <code>"true"</code> or <code>"5"</code>.</p>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/stores">stores configuration reference</a> for the full list of Azure Key Vault options.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-key-vault-store-tags-expiration#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>This came from a request in <a href="https://github.com/cloudposse/atmos/issues/1549" target="_blank" rel="noopener noreferrer" class="">issue #1549</a>. If another store backend needs metadata at write time, such as AWS tags or Google Secret Manager labels, tell us in the issue tracker.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Enhancement" term="Enhancement"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Choose the S3 Bucket Namespace When Atmos Provisions Your State Backend]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/s3-backend-bucket-namespace</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/s3-backend-bucket-namespace"/>
        <updated>2026-10-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Every S3 bucket name has historically lived in one namespace shared by all AWS customers. The name you want for your Terraform state bucket may already belong to someone else, and when you delete a bucket, its name goes back into the shared pool where anyone can claim it. Amazon S3 now offers an account-scoped namespace that fixes both problems: bucket names in it are reserved to your account and cannot collide with anyone else's, now or after the bucket is deleted. Atmos could not request that namespace when it created a state bucket, so teams that want it had to create the bucket by hand before Atmos could take over.]]></summary>
        <content type="html"><![CDATA[<p>Every S3 bucket name has historically lived in one namespace shared by all AWS customers. The name you want for your Terraform state bucket may already belong to someone else, and when you delete a bucket, its name goes back into the shared pool where anyone can claim it. Amazon S3 now offers an account-scoped namespace that fixes both problems: bucket names in it are reserved to your account and cannot collide with anyone else's, now or after the bucket is deleted. Atmos could not request that namespace when it created a state bucket, so teams that want it had to create the bucket by hand before Atmos could take over.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/s3-backend-bucket-namespace#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>State buckets are long-lived and every deployment depends on them, so a name collision or a lost name hurts more here than almost anywhere else. The usual workaround is to bake an account ID and region into the name and hope nobody else picks the same one, but a convention only lowers the odds. Nothing stops another account from creating a bucket with that name, including after you delete yours. The account-scoped namespace removes the question, and organizations can go further and require it with an IAM or service control policy that checks the <code>s3:x-amz-bucket-namespace</code> condition key.</p>
<p><a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/provision/backend">Backend provisioning</a> exists to remove the chicken-and-egg problem of needing a state bucket before you can deploy the component that manages it. The S3 provisioner created buckets in the default namespace and had no way to request another one. S3 selects a namespace with a parameter on the bucket creation request, and a well-formed name is not enough. Anyone who wanted account-scoped state buckets, or whose policy demanded them, was back to a separate bootstrap step.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/s3-backend-bucket-namespace#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>The S3 provisioner now accepts an optional <code>bucket_namespace</code> setting under <code>provision.backend</code>. When it is set, Atmos sends it as the <code>BucketNamespace</code> parameter of the S3 <code>CreateBucket</code> call. When it is not set, nothing changes: existing configurations behave exactly as before.</p>
<p>The setting lives next to <code>enabled</code>, not under <code>backend</code>, so it never appears in the generated Terraform backend configuration. Atmos validates the value before it contacts AWS. The valid values are <code>global</code> and <code>account-regional</code>, as defined by Amazon S3, and any other value fails with the list of valid values.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/s3-backend-bucket-namespace#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Set the namespace and use a bucket name that follows the S3 naming rules for that namespace. Atmos uses the name exactly as written, including names produced by Atmos templates:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">example</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">backend_type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> s3</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">backend</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">s3</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">bucket</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> example</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">tfstate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">111122223333</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">us</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">east</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">an</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">region</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> us</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">east</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> terraform.tfstate</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">use_lockfile</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">provision</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">backend</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">bucket_namespace</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> account</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">regional</span></span><br></div></code></pre></div></div>
<p>Both automatic provisioning on <code>terraform init</code> and <code>atmos terraform backend create</code> honor the setting. See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/provision/backend#bucket-namespace">Bucket Namespace</a> reference for details and the <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/gpbucketnamespaces.html" target="_blank" rel="noopener noreferrer" class="">S3 namespaces documentation</a> for the naming rules.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/s3-backend-bucket-namespace#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Tell us how you provision your state backends in <a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a>, or open an issue if the provisioner is missing a setting you need.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[See why a Helm release failed, right in the Atmos error]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics"/>
        <updated>2026-10-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A controller rollout times out. The deploy fails with "release did not become ready within 5m0s" and nothing else. So you switch to the cluster, run kubectl get pods, then describe, then logs on whichever pod looks wrong - and in CI you often can't do any of that. Worse, if the release is set to roll back on failure, the rollback has already deleted the crashing pods by the time you look, taking the evidence with it.]]></summary>
        <content type="html"><![CDATA[<p>A controller rollout times out. The deploy fails with "release did not become ready within 5m0s" and nothing else. So you switch to the cluster, run <code>kubectl get pods</code>, then <code>describe</code>, then <code>logs</code> on whichever pod looks wrong - and in CI you often can't do any of that. Worse, if the release is set to roll back on failure, the rollback has already deleted the crashing pods by the time you look, taking the evidence with it.</p>
<p>Native Helm releases in Atmos now capture that evidence at the moment of failure and fold it straight into the error: which pod is failing, what the container is reporting, and - at debug level - the crash log and recent events.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>A failed readiness wait tells you <em>that</em> a release did not come up, but not <em>why</em>. The error names the release and namespace, yet the actual cause - a <code>CrashLoopBackOff</code>, an <code>ImagePullBackOff</code> from a bad registry mirror, a container exiting non-zero on a bad config value - lives on the pods, not in the release record.</p>
<p>So the cause is one <code>kubectl</code> session away. Except:</p>
<ul>
<li class="">In CI there is usually no interactive cluster access, so the run just fails with a timeout and no cause.</li>
<li class="">When a release is configured to roll back or uninstall on failure, that recovery deletes the failing pods first. By the time anyone looks, the pod - and its logs - are gone.</li>
</ul>
<p>The result is a dependency-ordered rollout that stops at a release nobody can diagnose from the output alone.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>On a release failure, and <strong>before</strong> any rollback or uninstall runs, Atmos now enumerates the release's pods, finds the not-ready containers, and appends their diagnostics to the same error:</p>
<div class="language-text codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-text codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">Error: failed to perform helm release operation</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  workload diagnostics:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    pod keda-operator-7d9f  keda-operator CrashLoopBackOff (exit 1, 5 restarts)</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      last log (keda-operator):</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        panic: failed to load config: invalid duration "5x"</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      events:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        BackOff  Back-off restarting failed container</span></span><br></div></code></pre></div></div>
<p>The container-status summary (reason, exit code, restart count) is always included on failure. The log tail and the pod's recent events are added when you run at debug or trace level, so normal output stays concise.</p>
<p>To guarantee the evidence survives, Atmos now performs the configured <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/helm#release-lifecycle"><code>on_failure</code> rollback or uninstall</a> itself, after collecting the diagnostics rather than before - the rollback and history-retention behavior you configure is unchanged, it just no longer races the diagnostics.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>There is nothing to enable. Any native Helm <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/helm/apply"><code>apply</code></a> or <code>deploy</code> that fails readiness surfaces the diagnostics automatically. To include the log tail and events, raise the log level:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos helm apply keda </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-prod --logs-level</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">Debug</span><br></div></code></pre></div></div>
<p>Diagnostics are best-effort: if the cluster cannot be reached, Atmos reports the original failure unchanged rather than masking it.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>This pairs with the native Helm <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/helm#release-lifecycle">release lifecycle</a> controls. If there is a failure signal you want surfaced that Atmos does not yet capture, open an issue or discussion on <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">GitHub</a>.</p>]]></content>
        <author>
            <name>Andriy Knysh</name>
            <uri>https://github.com/aknysh</uri>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Experimental" term="Experimental"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Clear server-side apply conflicts without leaving the deploy path]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts"/>
        <updated>2026-10-02T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Kubernetes server-side apply tracks who owns every field of a managed object. When two actors write the same field - a controller that reconciles an object a release also sets, or an object whose ownership ledger was lost - the next apply fails with a field-ownership conflict. The standard escape is a one-off kubectl apply --server-side --force-conflicts or hand-editing managedFields, then re-running your deploy. That is fine on a laptop and impossible in CI, and a conflicted release blocks every dependent waiting on it.]]></summary>
        <content type="html"><![CDATA[<p>Kubernetes server-side apply tracks who owns every field of a managed object. When two actors write the same field - a controller that reconciles an object a release also sets, or an object whose ownership ledger was lost - the next apply fails with a field-ownership conflict. The standard escape is a one-off <code>kubectl apply --server-side --force-conflicts</code> or hand-editing <code>managedFields</code>, then re-running your deploy. That is fine on a laptop and impossible in CI, and a conflicted release blocks every dependent waiting on it.</p>
<p>Native Helm components now expose the two Helm 4 controls that resolve this - <code>server_side_apply</code> and <code>force_conflicts</code> - as release-policy settings and command-line flags, so a conflict clears in the normal <code>atmos helm apply</code> path.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Helm 4 applies release manifests with server-side apply by default, so field ownership is shared with any other actor that writes the same fields. Two situations routinely put another manager on a field a release also declares:</p>
<ul>
<li class="">A controller continuously reconciles an object it also received from a release, and takes ownership of fields the release sets.</li>
<li class="">An object's <code>managedFields</code> ledger is orphaned - for example, a custom resource whose CRD hosts a conversion webhook loses its ledger when a conversion fails during a controller disruption. The next apply synthesizes a stand-in manager that owns the pre-existing fields, and a later release apply that changes those fields conflicts with it.</li>
</ul>
<p>In both cases the apply reports a conflict and the install or upgrade aborts. Because Atmos set no conflict-resolution option, there was no way to clear it through the deploy path: you had to repair the object out of band and re-run. That breaks dependency-ordered rollouts, cannot be remediated in CI, and hid a control Helm 4 already implements.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Two keys are added to the native Helm <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/helm#release-lifecycle"><code>release</code> policy</a>, alongside the existing wait, timeout, history, install, and upgrade controls:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">helm</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">my-component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">release</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Apply method. Omit to use the Helm default.</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">server_side_apply</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Resolve field-ownership conflicts by overwriting the contested</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># fields and becoming their sole manager. Opt-in; default false.</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">force_conflicts</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">false</span></span><br></div></code></pre></div></div>
<p>With <code>force_conflicts</code> enabled, a release apply that meets a field owned by another manager overwrites the contested fields and becomes their sole owner, so the release reaches a successful completion state and its dependents proceed. It is opt-in by design: forcing overrides other managers, so a controller that legitimately co-owns a field loses it on the next apply. That trade-off is yours to make per component, which is why the default leaves conflicts fatal and visible.</p>
<p><code>server_side_apply</code> accepts <code>auto</code>, <code>true</code>, or <code>false</code>. Omitting it preserves the Helm default - server-side apply on install, and the prior release's method on upgrade - so a release that sets neither key behaves exactly as before.</p>
<p>Both settings resolve through the same path as the rest of the release lifecycle: stack type defaults, base-component inheritance, concrete component configuration, and command-line override. A release-wide value is the common case, and the per-phase <code>install</code> and <code>upgrade</code> blocks can override it when first install and later upgrades need different behavior. The configured values are validated before any chart download or cluster mutation.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Set the policy in a stack for steady-state behavior, or force a single recovery apply from the command line without editing configuration:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># One-off recovery: take ownership of the contested fields and continue.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos helm apply my-component </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-prod --force-conflicts</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Override the apply method for this run (a bare flag selects true).</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos helm apply my-component </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-prod --server-side-apply</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">false</span><br></div></code></pre></div></div>
<p>The <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/helm/apply#flags"><code>--force-conflicts</code> and <code>--server-side-apply</code></a> flags are available on <code>apply</code> and <code>deploy</code>, and take precedence over stack <code>release</code> configuration.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/helm#release-lifecycle">native Helm release lifecycle</a> documentation for the full policy reference. If you hit a server-side apply scenario this does not cover, open an issue or discussion on <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">GitHub</a> - we would like to hear about it.</p>]]></content>
        <author>
            <name>Andriy Knysh</name>
            <uri>https://github.com/aknysh</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[File deletion handling for scaffold/init --update]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion"/>
        <updated>2026-10-01T12:00:00.000Z</updated>
        <summary type="html"><![CDATA[Templates change over time: a file that made sense when you first generated your project gets]]></summary>
        <content type="html"><![CDATA[<p>Templates change over time: a file that made sense when you first generated your project gets
renamed, split up, or just stops being relevant. Most codegen tools treat that evolution as
something only a brand-new scaffold run can fix — your existing project just keeps carrying the
leftover file forever. And if you'd deleted that file yourself to clean up, the next update used
to bring it right back, overwriting the choice you'd already made.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p><a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate"><code>atmos scaffold generate --update</code></a> and
<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init --update</code></a> compute a three-way merge for every file the template
still generates: what changed upstream, layered onto what you've customized locally. But that
merge only ever ran for files that exist on both sides. Two real gaps followed from that:</p>
<ul>
<li class="">When the template stopped generating a file, <code>--update</code> never noticed. The stale file just sat
there, untouched, on every future update, forever.</li>
<li class="">When you deleted a file yourself — because you didn't need it, or you'd replaced it with
something else — the next <code>--update</code> silently wrote it straight back, as if your deletion had
never happened.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><code>--update-strategy=rendered</code> now detects a file the template stopped generating and removes it,
but only when your copy still matches exactly what the template last produced. If you'd edited
that file since, <code>--update</code> doesn't guess: it surfaces an unresolved conflict instead, the same
way a genuine merge conflict does, so you decide whether to keep it or let it go.
<code>--update-strategy=tracked</code> can't offer this part safely — there's no reliable way to know which
files in your project's own git history actually belonged to the template versus anything else
that happened to live there.</p>
<p>Independently of strategy, <code>--update</code> also stops silently overwriting a deletion you made
yourself. Both <code>tracked</code> and <code>rendered</code> now check whether a file was previously generated before
recreating it, and leave your deletion in place if so. Pass <code>--recreate-deleted</code> to opt back into
the old always-recreate behavior — it's deliberately its own flag rather than folded into
<code>--force</code>, since <code>--force</code> already means "the template's version wins" for merge conflicts, and
tying file recreation to it would make "resolve conflicts manually" and "recreate what I deleted"
mutually exclusive. <code>--force</code> does, however, now resolve a deletion conflict the same way it
resolves any other: if the template removed a file you'd since edited, <code>--force</code> deletes it anyway
instead of leaving you stuck with a conflict only manual cleanup could clear.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># A file the template no longer generates is removed automatically on --update,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># as long as you haven't edited it -- otherwise you'll get a conflict to resolve.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --update-strategy</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">rendered</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Recreate a file you deleted instead of leaving the deletion in place.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --recreate-deleted</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Force past a deletion conflict -- the template's choice (delete it) wins.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --update-strategy</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">rendered </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--force</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate"><code>atmos scaffold generate</code></a> and
<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init</code></a> docs for the full flag reference. Have feedback on this
feature? <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">Open an issue</a> or join the conversation in
the <a href="https://cloudposse.com/slack" target="_blank" rel="noopener noreferrer" class="">Cloud Posse community Slack</a>.</p>]]></content>
        <author>
            <name>Jorrit Elfferich</name>
            <uri>https://github.com/jorrite</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Use !include and other YAML functions in scaffold templates]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include"/>
        <updated>2026-10-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Two fields in the same scaffold template often need the exact same list of choices — a license]]></summary>
        <content type="html"><![CDATA[<p>Two fields in the same scaffold template often need the exact same list of choices — a license
picker and a region list are both really just "options sourced from some small reference table."
Until now, that table had nowhere to live but inside <code>scaffold.yaml</code> itself, copied into every
field that needed it. And a value as simple as the current git branch, an environment variable, or
a random suffix had no path into a template at all, short of prompting the user for it by hand.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Small pieces of reference data — license choices, region codes, a naming convention lookup — show
up constantly in real scaffold templates, and they rarely stay confined to one field. A <code>select</code>
field needs them as <code>{label, value}</code> options; a file elsewhere in the same template needs the raw
table to look values up by key. Duplicating that table by hand, once per field that needs it, means
every future edit has to find and update every copy — and a missed one quietly drifts out of sync
with the rest. Beyond reference data, templates also commonly need small dynamic values — the
user's git branch or commit SHA, an environment variable, a random suffix for a resource name —
with no way to derive any of them automatically.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><code>scaffold.yaml</code> now resolves a set of <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml">Atmos YAML functions</a> — the same
explicit-tag mechanism stack manifests already use — anywhere it currently accepts a literal
value: <code>options:</code>, a <code>type: computed</code> field's <code>value:</code>, or a <code>matrix:</code> axis.</p>
<ul>
<li class=""><a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/include"><code>!include</code></a>/<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/include.raw"><code>!include.raw</code></a> pulls in a
local or remote file, optionally reshaped with a YQ filter.</li>
<li class=""><a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/env"><code>!env</code></a> reads an environment variable, <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/random"><code>!random</code></a>
generates a random number, and <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/cwd"><code>!cwd</code></a> reads the current working directory.</li>
<li class="">The <code>!git.*</code>/<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/repo-root"><code>!repo-root</code></a> family exposes the current git branch,
commit SHA, repository name, and more.</li>
<li class=""><a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/literal"><code>!literal</code></a> preserves a value exactly as written, bypassing scaffold's
own template evaluation — useful when a value legitimately contains <code>{{ }}</code> and shouldn't be
treated as a template expression.</li>
</ul>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">scaffold.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">fields</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> license</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> select</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!include</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"./lib/licenses.yaml '. | to_entries | map({\"label\": .value.full_name, \"value\": .key})'"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> license_lookup</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> computed</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!include</span><span class="token plain"> ./lib/licenses.yaml</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> branch</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> computed</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!git.branch</span></span><br></div></code></pre></div></div>
<p>A locally-included file that exists solely to be included is automatically excluded from generated
output, the same way <code>scaffold.yaml</code> itself is. <code>atmos scaffold validate</code> resolves everything too,
not just <code>generate</code>, so a missing file, a bad filter, or a malformed value is caught up front.</p>
<p>Not every YAML function is available here: anything that needs real stack, component, or backend
context (<code>!terraform.state</code>, <code>!store</code>, <code>!secret</code>, and similar) is rejected with a clear error
instead. A template's <code>scaffold.yaml</code> is often resolved just to show its name and description in
<code>atmos scaffold list</code> or the interactive picker — before a user has chosen or generated anything —
so only functions that are safe to run in that situation are supported.</p>
<p>The <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/exec"><code>!exec</code></a> function is excluded for a different reason: it needs no stack
context at all, but <code>scaffold.yaml</code> is resolved for every template configured in <code>atmos.yaml</code> just
to populate the list and picker, not only the one a user actually generates — so allowing shell
execution there would let any configured template, including a shared or vendored one, run
arbitrary code merely by being listed.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add any of the functions above anywhere <code>options:</code>, a computed field's
<code>value:</code> (see <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#computed-fields">Computed Fields</a>), or a matrix axis
currently accepts a literal value. See the full
<a href="https://github.com/cloudposse/atmos/tree/main/examples/scaffolding-yaml-functions" target="_blank" rel="noopener noreferrer" class=""><code>examples/scaffolding-yaml-functions</code></a>
example, or the
<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#loading-external-data-with-include-and-other-yaml-functions">Loading External Data with <code>!include</code> and Other YAML Functions</a>
section of the <code>atmos scaffold generate</code> docs.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the
<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#loading-external-data-with-include-and-other-yaml-functions"><code>atmos scaffold generate</code></a>
docs for the full reference, or <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with
feedback.</p>]]></content>
        <author>
            <name>Jorrit Elfferich</name>
            <uri>https://github.com/jorrite</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Component Mocks Now Fill Gaps Instead of Replacing Real State]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback"/>
        <updated>2026-10-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A local plan rarely depends on infrastructure that is entirely missing or entirely deployed. For example, the VPC exists, the database has not been created yet, and the cluster is somewhere in between. Until now, component mocks forced an all-or-nothing choice: with --use-mocks, every Terraform lookup returned its mock, even for components whose real state was sitting in the backend.]]></summary>
        <content type="html"><![CDATA[<p>A local plan rarely depends on infrastructure that is entirely missing or entirely deployed. For example, the VPC exists, the database has not been created yet, and the cluster is somewhere in between. Until now, <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/mocks">component mocks</a> forced an all-or-nothing choice: with <code>--use-mocks</code>, every Terraform lookup returned its mock, even for components whose real state was sitting in the backend.</p>
<p>By default, <code>--use-mocks</code> now treats mocks as fallbacks. Real state wins whenever it exists, and a mock fills in only for a component that has not been provisioned or an output that is missing.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Mocks were designed to let a plan or <code>describe component</code> run before its dependencies exist. In practice, a stack is usually partly deployed. Turning mocks on replaced every <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/terraform.state"><code>!terraform.state</code></a> and <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/terraform.output"><code>!terraform.output</code></a> lookup with literal values, so a plan against a half-built environment showed fake IDs for resources that already had real ones. The only alternative was to turn mocks off and fail on the components that were not there yet.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>The behavior is configurable. In the new default <code>fallback</code> mode, each lookup resolves in this order:</p>
<ol>
<li class="">The real value, when the referenced component's state exists and declares the output.</li>
<li class="">The component's mock, when the state is not provisioned or the output is missing.</li>
<li class="">A YQ <code>//</code> default in the expression, if one is present.</li>
<li class="">The same result as without mocks: the not-provisioned error for a component that was never applied, or <code>null</code> for an output missing from applied state.</li>
</ol>
<p>Mocks never hide real problems. Credential, network, and backend failures still fail the command instead of quietly returning a mock value. As before, only <code>atmos terraform plan</code> and <code>atmos describe component</code> accept <code>--use-mocks</code>; every other Terraform subcommand, such as apply, deploy, and destroy, rejects it. Map outputs are merged: a mock fills keys that are missing from a real map output, while every value present in real state wins.</p>
<p>The other mode, <code>always</code>, keeps the previous behavior for lookups that must not depend on what is deployed, such as describing a component on a machine without cloud credentials. In <code>always</code> mode, <code>!terraform.state</code> and <code>!terraform.output</code> lookups resolve from mocks only and never initialize Terraform, authenticate, or read a backend for the components they reference. A plan still runs Terraform against the component being planned, with that component's own backend and provider credentials. Choose the mode per run with the flag, or set a project default with <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/components/terraform"><code>mocks.mode</code></a>, as shown below.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Declare mocks on the producer component as before:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">stacks/dev.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">vpc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">mocks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">vpc_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> vpc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">local</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">private_subnet_ids</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">subnet</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">a</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> subnet</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">b</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">app</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">vars</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">vpc_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!terraform.state</span><span class="token plain"> vpc vpc_id</span></span><br></div></code></pre></div></div>
<p>Then pick the mode per run, or set a project default:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Real state where it exists, mocks for the gaps.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform plan app </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev --use-mocks</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Lookups use mocks only, with no backend reads or credentials for them.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># The plan itself still uses app's own backend and provider credentials.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform plan app </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev --use-mocks</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">always</span><br></div></code></pre></div></div>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">atmos.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">mocks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">mode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> always   </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># fallback (default) | always</span></span><br></div></code></pre></div></div>
<p>The <code>mocks.mode</code> setting can also be set with <code>ATMOS_COMPONENTS_TERRAFORM_MOCKS_MODE</code>. Attach a mode to the flag with <code>=</code>, because <code>--use-mocks always</code> does not select one. An explicit mode passed with the flag, such as <code>--use-mocks=fallback</code> or <code>--use-mocks=always</code>, wins over the environment variable, which wins over <code>atmos.yaml</code>. A bare <code>--use-mocks</code> turns mocks on and keeps the configured mode.</p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="upgrading">Upgrading<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#upgrading" class="hash-link" aria-label="Direct link to Upgrading" title="Direct link to Upgrading" translate="no">​</a></h3>
<p>This changes what a bare <code>--use-mocks</code> does, so the new default is tied to <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/edition">config editions</a>. Projects pinned to an edition before <code>2026-10-01</code> keep the previous mocks-only behavior with no changes. Unpinned projects, and projects that move their edition forward, get the fallback behavior. To keep mocks-only regardless of edition, set <code>mocks.mode: always</code> or pass <code>--use-mocks=always</code>.</p>
<p>See <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/mocks#resolution-modes">resolution modes</a> for the full details.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try the provider-free <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/examples/terraform-component-mocks">component mocks example</a>, which walks through both the fallback and <code>always</code> flows. Questions and feedback are welcome in <a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a> or the <a href="https://cloudposse.com/slack" target="_blank" rel="noopener noreferrer" class="">SweetOps Slack</a>.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Enhancement" term="Enhancement"/>
        <category label="DX" term="DX"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Activate a PIM-eligible Azure role as part of your identity chain]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation"/>
        <updated>2026-09-30T12:00:00.000Z</updated>
        <summary type="html"><![CDATA[More and more Azure resource roles are handed out as PIM-eligible rather than standing: you hold]]></summary>
        <content type="html"><![CDATA[<p>More and more Azure resource roles are handed out as PIM-eligible rather than standing: you hold
the role only after you activate it, for a time-boxed window, with a justification. That activation
is a multi-step REST dance against Azure Resource Manager - enumerate what you are eligible for,
file a self-activation request, then poll until it provisions - and there is no native <code>az</code> command
for activating an eligible Azure <em>resource</em> role. So every working session starts with hand-rolled
<code>az rest</code> calls or a third-party script before you can actually run anything.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Just-in-time access is the right default for privileged roles, but the activation step lands on the
operator every single session, outside the tool they actually came to use. You want to run a plan
against production; first you have to remember the role definition id, the scope, a justification,
and the sequence of REST calls to turn your eligibility into an active assignment - and redo it
when the window expires. Worse, nothing downstream benefits from a single place that performs the
elevation: the credential your tooling consumes is the same either way, so there is no natural seam
to hang "activate my role, then run" on. Atmos already modeled the two things this needs - becoming
something more privileged through <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/auth/identities#identity-chaining">identity chaining</a>,
and credential time-boxing - but Azure only had the <code>azure/subscription</code> identity. There was no way to
express the elevation at all.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>A new <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/auth/identities#pim-role-activation"><code>azure/pim-role</code></a> identity chains from an existing
Azure identity and, on authentication, runs the Azure Resource Manager PIM self-activation for a
role you are eligible for - scoped and time-boxed by config. Because the elevation lives in the
identity chain, every consumer inherits it with no extra wiring: <code>atmos terraform</code>, <code>atmos auth exec</code>, the AKS kubeconfig exec plugin, and MCP servers all just see the role active.</p>
<p>Unlike assuming a role, <code>azure/pim-role</code> mints no new credentials. Azure RBAC evaluates roles by
object id at request time, not as token claims, so the activation elevates the principal you already
authenticated as - server-side - and the identity hands back the parent credentials unchanged, now
carrying the active role. That is what lets it sit transparently anywhere in a chain.</p>
<p>It is also careful about not being noisy:</p>
<ul>
<li class=""><strong>It does not re-request on every command.</strong> If an active assignment already covers the scope, it
returns immediately instead of filing another request and tripping PIM throttling.</li>
<li class=""><strong>It resumes instead of duplicating.</strong> If a request for the same role and scope is already waiting
on an approver, a later run attaches to that pending request rather than starting a new one.</li>
<li class=""><strong>It refuses clearly when it cannot proceed.</strong> A missing eligibility is reported as "not eligible"
(this activates an eligibility, it does not grant one), distinct from an activation that failed.
In a non-interactive context with no justification, it fails fast and tells you how to supply one.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add a <code>azure/pim-role</code> identity that elevates from an identity you already have:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">auth</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">identities</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">azure-dev</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure/subscription</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">via</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">provider</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">interactive</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">principal</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">subscription_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"00000000-0000-0000-0000-000000000000"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">prod-contributor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure/pim</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">role</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">via</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">identity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">dev          </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># elevate from who I already am</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">principal</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">role_definition_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">scope</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"/subscriptions/00000000-0000-0000-0000-000000000000"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">duration</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"8h"</span><span class="token plain">               </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># converted to ISO-8601; Azure enforces the role's policy maximum</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">justification</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"planned change window"</span></span><br></div></code></pre></div></div>
<p>Then authenticate or run as usual - the role activates as part of the chain:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos auth login </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--identity</span><span class="token plain"> prod-contributor</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos auth </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">exec</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--identity</span><span class="token plain"> prod-contributor -- terraform plan</span><br></div></code></pre></div></div>
<p>Need a different reason for a specific run? Pass the <code>--justification</code> global flag (or set the
<code>ATMOS_AUTH_JUSTIFICATION</code> environment variable) - it overrides the configured default:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos auth </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">exec</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--identity</span><span class="token plain"> prod-contributor </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--justification</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"incident INC-123"</span><span class="token plain"> -- terraform apply</span><br></div></code></pre></div></div>
<p>In a non-interactive context (CI, a running MCP server) with no justification available at all, the
login fails fast and tells you to pass <code>--justification</code> or set <code>ATMOS_AUTH_JUSTIFICATION</code>. If a role
requires approval, the login bounds its wait and shows progress; a later invocation picks up the
pending request instead of starting over.</p>
<p>This covers Azure <em>resource</em> roles. Entra directory roles and PIM for Groups activate through
different APIs and will be separate identity kinds.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>The <code>azure/pim-role</code> identity is part of the broader <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/auth">Atmos Auth</a> effort to
make least-privilege, just-in-time access something you configure once and forget. If you run PIM in
Azure, try it against an eligible role and let us know how it fits your workflow in
<a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a>.</p>]]></content>
        <author>
            <name>Andriy Knysh</name>
            <uri>https://github.com/aknysh</uri>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Security" term="Security"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Configurable merge-conflict threshold for --update]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag"/>
        <updated>2026-09-30T12:00:00.000Z</updated>
        <summary type="html"><![CDATA[Running --update against a generated project is supposed to save you from re-doing your own]]></summary>
        <content type="html"><![CDATA[<p>Running <code>--update</code> against a generated project is supposed to save you from re-doing your own
customizations by hand. But once your local changes and the template's own changes overlap enough
— more than half of a file's lines, by the merge's own accounting — the merge doesn't hand you
conflict markers to work through. It refuses outright, with no way to say "I understand, show me
the conflict anyway."</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p><a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate"><code>atmos scaffold generate --update</code></a> and
<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init --update</code></a> compute a three-way merge for every existing file: what
changed in the template, layered onto what you've customized locally. If that merge would touch
more than 50% of a file's lines, it bails out entirely — no conflict markers, no partial result,
just a hard failure and the file left untouched. That 50% ceiling was hardcoded, with no flag to
raise it, even though a large conflict is often exactly the kind of thing a person wants surfaced
for manual review rather than blocked outright.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><code>--max-changes</code> on both commands controls that same conflict-percentage threshold. The default
stays <code>50</code> — no behavior change if you don't pass it. <code>0</code> disables the check entirely: the merge
always proceeds and writes conflict markers for you to resolve, instead of refusing the update.</p>
<p>One nuance worth understanding before you reach for a specific number: the change percentage this
is compared against isn't itself capped at 100. When your local edits and the template's changes
both diverge significantly from the common base, the computed percentage can climb well past
100% (200%+ in some cases). That means only <code>--max-changes=0</code> is a guaranteed "never fail on this"
setting — raising it to 100, 200, or higher only makes a hard failure progressively less likely,
it doesn't rule one out. If what you actually want is "always give me conflict markers, never a
hard failure," reach for <code>0</code>, not a large number.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Default behavior is unchanged: fails if a merge would touch more than 50% of a file.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Always get conflict markers instead of a hard failure.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --max-changes</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos init </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --max-changes</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Or configure it once via environment variable.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">ATMOS_SCAFFOLD_MAX_CHANGES</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">ATMOS_INIT_MAX_CHANGES</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> atmos init </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate"><code>atmos scaffold generate</code></a> and
<a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init</code></a> docs for the full flag reference. Have feedback on this
feature? <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">Open an issue</a> or join the conversation in
the <a href="https://cloudposse.com/slack" target="_blank" rel="noopener noreferrer" class="">Cloud Posse community Slack</a>.</p>]]></content>
        <author>
            <name>Jorrit Elfferich</name>
            <uri>https://github.com/jorrite</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Automatic CLI Exception Reporting to Atmos Pro]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting"/>
        <updated>2026-09-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Finding the stack, component, and team behind a failed infrastructure command can]]></summary>
        <content type="html"><![CDATA[<p>Finding the stack, component, and team behind a failed infrastructure command can
require piecing together several CI logs. Atmos can now report CLI failures to
Atmos Pro with execution context and your existing metadata tags and labels.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Exception capture previously depended on configuring a separate Sentry destination.
Enabling Atmos Pro for a stack did not automatically send CLI exceptions to Pro,
leaving failures disconnected from the execution records already uploaded there.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>The <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/settings/pro#automatic-exception-reporting">automatic Pro exception reporter</a>
sends failures from GitHub Actions when the effective <code>settings.pro.enabled</code> is
<code>true</code>, using fresh GitHub OIDC credentials and the configured Pro base URL.
Separately configured Sentry destinations continue to receive events with the same
IDs and fingerprints.</p>
<p>Resolved <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/component-metadata">component metadata</a>, including
inherited stack defaults, becomes unprefixed Sentry tags. A <code>production</code> presence
tag becomes <code>production: "true"</code>; a <code>team: platform</code> label becomes
<code>team: "platform"</code>. Events also include stack/component identity and the execution
ID used by Pro uploads, with Atmos's existing masking applied.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Enable Pro in <code>atmos.yaml</code> for invocation-wide reporting, or use the existing
stack/component Pro setting:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">atmos.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">settings</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">pro</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span></span><br></div></code></pre></div></div>
<p>Grant the GitHub Actions workflow <code>id-token: write</code> permission:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> read</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> write</span></span><br></div></code></pre></div></div>
<p><strong>Upgrade behavior:</strong> existing stacks with <code>settings.pro.enabled: true</code> now send
CLI exceptions automatically in eligible GitHub Actions runs. To retain the
previous behavior while keeping other Pro features, explicitly opt out:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">settings</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">pro</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">errors</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">false</span></span><br></div></code></pre></div></div>
<p>The <code>ATMOS_PRO_ERRORS_ENABLED=false</code> environment override also disables reporting
and takes precedence over component settings. Edition pins do not suppress this
new behavior. Reporting failures preserve the command's exit code, with a
two-second delivery timeout and a shared two-second shutdown flush.</p>
<p>The CLI contract covers successful ingestion and authentication rejection; Pro's
provider verification and persisted-tag assertions are tracked in
<a href="https://github.com/cloudposse/atmos/issues/3219" target="_blank" rel="noopener noreferrer" class="">issue #3219</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/settings/pro#automatic-exception-reporting">reporting configuration and tag precedence</a>
for details, and <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with
feedback about exception reporting.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Feature" term="Feature"/>
        <category label="Atmos Pro" term="Atmos Pro"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Consistent Toolchain Paths and Stable Version Declarations]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations"/>
        <updated>2026-09-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[CI should install the artifacts reviewed and committed with a project. Ordinarily, Atmos verifies artifacts against the lockfile's recorded checksums, but it can still accept and record a new artifact when a tool or platform entry is missing.]]></summary>
        <content type="html"><![CDATA[<p>CI should install the artifacts reviewed and committed with a project. Ordinarily, Atmos verifies artifacts against the lockfile's recorded checksums, but it can still accept and record a new artifact when a tool or platform entry is missing.</p>
<p>Enable <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/toolchain#frozen-installs-in-ci"><code>toolchain.frozen_lock_file: true</code></a> in CI and security-sensitive environments to reject those missing entries and prevent lockfile writes. That way, you can prepare and review lockfile updates before running CI.</p>
<p>This update makes project configuration consistent across invocation directories and keeps automatic installs from changing declared dependencies. The same project paths and declarations apply whether a developer or CI runs the command.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Relative toolchain paths could follow the directory where you invoked Atmos. A command run from a component directory could therefore look for a different version manifest or use a different installation directory than the same command run from the project base.</p>
<p>Automatic installation also used the declaration-writing behavior of an explicit install command. Running an infrastructure command could leave a change in <code>.tool-versions</code>, even though you had not asked to change the project's dependencies.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/toolchain#configuration-options">Toolchain paths</a> now resolve from the configured project base, and automatic installs leave declared versions unchanged.</p>
<table><thead><tr><th>Behavior</th><th>Before</th><th>Now</th></tr></thead><tbody><tr><td>Relative toolchain paths</td><td>Could resolve from the invocation directory.</td><td>Resolve from the configured project base, including the default <code>.tool-versions</code> path.</td></tr><tr><td>Automatic dependency installation</td><td>Could add entries to <code>.tool-versions</code>.</td><td>Installs the dependency without rewriting declarations.</td></tr><tr><td>Atmos version switching</td><td>Could use earlier toolchain settings instead of the active project configuration.</td><td>Uses the active configuration, including selected profiles and project lockfile settings.</td></tr><tr><td>Toolchain path environment overrides</td><td><code>ATMOS_TOOLCHAIN_FILE_PATH</code> and <code>ATMOS_TOOLCHAIN_INSTALL_PATH</code> were not applied.</td><td>Override configured paths for explicit installs, automatic dependencies, and Atmos version switching.</td></tr></tbody></table>
<p>For example, with <code>/work/infra</code> as the configured project base, Atmos reads <code>/work/infra/.tool-versions</code> even when invoked from <code>/work/infra/components/vpc</code>. A configured <code>install_path: .tools</code> resolves to <code>/work/infra/.tools</code> from either directory. The same rule applies to relative <code>versions_file</code> and <code>lock_file</code> settings.</p>
<p>Binaries still use shared XDG cache storage by default. When Atmos installs a version of itself outside a project, it now keeps installation metadata there too, unless you explicitly override the installation path.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Existing projects get the path and declaration fixes without adding configuration. Continue running your usual Atmos commands; when they need to install a tool automatically, the project's declarations remain unchanged. Use explicit <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/toolchain/install">toolchain management commands</a> when you intend to add or change those declarations.</p>
<p>Use the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/toolchain#environment-variables">toolchain environment overrides</a> to select a different manifest or installation directory without editing project configuration. For example, <code>ATMOS_TOOLCHAIN_INSTALL_PATH=/shared/atmos-tools atmos toolchain install</code> installs binaries in the supplied directory even when invoked outside the project.</p>
<p>Automatic installs can still update <strong>resolved artifact metadata</strong> in the existing <code>toolchain.lock.yaml</code>: they record missing version or platform entries after successful installation, preserve matching entries, and fail on checksum mismatches. The distinction is between declaring a dependency and recording the artifact used to satisfy it. See <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/toolchain#automatic-installation-and-lockfiles">automatic installation and lockfiles</a> for details.</p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="compatibility">Compatibility<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#compatibility" class="hash-link" aria-label="Direct link to Compatibility" title="Direct link to Compatibility" translate="no">​</a></h3>
<p>If you relied on toolchain paths relative to the invocation directory, adjust them relative to the project base or use absolute paths. These behavior fixes apply to every <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/edition">config edition</a>; pinning an older edition does not restore the earlier path or declaration-writing behavior.</p>
<p>The existing lockfile defaults still depend on your edition. Editions before <code>2026-08-05</code> retain <code>use_lock_file: false</code>; set it explicitly to enable ordinary lockfile use. Frozen mode remains opt-in and requires verification regardless of that setting.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try running your existing commands from a project subdirectory and check that automatic installs leave <code>.tool-versions</code> unchanged. Share any unexpected behavior in <a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a>.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Enhancement" term="Enhancement"/>
        <category label="Bug Fix" term="Bug Fix"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Derive a scaffold field once with type: computed]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields"/>
        <updated>2026-09-23T12:00:00.000Z</updated>
        <summary type="html"><![CDATA[A scaffold template's spec.fields[] questionnaire is great at collecting answers, but not every]]></summary>
        <content type="html"><![CDATA[<p>A scaffold template's <code>spec.fields[]</code> questionnaire is great at collecting answers, but not every
value a template needs is really an answer. Some values are just a function of other answers —
"the primary region, defaulting to the only region when there's just one" — and until now, every
file that needed that value had to re-derive it itself, with the same <code>{{ if .Config.primary_region_select }}...{{ end }}</code>
snippet copied into each one.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Templating and scaffolding tools all hit the same shape of problem eventually: a value the
generated output needs isn't something the user should be prompted for at all — it's derived from
answers they already gave. Ask for a list of regions, then only ask for a primary region when
there's more than one; when there's exactly one, it's the primary by definition, no prompt needed.
That derivation logic is simple once, but a scaffold template has no single place to put it. It
gets pasted into every file that references the value, and every copy has to independently stay
in sync with the same conditional. Miss one, or get the fallback logic subtly wrong in one file,
and that file quietly disagrees with the rest of the generated project.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>A new <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#computed-fields"><code>type: computed</code></a> field declares a value
once — either derived from other answers via a <code>value:</code> Go-template expression, or a plain
literal (string, number, boolean, list, or map) used as-is — and is never itself prompted for or
settable with <code>--set</code>. A string is only treated as an expression when it actually contains a
template action; a plain string like <code>hello</code> is a literal too, same as any other type:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">fields</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> regions</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> multiselect</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">us</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">east</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> us</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">west</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> eu</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">west</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> primary_region_select</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> select</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> answers.regions</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">when</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"size(answers.regions) &gt; 1"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> primary_region</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> computed</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"{{ ternary answers.primary_region_select (index answers.regions 0) (gt (len answers.regions) 1) }}"</span></span><br></div></code></pre></div></div>
<p><code>primary_region</code> derives its value exactly once, and <code>.Config.primary_region</code> is then usable
everywhere <code>.Config</code> is — file content, <code>target:</code> path templates, and <code>matrix:</code> axes — with no
per-file fallback logic to keep in sync. Computed fields evaluate in declaration order, after
every regular field's answer is already final, so a computed field can reference any regular
field regardless of where it's declared, and any <em>earlier</em>-declared computed field's own result.</p>
<p>A computed field's <code>value:</code> doesn't have to be an expression at all — a plain literal works too,
useful for a small hand-authored reference table shared across every file in the template:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> provider_version_pins</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> computed</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token key atrule">aws</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"~&gt; 5.0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token key atrule">azurerm</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"~&gt; 3.0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token key atrule">google</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"~&gt; 5.0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span></span><br></div></code></pre></div></div>
<p><code>provider_version_pins</code> is stored exactly as written, with no template rendering, and is
reachable the same way as any other computed field: <code>.Config.provider_version_pins</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add a <code>type: computed</code> field to any existing <code>scaffold.yaml</code>, following the shape above, and
reference its name from <code>.Config</code> in any file the template generates:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate </span><span class="token operator" style="color:rgb(127, 219, 202)">&lt;</span><span class="token plain">template</span><span class="token operator" style="color:rgb(127, 219, 202)">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(127, 219, 202)">&lt;</span><span class="token plain">target</span><span class="token operator" style="color:rgb(127, 219, 202)">&gt;</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">regions</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">us-east-1,us-west-2</span><br></div></code></pre></div></div>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#computed-fields">Computed Fields</a> section of the
<code>atmos scaffold generate</code> docs for the full set of validation rules (<code>value:</code> is required on a
computed field and rejected on every other type; <code>required:</code>/<code>default:</code> are both rejected on a
computed field) and the ordering constraints that keep a computed field's dependencies resolvable.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#computed-fields"><code>atmos scaffold generate</code></a> docs for the
full reference, or <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with feedback.</p>]]></content>
        <author>
            <name>Jorrit Elfferich</name>
            <uri>https://github.com/jorrite</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Skip or duplicate a whole directory with glob spec.files[].path]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob"/>
        <updated>2026-09-18T12:00:00.000Z</updated>
        <summary type="html"><![CDATA[A scaffold template's spec.files[] entries have always matched one discovered file at a time —]]></summary>
        <content type="html"><![CDATA[<p>A scaffold template's <code>spec.files[]</code> entries have always matched one discovered file at a time —
every file that needed gating or duplicating got its own entry, one <code>path:</code> per file. That's fine
for a handful of files. It breaks down the moment the thing you want to skip or duplicate is a
whole directory: a legacy docs tree gated behind an opt-in answer, or a <code>components/</code> tree that
needs to exist once per environment, region, or tenant. Either case meant repeating the same
<code>when:</code> or the same <code>matrix:</code> on every file inside the directory, one entry per file, kept in sync
by hand as the directory grew.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>The <code>matrix:</code> field already expands a single declared file into one generated file per selected value —
pick three environments, get three files, from one template file. But real templates aren't
single files; they're whole directories. A <code>components/</code> tree with a dozen resources that needs
to exist under every environment couldn't be matrixed as a unit — only file by file, one
<code>spec.files[]</code> entry per file, each with its own repeated <code>matrix:</code> and <code>target:</code>. Skipping a
directory recursively (docs that only ship when an answer opts in, a cloud-specific subtree that
only applies to one provider) had the same problem in miniature: one <code>when:</code>-gated entry per file,
duplicated across every file the directory contained.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>The <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#glob-paths-and-directory-level-matrix"><code>spec.files[].path</code></a>
field can now be a glob pattern instead of a literal path — <code>*</code>, <code>?</code>, <code>[...]</code>, <code>**</code> for any depth, and
<code>{a,b}</code> brace expansion — matched against every file the template discovers. One entry now covers
an entire directory:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"docs/legacy/**"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">when</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"answers.include_legacy_docs"</span></span><br></div></code></pre></div></div>
<p>The <code>when:</code> field still evaluates exactly as it does for a single file — it just now applies to every file
the glob matches, at any depth, recursively, with no per-file repetition. When more than one
entry's <code>path:</code> matches the same file, the <em>last</em> one declared wins, the same precedence
<code>.gitignore</code>/<code>CODEOWNERS</code> use: write broad patterns first, specific overrides after.</p>
<p>Combine a glob <code>path:</code> with <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#dynamic-file-generation"><code>matrix:</code></a>
and <code>target:</code> to duplicate an entire directory once per combination, the same way a single file
already could:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"components/**"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">target</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"environments/{{ .matrix.env }}/{{ .file.RelPath }}"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">matrix</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">dev</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> staging</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> production</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span></span><br></div></code></pre></div></div>
<p>Since a glob can match many files, <code>target:</code> needs to know <em>which</em> matched file an output came
from — <code>.file.RelPath</code> is that file's own path with the glob's literal prefix stripped (so
<code>components/vpc/main.tf</code> becomes <code>vpc/main.tf</code>), available in <code>target:</code> and the file's own content
alongside <code>.matrix.&lt;axis&gt;</code>. A <code>components/</code> directory with <code>vpc/main.tf</code> and <code>eks/main.tf</code>
produces six files across three environments — each preserving its own relative position under
every environment.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>The <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/examples/scaffolding-directory-matrix">scaffolding-directory-matrix example</a> is a minimal,
runnable template — a two-resource <code>components/</code> directory duplicated once per selected
environment:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token builtin class-name" style="color:rgb(255, 203, 139)">cd</span><span class="token plain"> examples/scaffolding-directory-matrix</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate example ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">environments</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">dev,staging</span><br></div></code></pre></div></div>
<p>This generates four files: <code>environments/dev/vpc/main.tf</code>, <code>environments/dev/eks/main.tf</code>, and
the same pair under <code>staging/</code> — two full copies of <code>components/</code>, one per selected environment,
without listing <code>vpc/main.tf</code> and <code>eks/main.tf</code> individually in <code>scaffold.yaml</code>. Add a glob
<code>path:</code> to any <code>spec.files[]</code> entry in your own templates — with <code>when:</code> alone to skip a
directory, or with <code>matrix:</code> and <code>.file.RelPath</code> in <code>target:</code> to duplicate one.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#glob-paths-and-directory-level-matrix"><code>atmos scaffold generate</code></a>
docs for the full reference, or <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with
feedback.</p>]]></content>
        <author>
            <name>Jorrit Elfferich</name>
            <uri>https://github.com/jorrite</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Bring your own model: OpenRouter, DeepSeek, and Z.AI for Atmos AI]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers"/>
        <updated>2026-09-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The AI model landscape moves faster than any single vendor's roadmap. A model that was the obvious]]></summary>
        <content type="html"><![CDATA[<p>The AI model landscape moves faster than any single vendor's roadmap. A model that was the obvious
choice last quarter is often outclassed - or undercut on price by an order of magnitude - by one you
hadn't heard of this quarter. Locking your infrastructure assistant to one vendor's API means you
either overpay or miss out, and for teams outside the US, a US-only provider list can be a
non-starter entirely.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Atmos AI let you talk to your infrastructure through a fixed set of API providers. If you wanted to
try a cheaper or non-US model - route through an aggregator, run DeepSeek directly, or use a GLM
model from Z.AI - you were out of luck unless you were willing to point the generic OpenAI provider
at a hand-copied base URL and hope the defaults lined up. There was no first-class way to say "use
OpenRouter" and get a sensible model, API-key variable, and endpoint out of the box.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Atmos AI adds three OpenAI-compatible API providers you can select by name:</p>
<ul>
<li class=""><strong>OpenRouter</strong> (<code>openrouter</code>) - a router that fronts hundreds of models behind one API key. Switch
models by changing the <code>model</code> slug (<code>anthropic/claude-sonnet-4-5</code>, <code>openai/gpt-4o</code>,
<code>deepseek/deepseek-chat</code>, ...) without touching anything else.</li>
<li class=""><strong>DeepSeek</strong> (<code>deepseek</code>) - the DeepSeek API directly, including <code>deepseek-reasoner</code> for the
reasoning model. Low cost for a lot of everyday infrastructure questions.</li>
<li class=""><strong>Z.AI</strong> (<code>zai</code>) - Zhipu's GLM models over their OpenAI-compatible endpoint.</li>
</ul>
<p>Each one behaves like every other Atmos AI provider: set an API key with the <code>!env</code> function, and
optionally override the model, <code>base_url</code>, or token limits. Because they are OpenAI-compatible, they
work everywhere the existing providers do - <code>atmos ai ask</code>, <code>atmos ai chat</code>, and the <code>--ai</code> flag.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add the provider under <code>ai.providers</code> in <code>atmos.yaml</code> and select it as the default:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">ai</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">default_provider</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> openrouter</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">providers</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">openrouter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">model</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"deepseek/deepseek-chat"</span><span class="token plain">   </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># any provider-prefixed slug</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">api_key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!env</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"OPENROUTER_API_KEY"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">deepseek</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">model</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"deepseek-chat"</span><span class="token plain">            </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># or "deepseek-reasoner"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">api_key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!env</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"DEEPSEEK_API_KEY"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">zai</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">model</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"glm-5.3"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">api_key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!env</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"ZAI_API_KEY"</span></span><br></div></code></pre></div></div>
<p>Then ask away:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token builtin class-name" style="color:rgb(255, 203, 139)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">OPENROUTER_API_KEY</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">sk-or-</span><span class="token punctuation" style="color:rgb(199, 146, 234)">..</span><span class="token plain">.</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos ai ask </span><span class="token string" style="color:rgb(173, 219, 103)">"What stacks and components do we have?"</span><br></div></code></pre></div></div>
<p>See the full list of options on the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/ai/providers">AI providers configuration page</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>The provider list will keep growing as the model landscape shifts. If there's an OpenAI-compatible
provider you want to see as a first-class name in Atmos, open an issue or a pull request on
<a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">cloudposse/atmos</a> - adding one is a small, well-templated change.</p>]]></content>
        <author>
            <name>Andriy Knysh</name>
            <uri>https://github.com/aknysh</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Use opencode as your Atmos AI provider]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider"/>
        <updated>2026-09-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[If you already drive your infrastructure work through a terminal coding agent, paying for a]]></summary>
        <content type="html"><![CDATA[<p>If you already drive your infrastructure work through a terminal coding agent, paying for a
separate AI API key just to ask Atmos a question is redundant. You've authenticated the agent
once, picked your model provider there, and you'd rather Atmos reuse that setup than make you
manage a second set of credentials.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Atmos AI could reuse a locally installed coding-agent CLI - Claude Code, OpenAI Codex, GitHub
Copilot - instead of an API key, but the popular open-source <a href="https://opencode.ai/" target="_blank" rel="noopener noreferrer" class="">opencode</a>
agent wasn't one of them. opencode users had to fall back to configuring a raw API provider,
which meant a second credential to manage and gave up opencode's own model selection and MCP
setup.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Atmos AI adds <strong>opencode</strong> as a CLI provider. Point Atmos at it and every <code>atmos ai</code> command runs
through your existing opencode installation and whichever model provider you've authenticated
there - no API key in <code>atmos.yaml</code>:</p>
<ul>
<li class="">Reuses your opencode auth and model configuration (<code>opencode auth login</code>).</li>
<li class=""><strong>Full MCP pass-through</strong>: MCP servers you declare in <code>atmos.yaml</code> are handed to opencode
automatically, with auth-requiring servers wrapped in <code>atmos auth exec</code> and the Atmos toolchain
on <code>PATH</code>. Atmos writes a temporary config and points opencode at it via <code>OPENCODE_CONFIG</code>, so
your own <code>opencode.json</code> is never touched.</li>
<li class="">Participates in auto-detection: with <code>ai.enabled: true</code> and no <code>default_provider</code>, Atmos finds
the <code>opencode</code> binary on your <code>PATH</code> and uses it.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Select it as the default provider (or let auto-detection find it):</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">ai</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">default_provider</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> opencode</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">providers</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">opencode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># optional - opencode's provider/model slug; defaults to opencode's own default</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">model</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"anthropic/claude-sonnet-4-5"</span></span><br></div></code></pre></div></div>
<p>Then ask away - opencode handles the model call and any MCP tools:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos ai ask </span><span class="token string" style="color:rgb(173, 219, 103)">"Which components changed in the dev stack?"</span><br></div></code></pre></div></div>
<p>See the <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/ai/providers">AI providers configuration page</a> for the full CLI-provider
reference, including the MCP pass-through behavior.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>opencode joins Claude Code, OpenAI Codex, and GitHub Copilot as bring-your-own-subscription CLI
providers. If there's another local coding agent you'd like Atmos to drive, open an issue or a pull
request on <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">cloudposse/atmos</a>.</p>]]></content>
        <author>
            <name>Andriy Knysh</name>
            <uri>https://github.com/aknysh</uri>
        </author>
        <category label="Feature" term="Feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Faster Vendoring with Concurrent Downloads]]></title>
        <id>https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring</id>
        <link href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring"/>
        <updated>2026-09-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Waiting for each component download to finish before the next begins adds up in]]></summary>
        <content type="html"><![CDATA[<p>Waiting for each component download to finish before the next begins adds up in
large repositories. Atmos vendoring now prepares independent packages concurrently
and shows their progress together, while keeping destination writes in declaration
order.</p>
<div><div class="window_X9dN"><div class="titlebar_DN7h"><span class="dots_R2sg" aria-hidden="true"><i></i><i></i><i></i></span><span class="title_Dael">Concurrent vendoring and cleanup</span></div><pre class="screen__b5c noPreWrap_ImkX screenLoading_abuO"><span> </span></pre><div class="controls_eyLV"><button type="button" class="playButton_kD9r" aria-label="Pause cast"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M6 5H8V19H6V5ZM16 5H18V19H16V5Z"></path></svg></button><input aria-label="Cast position" type="range" min="0" max="0" step="0.01" value="0"><span>00:00.0<!-- --> / <!-- -->00:00.0</span></div></div><div class="castActions_M13G"><div class="container_zGFV"><div class="group_ncGU" role="group" aria-label="Share this demo"><button type="button" class="primary_hpkh" title="Copy a link to this demo" aria-live="polite"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg><span>Share</span></button><button type="button" class="caret_pPxC" aria-expanded="false" aria-label="More share options" title="More share options"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div><div class="container_EXko"><button type="button" class="trigger_WxG7" aria-expanded="false" aria-label="Download cast"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg><span>Download</span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div></div>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>A repository can contain many component sources, mixins, and targets. Fetching
these one at a time leaves the network idle between jobs and makes a large update
hard to follow. Overlapping destinations also mean downloads cannot simply copy
files whenever they finish.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/vendor/pull">Vendor pull</a> prepares up to four packages at once and
installs them in declaration order. Local sources wait for earlier writes before
being read. <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/vendor/vendor-update">Vendor update</a> checks upstream
versions concurrently and preserves the order and formatting of manifest edits.</p>
<p>The shared progress display shows active phases, download percentages when the total
size is known, retries, and completed results. A ready package is waiting for its turn
to install. The overall bar advances during downloads with known sizes and when
packages become ready, with equal weight for preparation and installation. The
completed count advances only after processing finishes. CI receives plain result
lines, and structured update reports stay on stdout.</p>
<p>The <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/vendor/vendor-clean">vendor clean</a> command displays relative paths
and preserves every lock entry when removing vendored files. Recorded versions,
checksums, and provenance remain available for the next pull. Fully cleaned packages
reinstall without drift warnings; partial deletion and modified files still trigger checks.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos vendor pull --max-concurrency </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos vendor update </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--check</span><span class="token plain"> --max-concurrency </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos vendor update </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--pull</span><span class="token plain"> --max-concurrency </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><br></div></code></pre></div></div>
<p>Set <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/vendor#concurrency"><code>vendor.max_concurrency</code></a> in <code>atmos.yaml</code>
or use <code>ATMOS_VENDOR_MAX_CONCURRENCY</code>. Explicit flags take precedence over the
environment and configuration. Set one worker for serial execution.</p>
<p>Projects with an <a class="" href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/edition">edition pin</a> before <code>2026-09-15</code> keep
one worker by default. All editions receive the progress display, and explicit
concurrency settings override the pin.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3322.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try the commands on your component catalog and share feedback in
<a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">Atmos GitHub Discussions</a>.</p>]]></content>
        <author>
            <name>Erik Osterman</name>
            <uri>https://github.com/osterman</uri>
        </author>
        <category label="Enhancement" term="Enhancement"/>
        <category label="DX" term="DX"/>
    </entry>
</feed>