<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Saltstack on Manuel Herrmann</title>
    <link>https://blog.0x17.de/tags/saltstack/</link>
    <description>Recent content in Saltstack on Manuel Herrmann</description>
    <image>
      <title>Manuel Herrmann</title>
      <url>https://blog.0x17.de/images/mh.jpg</url>
      <link>https://blog.0x17.de/images/mh.jpg</link>
    </image>
    <generator>Hugo -- 0.131.0</generator>
    <language>en</language>
    <lastBuildDate>Sun, 30 Mar 2025 00:00:00 +0200</lastBuildDate>
    <atom:link href="https://blog.0x17.de/tags/saltstack/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>SaltStack from Zero to Efficient: A Practical Journey</title>
      <link>https://blog.0x17.de/post/saltstack-from-zero-to-efficient/</link>
      <pubDate>Sun, 30 Mar 2025 00:00:00 +0200</pubDate>
      <guid>https://blog.0x17.de/post/saltstack-from-zero-to-efficient/</guid>
      <description>Let&amp;rsquo;s face it: infrastructure management can be tedious. But what if I told you that SaltStack has some seriously useful features that can transform your experience from mundane to efficient?
While many configuration management tools like Ansible, Chef, or Puppet offer similar functionality, SaltStack distinguishes itself through its powerful event-driven architecture, efficient minion communication, and exceptional templating capabilities. These features make it particularly well-suited for managing complex, dynamic infrastructure at scale.</description>
      <content:encoded><![CDATA[<p>Let&rsquo;s face it: infrastructure management can be tedious. But what if I told you that <a href="https://saltproject.io/">SaltStack</a> has some seriously useful features that can transform your experience from mundane to efficient?</p>
<p>While many configuration management tools like Ansible, Chef, or Puppet offer similar functionality, SaltStack distinguishes itself through its powerful <a href="https://docs.saltproject.io/en/latest/topics/event/index.html">event-driven architecture</a>, <a href="https://docs.saltproject.io/en/latest/topics/topology/index.html">efficient minion communication</a>, and <a href="https://docs.saltproject.io/en/latest/topics/jinja/index.html">exceptional templating capabilities</a>. These features make it particularly well-suited for managing complex, dynamic infrastructure at scale.</p>
<p><img alt="intro" loading="lazy" src="/post/saltstack-from-zero-to-efficient/intro.png"></p>
<p>In this article, I&rsquo;ll show you how to make SaltStack more pleasant to use, establish a proper development workflow for modules, and maintain a clean main branch while doing so.</p>
<h2 id="integrating-git-with-salt-for-better-version-control">Integrating Git with Salt for Better Version Control</h2>
<p>Imagine having your entire infrastructure configuration tracked, versioned, and deployable with the same tools you use for application code. This isn&rsquo;t just possible with Salt—it&rsquo;s seamless.</p>
<p>Instead of manually copying files to your Salt master or using rsync jobs, GitFS allows you to use Git repositories directly as your source of truth for states and pillar data. This means automatic versioning, change tracking, and the ability to roll back to any previous state of your infrastructure. For configuration options and credential management, see the official <a href="https://docs.saltproject.io/en/latest/topics/tutorials/gitfs.html">documentation on using GitFS</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># Salt master configuration for GitFS integration</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">fileserver_backend</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">gitfs                      </span> <span style="color:#75715e"># Enable the GitFS backend</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">gitfs_provider</span>: <span style="color:#ae81ff">gitpython      </span> <span style="color:#75715e"># Use GitPython for Git operations</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">gitfs_base</span>: <span style="color:#ae81ff">main               </span> <span style="color:#75715e"># Default branch to use</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">gitfs_update_interval</span>: <span style="color:#ae81ff">60</span>       <span style="color:#75715e"># Check for updates every 60 seconds</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">git_pillar_provider</span>: <span style="color:#ae81ff">gitpython </span> <span style="color:#75715e"># Use GitPython for pillar as well</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">git_pillar_update_interval</span>: <span style="color:#ae81ff">60</span>  <span style="color:#75715e"># Check for pillar updates every 60 seconds</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">git_pillar_base</span>: <span style="color:#ae81ff">main          </span> <span style="color:#75715e"># Default branch for pillar data</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">gitfs_remotes</span>:
</span></span><span style="display:flex;"><span>   - <span style="color:#f92672">ssh://git@gitlab.example.com/username/salt-control.git</span>:
</span></span><span style="display:flex;"><span>     - <span style="color:#f92672">root</span>: <span style="color:#ae81ff">state             </span> <span style="color:#75715e"># Root directory for state files in the repo</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">ext_pillar</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">git</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">__env__ ssh://git@gitlab.example.com/username/salt-control.git</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">root</span>: <span style="color:#ae81ff">pillar           </span> <span style="color:#75715e"># Root directory for pillar data in the repo</span>
</span></span></code></pre></div><p>With this setup, your entire infrastructure becomes a Git repository. Every change is tracked. Every deployment is versioned. Every rollback is a simple <code>git revert</code> away.</p>
<h3 id="understanding-the-__env__-parameter">Understanding the <code>__env__</code> Parameter</h3>
<p>The <code>__env__</code> parameter in the Git pillar configuration serves an important role in environment management. When Salt executes, it replaces <code>__env__</code> with the current environment name (like &ldquo;prod&rdquo;, &ldquo;dev&rdquo;, or a Git branch name), automatically ensuring your pillar data matches your state files.</p>
<p>This parameter enables consistent environment isolation by:</p>
<ul>
<li>Matching pillar data to the same environment as your state files</li>
<li>Preventing accidental deployment of development configurations to production</li>
<li>Enabling branch-specific pillar data that stays synchronized with your state files</li>
</ul>
<p>For example, when you run <code>salt '*' state.apply saltenv=feature-branch</code>, the <code>__env__</code> parameter ensures that pillar data from the <code>feature-branch</code> branch is used, maintaining perfect consistency between your states and configuration.</p>
<p>This environment separation is crucial for maintaining a reliable configuration deployment process and preventing environment-specific data from being applied to the wrong targets.</p>
<h3 id="overcoming-gitfs-synchronization-challenges">Overcoming GitFS Synchronization Challenges</h3>
<p>While GitFS is powerful, it&rsquo;s important to note that synchronization can be challenging. Salt has a documented issue (<a href="https://github.com/saltstack/salt/issues/66793">Salt Issue #66793</a>) that makes it difficult to update the fileserver from Salt orchestration.</p>
<p>I&rsquo;ve developed a custom solution that addresses this issue without requiring any patches to Salt&rsquo;s code. Here&rsquo;s my implementation, which is derived from Salt&rsquo;s own <a href="https://github.com/saltstack/salt/blob/develop/salt/runners/fileserver.py">fileserver.py update function</a> but modified to work in orchestration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># /my/salt/repo/states/_runners/fileserver_hotfix.py</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> salt.fileserver
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">update</span>():
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    Update the Salt fileserver cache.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    my_opts <span style="color:#f92672">=</span> dict(__opts__)
</span></span><span style="display:flex;"><span>    my_opts<span style="color:#f92672">.</span>pop(<span style="color:#e6db74">&#39;__pub_user&#39;</span>, <span style="color:#66d9ef">None</span>)  <span style="color:#75715e"># Remove the problematic key</span>
</span></span><span style="display:flex;"><span>    fs <span style="color:#f92672">=</span> salt<span style="color:#f92672">.</span>fileserver<span style="color:#f92672">.</span>Fileserver(my_opts)
</span></span><span style="display:flex;"><span>    fs<span style="color:#f92672">.</span>update()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">True</span>
</span></span></code></pre></div><p>This simple module removes the <code>__pub_user</code> key that causes the issue. After committing and pushing this file, synchronize your GitFS:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt-run fileserver.update
</span></span><span style="display:flex;"><span>salt-run saltutil.sync_runners
</span></span></code></pre></div><p>Now, you can create an orchestration state file that handles the complete synchronization process:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># /my/salt/repo/states/orch/sync_all.sls</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">update_fileserver</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">salt.runner</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">fileserver_hotfix.update</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">update_git_pillar</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">salt.runner</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">git_pillar.update</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">require</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">salt</span>: <span style="color:#ae81ff">update_fileserver</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">sync_all_modules</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">salt.function</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">saltutil.sync_all</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">tgt</span>: <span style="color:#e6db74">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">tgt_type</span>: <span style="color:#ae81ff">glob</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">require</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">salt</span>: <span style="color:#ae81ff">update_git_pillar</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">refresh_pillar</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">salt.function</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">saltutil.refresh_pillar</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">tgt</span>: <span style="color:#e6db74">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">tgt_type</span>: <span style="color:#ae81ff">glob</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">require</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">salt</span>: <span style="color:#ae81ff">sync_all_modules</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">update_mine</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">salt.function</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">mine.update</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">tgt</span>: <span style="color:#e6db74">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">tgt_type</span>: <span style="color:#ae81ff">glob</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">require</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">salt</span>: <span style="color:#ae81ff">refresh_pillar</span>
</span></span></code></pre></div><p>After committing and synchronizing again, you can <a href="https://docs.saltproject.io/salt/user-guide/en/latest/topics/runners-orchestration.html">run this orchestration</a> to update everything:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt-run state.orchestrate orch.sync_all
</span></span></code></pre></div><p>This approach ensures that your GitFS, pillar data, and mine information stay in sync, resolving one of the more challenging aspects of working with Git and Salt.</p>
<p>Note that in high-availability setups with multiple Salt masters, you may need to adjust this approach to ensure all masters are synchronized properly. Consider adding a salt-run command that targets all masters in such scenarios.</p>
<h2 id="repository-organization-single-vs-multiple">Repository Organization: Single vs. Multiple</h2>
<p>&ldquo;Should I keep my states and pillars in the same repository or separate them?&rdquo; (See <a href="https://docs.saltproject.io/en/latest/topics/best_practices.html">Salt&rsquo;s best practices</a> for more guidance.)</p>
<p>This is a common question in the Salt community, and there are compelling arguments for both approaches:</p>
<h3 id="the-single-repository-approach">The Single-Repository Approach</h3>
<p>When your states and pillars live together:</p>
<ul>
<li>Changes to your infrastructure and its configuration happen in one atomic commit</li>
<li>Your Git history tells a complete story of how your infrastructure evolved</li>
<li>You can make sweeping changes across your entire system without fear of misalignment</li>
<li>Testing becomes dramatically simpler since everything moves together</li>
</ul>
<p><strong>Example scenario</strong>: A team managing a consistent application stack across multiple environments (development, staging, production) would benefit from a single repository. When updating the application&rsquo;s configuration, they can change both the state files (how the application is installed and configured) and the pillar data (environment-specific variables like database credentials) in a single commit, ensuring everything stays in sync.</p>
<h3 id="when-to-use-multiple-repositories">When to Use Multiple Repositories</h3>
<ul>
<li>When different teams own the infrastructure code versus the configuration data</li>
<li>When your security requirements demand stricter access controls for sensitive data</li>
<li>When your pillar data changes frequently, but your states are relatively stable</li>
</ul>
<p><strong>Example scenario</strong>: An organization where the security team manages credentials and sensitive configuration while the operations team manages infrastructure code. In this case, keeping pillar data in a separate repository with stricter access controls allows the security team to update credentials without requiring changes to the infrastructure code, while still leveraging the same deployment mechanisms.</p>
<p>The beauty of Salt is that it supports both approaches equally well. You can start with a single repository and split later if needed, or vice versa.</p>
<h2 id="effective-testing-with-saltenv">Effective Testing with SaltEnv</h2>
<p>Here&rsquo;s where Salt really shines compared to other configuration management tools: the ability to test changes in complete isolation using Git branches.</p>
<h3 id="before-traditional-testing-workflow">Before: Traditional Testing Workflow</h3>
<ol>
<li>Develop changes locally</li>
<li>Push to testing environment</li>
<li>Test changes</li>
<li>If issues arise, revert changes or fix in place</li>
<li>Deploy to production</li>
</ol>
<h3 id="after-salt-branch-based-testing-workflow">After: Salt Branch-Based Testing Workflow</h3>
<ol>
<li>Create a feature branch in your Git repository</li>
<li>Develop and commit changes</li>
<li>Push the branch to your Git remote</li>
<li>Test using the branch name as the environment:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt <span style="color:#e6db74">&#39;*&#39;</span> state.apply saltenv<span style="color:#f92672">=</span>new-feature
</span></span></code></pre></div></li>
<li>Iterate on the branch until everything works</li>
<li>Merge to main branch only when fully tested</li>
</ol>
<p>This command tells Salt to use the <code>new-feature</code> branch for both your state files and pillar data, completely isolated from your production environment. It&rsquo;s like having a parallel universe where you can experiment freely without fear of breaking production.</p>
<p>For even more focused testing, you can apply a <a href="https://docs.saltproject.io/en/3006/ref/modules/all/salt.modules.state.html#salt.modules.state.sls">single state file</a> instead of running the entire top.sls configuration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt <span style="color:#e6db74">&#39;*&#39;</span> state.sls mystate saltenv<span style="color:#f92672">=</span>new-feature
</span></span></code></pre></div><p>This allows you to test only the specific component you&rsquo;re modifying, making the testing process faster and more targeted.</p>
<h3 id="the-test-mode-simulate-before-you-apply">The Test Mode: Simulate Before You Apply</h3>
<p>One of Salt&rsquo;s most valuable features for testing is the <code>test=true</code> parameter. This parameter performs a dry run of your state execution, showing you exactly what would change without actually making any modifications to your systems.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt <span style="color:#e6db74">&#39;*&#39;</span> state.sls mystate test<span style="color:#f92672">=</span>true
</span></span></code></pre></div><p>With <code>test=true</code>, Salt will:</p>
<ul>
<li>Connect to your target systems</li>
<li>Load all state files and pillar data</li>
<li>Check current system state against desired state</li>
<li>Report what would change (added, modified, removed)</li>
<li>Exit without making any actual changes</li>
</ul>
<p>For more complex state files, you can combine <code>test=true</code> with your branch testing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt <span style="color:#e6db74">&#39;*&#39;</span> state.sls mystate saltenv<span style="color:#f92672">=</span>new-feature test<span style="color:#f92672">=</span>true
</span></span></code></pre></div><p>This powerful combination lets you:</p>
<ol>
<li>Test against an isolated Git branch without affecting production code</li>
<li>Simulate the execution without making actual changes</li>
<li>Verify that your states target the correct systems</li>
<li>Confirm that the changes match your expectations</li>
</ol>
<p>The test mode is particularly valuable when working with destructive operations or when deploying to critical production systems, as it provides an extra layer of verification before committing to changes. Once you&rsquo;re confident in the changes, you can run the same command without the <code>test=true</code> parameter to apply them for real.</p>
<p>With <a href="https://docs.saltproject.io/en/latest/ref/configuration/master.html#pillarenv-from-saltenv"><code>pillarenv_from_saltenv: True</code></a> in your configuration, Salt automatically keeps your test data synchronized with your test code, ensuring consistent testing across environments.</p>
<p>This approach allows for thorough testing of complex infrastructure changes before merging to the main branch, significantly reducing the risk of issues in production environments.</p>
<h2 id="essential-debugging-tools-for-salt-states">Essential Debugging Tools for Salt States</h2>
<p>Ever wished you could see exactly what Salt is thinking? You can, with these debugging superpowers:</p>
<h3 id="show_slshttpsdocssaltprojectioenlatestrefmodulesallsaltmodulesstatehtmlsaltmodulesstateshow_sls---peek-behind-the-curtain"><a href="https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.state.html#salt.modules.state.show_sls"><code>show_sls</code></a> - Peek Behind the Curtain</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt <span style="color:#e6db74">&#39;*&#39;</span> state.show_sls my_state
</span></span></code></pre></div><p>This reveals the raw SLS data structure after Salt has processed it, letting you verify your states are defined correctly.</p>
<p>Example output:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">my_host</span>:
</span></span><span style="display:flex;"><span>    ----------
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">nginx</span>:
</span></span><span style="display:flex;"><span>        ----------
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">__env__</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ae81ff">base</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">__sls__</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ae81ff">nginx</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">pkg</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ae81ff">|_</span>
</span></span><span style="display:flex;"><span>              ----------
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">name</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#ae81ff">nginx</span>
</span></span><span style="display:flex;"><span>            - <span style="color:#ae81ff">installed</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ae81ff">|_</span>
</span></span><span style="display:flex;"><span>              ----------
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">order</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#ae81ff">10019</span>
</span></span></code></pre></div><h3 id="show_low_slshttpsdocssaltprojectioenlatestrefmodulesallsaltmodulesstatehtmlsaltmodulesstateshow_low_sls---see-the-final-plan"><a href="https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.state.html#salt.modules.state.show_low_sls"><code>show_low_sls</code></a> - See the Final Plan</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt <span style="color:#e6db74">&#39;*&#39;</span> state.show_low_sls my_state
</span></span></code></pre></div><p>The &ldquo;low state&rdquo; is Salt&rsquo;s final internal representation after all preprocessing is done. It&rsquo;s especially useful when debugging complex states with multiple includes or extensive Jinja templating because it shows exactly what Salt will execute after all rendering and inheritance has been resolved.</p>
<p>Example output:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">my_host</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ae81ff">|_</span>
</span></span><span style="display:flex;"><span>      ----------
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">__env__</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">base</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">__id__</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">nginx</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">__sls__</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">nginx</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">fun</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">installed</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">nginx</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">order</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">10019</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">state</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">pkg</span>
</span></span></code></pre></div><h3 id="show_pillarhttpsdocssaltprojectioenlatestrefrunnersallsaltrunnerspillarhtmlsaltrunnerspillarshow_pillar---expose-all-secrets"><a href="https://docs.saltproject.io/en/latest/ref/runners/all/salt.runners.pillar.html#salt.runners.pillar.show_pillar"><code>show_pillar</code></a> - Expose All Secrets</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt-run pillar.show_pillar my_host
</span></span></code></pre></div><p>This displays all pillar data available to a minion, which is crucial for tracking down missing or incorrect configuration values.</p>
<p>Example output:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">my_host</span>:
</span></span><span style="display:flex;"><span>    ----------
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">nginx</span>:
</span></span><span style="display:flex;"><span>        ----------
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">port</span>: <span style="color:#ae81ff">80</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">worker_processes</span>: <span style="color:#ae81ff">4</span>
</span></span></code></pre></div><h3 id="show_tophttpsdocssaltprojectioenlatestrefmodulesallsaltmodulesstatehtmlsaltmodulesstateshow_top---understand-the-hierarchy"><a href="https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.state.html#salt.modules.state.show_top"><code>show_top</code></a> - Understand the Hierarchy</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>salt <span style="color:#e6db74">&#39;*&#39;</span> state.show_top
</span></span></code></pre></div><p>This displays which top files are applied to the minion and in what order, helping you untangle complex state inheritance.</p>
<p>Example output:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">my_host</span>:
</span></span><span style="display:flex;"><span>    ----------
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">base</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#ae81ff">ssh</span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ae81ff">nginx</span>
</span></span></code></pre></div><p>These commands have saved me days of troubleshooting. When something isn&rsquo;t working as expected, I don&rsquo;t have to guess—I can see exactly what Salt sees.</p>
<h2 id="practical-jinja2-templatehttpsdocssaltprojectioenlatesttopicsjinjaindexhtml-debugging">Practical <a href="https://docs.saltproject.io/en/latest/topics/jinja/index.html">Jinja2 Template</a> Debugging</h2>
<p>Jinja2 templates are powerful but can be frustrating when they don&rsquo;t behave as expected. Here&rsquo;s my secret weapon for debugging them:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># Interactive Python shell example for debugging complex Jinja2 templates</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&gt;&gt;&gt;</span> <span style="color:#f92672">import</span> jinja2
</span></span><span style="display:flex;"><span><span style="color:#f92672">&gt;&gt;&gt;</span> context <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>     <span style="color:#e6db74">&#34;users&#34;</span>: [
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>         {<span style="color:#e6db74">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;alice&#34;</span>, <span style="color:#e6db74">&#34;groups&#34;</span>: [<span style="color:#e6db74">&#34;admin&#34;</span>, <span style="color:#e6db74">&#34;dev&#34;</span>]},
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>         {<span style="color:#e6db74">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;bob&#34;</span>, <span style="color:#e6db74">&#34;groups&#34;</span>: [<span style="color:#e6db74">&#34;dev&#34;</span>]},
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>         {<span style="color:#e6db74">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;charlie&#34;</span>, <span style="color:#e6db74">&#34;groups&#34;</span>: [<span style="color:#e6db74">&#34;ops&#34;</span>]}
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>     ]
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span> }
</span></span><span style="display:flex;"><span><span style="color:#f92672">&gt;&gt;&gt;</span> template <span style="color:#f92672">=</span> jinja2<span style="color:#f92672">.</span>Template(<span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">... {</span><span style="color:#e6db74">% f</span><span style="color:#e6db74">or user in users %}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">... {</span><span style="color:#e6db74">% i</span><span style="color:#e6db74">f &#39;admin&#39; in user.groups %}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">... {{ user.name }} is an admin
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">... {</span><span style="color:#e6db74">% e</span><span style="color:#e6db74">ndif %}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">... {</span><span style="color:#e6db74">% e</span><span style="color:#e6db74">ndfor %}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">... &#34;&#34;&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#f92672">&gt;&gt;&gt;</span> print(template<span style="color:#f92672">.</span>render(<span style="color:#f92672">**</span>context))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>alice <span style="color:#f92672">is</span> an admin
</span></span></code></pre></div><p>Notice the extra blank lines in the output? This is where whitespace control becomes important, as we&rsquo;ll see in the next section.</p>
<p>This approach lets you test complex templates outside of Salt, iterating quickly until you get exactly what you want. I&rsquo;ve solved in minutes what would have taken hours of trial and error directly in Salt states.</p>
<h2 id="optimizing-whitespace-in-jinja2-templates">Optimizing Whitespace in Jinja2 Templates</h2>
<p>This might seem trivial, but proper whitespace handling in Jinja2 templates can make your Salt states dramatically more readable and maintainable.</p>
<p>Consider this example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-jinja" data-lang="jinja"><span style="display:flex;"><span># Users
</span></span><span style="display:flex;"><span><span style="color:#75715e">{%</span> <span style="color:#66d9ef">for</span> user <span style="color:#66d9ef">in</span> users <span style="color:#75715e">%}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> user.name <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">{%</span> <span style="color:#66d9ef">endfor</span> <span style="color:#75715e">%}</span>
</span></span></code></pre></div><p>The rendered output would include an extra blank line before the first user:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span># Users
</span></span><span style="display:flex;"><span>(blank line here)
</span></span><span style="display:flex;"><span>Alice
</span></span><span style="display:flex;"><span>Bob
</span></span><span style="display:flex;"><span>Charlie
</span></span></code></pre></div><p>However, with <a href="https://jinja.palletsprojects.com/en/3.1.x/templates/#whitespace-control"><code>{%-</code> syntax</a> (note the dash):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-jinja" data-lang="jinja"><span style="display:flex;"><span># Users
</span></span><span style="display:flex;"><span><span style="color:#75715e">{%</span>- <span style="color:#66d9ef">for</span> user <span style="color:#66d9ef">in</span> users <span style="color:#75715e">%}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">{{</span> user.name <span style="color:#75715e">}}</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">{%</span>- <span style="color:#66d9ef">endfor</span> <span style="color:#75715e">%}</span>
</span></span></code></pre></div><p>The output is cleaner without the extra empty line:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span># Users
</span></span><span style="display:flex;"><span>Alice
</span></span><span style="display:flex;"><span>Bob
</span></span><span style="display:flex;"><span>Charlie
</span></span></code></pre></div><p>This might seem like a small detail, but when you&rsquo;re working with complex, nested templates, proper whitespace control becomes essential for maintaining sanity.</p>
<h2 id="conclusion-making-infrastructure-management-more-efficient">Conclusion: Making Infrastructure Management More Efficient</h2>
<p>After using Salt extensively, these features have transformed infrastructure management from a challenging task into something much more manageable and satisfying. The combination of Git integration, environment isolation, robust debugging tools, and a methodology for testing Jinja templating creates a process that&rsquo;s not just efficient but actually enjoyable.</p>
<p>The next time you approach an infrastructure change, remember these Salt features. They can significantly improve your configuration management experience.</p>
<p><img alt="conclusion" loading="lazy" src="/post/saltstack-from-zero-to-efficient/conclusion.png"></p>
<p>As DevOps practices continue to evolve toward more <a href="https://www.gitops.tech/">GitOps-focused workflows</a> and infrastructure-as-code becomes the standard, mastering these Salt techniques positions you well for the future. The ability to test infrastructure changes in isolation while maintaining strict version control aligns perfectly with modern continuous integration and delivery practices.</p>
<p>Try them out, and you might find that Salt becomes an essential tool in your DevOps toolkit.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
