<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: quixoticmonk</title>
    <description>The latest articles on Forem by quixoticmonk (@quixoticmonk).</description>
    <link>https://forem.com/quixoticmonk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F90033%2F5b802c42-a861-4ac3-b2f4-81eb93a7d044.png</url>
      <title>Forem: quixoticmonk</title>
      <link>https://forem.com/quixoticmonk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/quixoticmonk"/>
    <language>en</language>
    <item>
      <title>Terraform 1.15 Alpha: Variable and Output Deprecation</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Sat, 14 Feb 2026 14:25:02 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/terraform-115-alpha-variable-and-output-deprecation-4dol</link>
      <guid>https://forem.com/quixoticmonk/terraform-115-alpha-variable-and-output-deprecation-4dol</guid>
      <description>&lt;p&gt;Terraform has been iterating a lot more in the past few months introducing features or updates to the core binary which were always desired. Looking at the release notes of &lt;a href="https://github.com/hashicorp/terraform/releases/tag/v1.15.0-alpha20260204" rel="noopener noreferrer"&gt;v1.15.0-alpha20260204&lt;/a&gt; version , you see a few gems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;You can set a deprecated attribute on variable and output blocks to indicate that they are deprecated. This will produce warnings when passing in a value for a deprecated variable or when referencing a deprecated output.&lt;/code&gt;. Its been a wish to have an easy way to manage guidance around input and output deprecation between releases of a module than just via release notes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Terraform Test: Allow functions within mock blocks&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will focus on the first one for this post. I have a few collegues who are pretty pumped about this as they manage public and private modules. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This post covers features from Terraform 1.15.0-alpha20260204. As this is an alpha release, syntax, behavior, and functionality may change before the final release. I wouldn't reccommend using this in a production setup as is.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why is this important ?
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Module authors:
&lt;/h4&gt;

&lt;p&gt;I have seen many a teams end up passing the entire resource objects than specific attributes from a module as output. I feel that is lazy, but I can understand why they had to do what they had to because of past experiences when the interface of their moodule changed due to some suboptimal initial decisions. Every one has good intentions and want to fix them as soon as possible. But what about the teams who use their module ?&lt;/p&gt;

&lt;h4&gt;
  
  
  Module users:
&lt;/h4&gt;

&lt;p&gt;When a module interfaces change, you're left scrambling through release notes trying to figure out what broke and how to fix it. A module that worked yesterday suddenly requires different inputs, or worse - outputs you depend on have changed. Without clear deprecation warnings , you only discover these issues when &lt;code&gt;terraform plan&lt;/code&gt; fails or produces unexpected results way later in a future release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deprecation-Guided Migration
&lt;/h2&gt;

&lt;p&gt;The deprecation feature in 1.15-alpha allows module authors to guide users through interface changes gradually. Here's how it works:&lt;/p&gt;

&lt;p&gt;Let's establish the Terraform version I am using currently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform version
Terraform v1.15.0-alpha20260204
on darwin_arm64
+ provider registry.terraform.io/hashicorp/aws v6.31.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Variable Deprecation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simple message
&lt;/h3&gt;

&lt;p&gt;Lets start with a simple example. No providers. No modules. Just one variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"This variable will be removed in v2.0"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference id for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Terraform plan  :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
var.ref_id
  Reference &lt;span class="nb"&gt;id &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;service 1

  Enter a value: 234dfeff-123213dsad-12dsad


No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no
differences, so no changes are needed.
╷
│ Warning: Deprecated variable got a value
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt;:
│    1: variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
│    2:   &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string
│    3:   deprecated  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"This variable will be removed in v2.0"&lt;/span&gt;
│
│ This variable will be removed &lt;span class="k"&gt;in &lt;/span&gt;v2.0

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

&lt;/div&gt;



&lt;p&gt;This looks awfully similar to the provider attribute deprecations you may have seen between major versions, the .name vs .region one for &lt;code&gt;aws_region&lt;/code&gt; datasource for example. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The only thing with this example is that when you run &lt;code&gt;terraform validate&lt;/code&gt; with a default value set for the var, it doesn't show any deprecation messages.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1213123"&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"This variable will be removed in v2.0"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference id for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Validate&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tf validate
Success! The configuration is valid.

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Structured message
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOT&lt;/span&gt;&lt;span class="sh"&gt;
      ⚠️ This variable is deprecated and will be removed in v2.0.

      Migration steps:
      1. Replace with 'ref_name' for the primary value
      2. See release notes here: https://example.com
&lt;/span&gt;&lt;span class="no"&gt;    EOT
&lt;/span&gt;  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference id for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Terraform plan gives you :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
terraform plan
var.ref_id
  Reference &lt;span class="nb"&gt;id &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;service 1

  Enter a value: 234dfeff-123213dsad-12dsad

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no
differences, so no changes are needed.
╷
│ Warning: Deprecated variable got a value
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt;:
│    1: variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
│    2:   &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string
│    3:   deprecated  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOT&lt;/span&gt;&lt;span class="sh"&gt;
│    4:       ⚠️ This variable is deprecated and will be removed in v2.0.
│    5:       Migration steps:
│    6:       1. Replace with 'ref_name' for the primary value
│    7:       2. See release notes here: https://example.com
│    8:     EOT
│
│ ⚠️ This variable is deprecated and will be removed in v2.0.
│
│ Migration steps:
│ 1. Replace with 'ref_name' for the primary value
│ 2. See release notes here: https://example.com
│
╵
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Function support ?
&lt;/h3&gt;

&lt;p&gt;Let's test this. OK. I know I am pushing the example here :) But let's see how the feature behaves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"This variable is deprecated and will be removed in v2.0."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference id for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Terraform plan/validate :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
╷
│ Error: Unsuitable value &lt;span class="nb"&gt;type&lt;/span&gt;
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt;:
│    3:   deprecated  &lt;span class="o"&gt;=&lt;/span&gt; title&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"This variable is deprecated and will be removed in v2.0."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
│
│ Unsuitable value: value must be known
╵
╷
│ Error: Function calls not allowed
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt;:
│    3:   deprecated  &lt;span class="o"&gt;=&lt;/span&gt; title&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"This variable is deprecated and will be removed in v2.0."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
│
│ Functions may not be called here.

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

&lt;/div&gt;



&lt;p&gt;Functions are not supported. But what about the &lt;code&gt;Unsuitable value: value must be known&lt;/code&gt; message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference id for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Aaah, the use of a function left the deprecated value to be null. Supported by the result below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
╷
│ Error: Unsuitable value &lt;span class="nb"&gt;type&lt;/span&gt;
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt;:
│    3:   deprecated  &lt;span class="o"&gt;=&lt;/span&gt; null
│
│ Unsuitable value: null value is not allowed
╵

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

&lt;/div&gt;



&lt;p&gt;Empty String:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="c1"&gt;# empty string is supported&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference id for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Empty string result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
terraform plan
var.ref_id
  Reference &lt;span class="nb"&gt;id &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;service 1

  Enter a value: 234dfeff-123213dsad-12dsad

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no
differences, so no changes are needed.
╷
│ Warning: Deprecated variable got a value
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt;:
│    1: variable &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
│    2:   &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string
│    3:   deprecated  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
│

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

&lt;/div&gt;



&lt;p&gt;So how have you handled deprecations of inputs in modules before ?&lt;/p&gt;

&lt;h3&gt;
  
  
  Module example
&lt;/h3&gt;

&lt;p&gt;The usage doesn't change much for variables from within a module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# modules/app/main.tf&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"⚠️ Pass 'ref_name' instead of the variable. This will be removed in v2.0"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference id for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference name for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Handle both old and new patterns during transition&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;actual_ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref_id&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref_id&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"app_with_deprecations"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/app"&lt;/span&gt;
  &lt;span class="nx"&gt;ref_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"234dfeff-123213dsad-12dsad"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;On Terraform plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no
differences, so no changes are needed.
╷
│ Warning: Deprecated variable got a value
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;module &lt;span class="s2"&gt;"app_with_deprecations"&lt;/span&gt;:
│    3:   ref_id &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"234dfeff-123213dsad-12dsad"&lt;/span&gt;
│
│ ⚠️ Pass &lt;span class="s1"&gt;'ref_name'&lt;/span&gt; instead of the variable. This will be removed &lt;span class="k"&gt;in &lt;/span&gt;v2.0
╵
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Outputs
&lt;/h2&gt;

&lt;p&gt;Lets switch to how the outputs behave in this case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Module outputs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# modules/app/main.tf&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"⚠️ Pass 'ref_name' instead of the variable. This will be removed in v2.0"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference id for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ref_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Reference name for service 1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;...&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"old_value"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref_id&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"⚠️ Pass 'name_value' instead of the output. This will be removed in v2.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"new_value"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ref_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Terraform plan or apply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;You can apply this plan to save these new output values to the Terraform state, without changing
any real infrastructure.
╷
│ Warning: Deprecated variable got a value
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;module &lt;span class="s2"&gt;"app_with_deprecations"&lt;/span&gt;:
│    3:   ref_id &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"234dfeff-123213dsad-12dsad"&lt;/span&gt;
│
│ ⚠️ Pass &lt;span class="s1"&gt;'ref_name'&lt;/span&gt; instead of the variable. This will be removed &lt;span class="k"&gt;in &lt;/span&gt;v2.0
│
│ &lt;span class="o"&gt;(&lt;/span&gt;and 2 more similar warnings elsewhere&lt;span class="o"&gt;)&lt;/span&gt;
╵
╷
│ Warning: Deprecated value used
│
│   on main.tf line 7, &lt;span class="k"&gt;in &lt;/span&gt;output &lt;span class="s2"&gt;"mod_out"&lt;/span&gt;:
│    7:   value &lt;span class="o"&gt;=&lt;/span&gt; module.app_with_deprecations.old_value
│
│   The deprecation originates from module.app_with_deprecations.old_value
│
│ ⚠️ Pass &lt;span class="s1"&gt;'name_value'&lt;/span&gt; instead of the output. This will be removed &lt;span class="k"&gt;in &lt;/span&gt;v2.0
│
│ &lt;span class="o"&gt;(&lt;/span&gt;and one more similar warning elsewhere&lt;span class="o"&gt;)&lt;/span&gt;
╵

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

mod_out &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"234dfeff-123213dsad-12dsad"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What about root module outputs ?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"mod_out"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Deprecated value"&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"This is not supported :) "&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Terraform plan or apply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;│ Error: Root module output deprecated
│
│   on main.tf line 3, &lt;span class="k"&gt;in &lt;/span&gt;output &lt;span class="s2"&gt;"mod_out"&lt;/span&gt;:
│    3:   deprecated &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"This is not supported :) "&lt;/span&gt;
│
│ Root module outputs cannot be deprecated, as there is no higher-level module to inform of the
│ deprecation.
╵

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

&lt;/div&gt;



&lt;p&gt;Only module outputs can be deprecated, not root module outputs. And as the message indicates, it makes sense as there is no higher level module which should know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deprecation Suppression flag
&lt;/h2&gt;

&lt;p&gt;I was a little confused at first on how this would work. It was a little bit of trial and error to see how the suppression works. And I did keep trying with the wrong flag too ( &lt;code&gt;suppress_deprecations_warnigns&lt;/code&gt; based on the PR ) In short : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;ignore_nested_deprecations&lt;/code&gt; attribute lets you suppress these internal warnings while still being alerted when you use deprecated outputs from the module.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Understanding &lt;code&gt;Internal&lt;/code&gt; vs &lt;code&gt;Your&lt;/code&gt; Warnings
&lt;/h3&gt;

&lt;p&gt;Let's create a module structure to demonstrate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# modules/legacy/nested/main.tf&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"deprecated_output"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"This nested output is deprecated"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nested-value"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# modules/legacy/main.tf&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"old_api"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;deprecated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Use new_api instead"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"legacy-value"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"new_api"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"new-value"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"nested"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./nested"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# This usage inside the module generates an "internal" warning&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;internal_use&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deprecated_output&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets test this across 4 scenarios&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am including both the responses from &lt;code&gt;terraform validate&lt;/code&gt; and &lt;code&gt;terraform plan&lt;/code&gt; on all these scenarios as the latter did show me the additional collapsed warning messages which I believe are due to my usage of the module output.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: No suppression, output defined&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"with_warnings"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/legacy"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"test_output"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;with_warnings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;old_api&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tf validate
╷
│ Warning: Deprecated value used
│
│   on main.tf line 10, &lt;span class="k"&gt;in &lt;/span&gt;output &lt;span class="s2"&gt;"test_output"&lt;/span&gt;:
│   10:   value &lt;span class="o"&gt;=&lt;/span&gt; module.with_warnings.old_api
│
│   The deprecation originates from module.with_warnings.old_api
│
│ Use new_api instead
│
│ &lt;span class="o"&gt;(&lt;/span&gt;and one more similar warning elsewhere&lt;span class="o"&gt;)&lt;/span&gt;
╵
Success! The configuration is valid, but there were some validation warnings as shown above.


experiments/deprecation_notice/test-example via 💠 default
❯ tf plan

Changes to Outputs:
  + test_output &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"legacy-value"&lt;/span&gt;

You can apply this plan to save these new output values to the Terraform state, without changing any real
infrastructure.
╷
│ Warning: Deprecated value used
│
│   on main.tf line 10, &lt;span class="k"&gt;in &lt;/span&gt;output &lt;span class="s2"&gt;"test_output"&lt;/span&gt;:
│   10:   value &lt;span class="o"&gt;=&lt;/span&gt; module.with_warnings.old_api
│
│   The deprecation originates from module.with_warnings.old_api
│
│ Use new_api instead
│
│ &lt;span class="o"&gt;(&lt;/span&gt;and 3 more similar warnings elsewhere&lt;span class="o"&gt;)&lt;/span&gt;
╵

──────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn&lt;span class="s1"&gt;'t use the -out option to save this plan, so Terraform can'&lt;/span&gt;t guarantee to take exactly these
actions &lt;span class="k"&gt;if &lt;/span&gt;you run &lt;span class="s2"&gt;"terraform apply"&lt;/span&gt; now.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;validate&lt;/code&gt; shows 2 warnings - your usage of deprecated output + internal module usage while &lt;code&gt;plan&lt;/code&gt; shows 4 in total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: No suppression, output commented&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"with_warnings"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/legacy"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# output "test_output" {&lt;/span&gt;
&lt;span class="c1"&gt;#   value = module.with_warnings.old_api&lt;/span&gt;
&lt;span class="c1"&gt;# }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tf validate
╷
│ Warning: Deprecated value used
│
│   on modules/legacy/main.tf line 16, &lt;span class="k"&gt;in &lt;/span&gt;locals:
│   16:   internal_use &lt;span class="o"&gt;=&lt;/span&gt; module.nested.deprecated_output
│
│   The deprecation originates from module.nested.deprecated_output
│
│ This nested output is deprecated
╵
Success! The configuration is valid, but there were some validation warnings as shown above.


experiments/deprecation_notice/test-example via 💠 default
❯ tf plan

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no
changes are needed.
╷
│ Warning: Deprecated value used
│
│   on modules/legacy/main.tf line 16, &lt;span class="k"&gt;in &lt;/span&gt;locals:
│   16:   internal_use &lt;span class="o"&gt;=&lt;/span&gt; module.nested.deprecated_output
│
│   The deprecation originates from module.nested.deprecated_output
│
│ This nested output is deprecated
│
│ &lt;span class="o"&gt;(&lt;/span&gt;and one more similar warning elsewhere&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;  &lt;code&gt;validate&lt;/code&gt; shows 1 warning - only the internal module's use of nested deprecated output. Plan shows 2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 3: Suppression enabled, output commented&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"suppressed"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;                     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/legacy"&lt;/span&gt;
  &lt;span class="nx"&gt;ignore_nested_deprecations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# output "test_output" {&lt;/span&gt;
&lt;span class="c1"&gt;#   value = module.suppressed.old_api&lt;/span&gt;
&lt;span class="c1"&gt;# }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tf validate
Success! The configuration is valid.


experiments/deprecation_notice/test-example via 💠 default
❯ tf plan

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no
changes are needed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Shows 0 warnings - internal deprecation suppressed, no root usage&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 4: Suppression enabled, output defined&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"suppressed"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;                     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/legacy"&lt;/span&gt;
  &lt;span class="nx"&gt;ignore_nested_deprecations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"test_output"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suppressed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;old_api&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tf validate
╷
│ Warning: Deprecated value used
│
│   on main.tf line 11, &lt;span class="k"&gt;in &lt;/span&gt;output &lt;span class="s2"&gt;"test_output"&lt;/span&gt;:
│   11:   value &lt;span class="o"&gt;=&lt;/span&gt; module.suppressed.old_api
│
│   The deprecation originates from module.suppressed.old_api
│
│ Use new_api instead
╵
Success! The configuration is valid, but there were some validation warnings as shown above.


experiments/deprecation_notice/test-example via 💠 default
❯ tf plan

Changes to Outputs:
  + test_output &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"legacy-value"&lt;/span&gt;

You can apply this plan to save these new output values to the Terraform state, without changing any real
infrastructure.
╷
│ Warning: Deprecated value used
│
│   on main.tf line 11, &lt;span class="k"&gt;in &lt;/span&gt;output &lt;span class="s2"&gt;"test_output"&lt;/span&gt;:
│   11:   value &lt;span class="o"&gt;=&lt;/span&gt; module.suppressed.old_api
│
│   The deprecation originates from module.suppressed.old_api
│
│ Use new_api instead
│
│ &lt;span class="o"&gt;(&lt;/span&gt;and one more similar warning elsewhere&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;validate&lt;/code&gt; shows 1 warning - your usage still warned, internal deprecation suppressed. Plan shows 2 as it was previously.&lt;/p&gt;

&lt;p&gt;I think it would come in handy if I am using third party modules with deprecations I can't fix or has changes which I don't technically use or care about in my usage. At the same time , I want deprecations in my own module to be surfaced to the users. Hopefully that helps to understand how the &lt;code&gt;ignore_nested_deprecations&lt;/code&gt; functions.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Terraform 1.15's deprecation feature addresses a pain point in module upgrades and management. It provides module authors with the tools they need to improve their interfaces while maintaining backward compatibility and giving users clear guidance on module usage.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>deprecation</category>
      <category>devops</category>
      <category>alpha</category>
    </item>
    <item>
      <title>Adding agent skills for Terraform</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Sat, 14 Feb 2026 14:09:23 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/adding-agent-skills-for-terraform-4e7m</link>
      <guid>https://forem.com/quixoticmonk/adding-agent-skills-for-terraform-4e7m</guid>
      <description>&lt;p&gt;MCP exploded. There is no better way to say that.  Every developer I knew had at least a handful of those MCP servers running with their coding agents. But the context overload with all those was massive and I found myself seeing messages around &lt;code&gt;context overflow&lt;/code&gt; a fair few times. I know for a fact that most of the MCP servers I have enabled and the tool definitions they have were not needed to be read by the agent all the time.&lt;/p&gt;

&lt;p&gt;So how do I solve this context bloat while ensuring I can give all the necessary information required for the model to give me that desired result? I have been hearing or reading about a few things which could help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skills ( was a Claude-only thing till it was made an open spec)&lt;/li&gt;
&lt;li&gt;Powers in kiro ( an IDE based approach and not yet in kiro CLI)&lt;/li&gt;
&lt;li&gt;Code execution with MCP server ( Maybe for another post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The curiosity around skills peaked with &lt;a href="https://github.com/antonbabenko/terraform-skill" rel="noopener noreferrer"&gt;Anton's terraform skills&lt;/a&gt; and the official &lt;a href="https://github.com/hashicorp/agent-skills" rel="noopener noreferrer"&gt;HashiCorp agent skills&lt;/a&gt;. My experience with using it with kiro CLI is documented &lt;a href="https://manuchandrasekhar.com/posts/2026/01-27-kiro-terraform-skills/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. But how do you create your own skill if one doesn't exist ? The process of testing and refining a new skill turned into a great learning experience about how these skills work in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what's the problem statement?
&lt;/h2&gt;

&lt;p&gt;I need a mechanism to effectively inform my agent to create the desired configuration to help bring existing cloud resources under Terraform management. If you hear, the first thing that comes to mind ( and should ) is &lt;code&gt;import&lt;/code&gt; statements or blocks. The &lt;code&gt;terraform-search-import&lt;/code&gt; skill I am discussing here aims to address that challenge.  I do feel there is a need for &lt;code&gt;No so green field&lt;/code&gt; series on infrastructure management.&lt;/p&gt;

&lt;p&gt;So how do did end up here ? Most organizations have infrastructure that wasn't originally created with Terraform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resources created through AWS Console, CLI, or other tools during some POC phase
OR&lt;/li&gt;
&lt;li&gt;Legacy infrastructure that predates Infrastructure as Code adoption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditionally, importing these resources into Terraform required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Manual discovery&lt;/strong&gt; - Finding all resources across your cloud accounts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual configuration writing&lt;/strong&gt; - Creating HCL resource blocks for each resource&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual import commands&lt;/strong&gt; - Running &lt;code&gt;terraform import&lt;/code&gt; for each resource individually&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trial and error&lt;/strong&gt; - Getting the resource configuration exactly right&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process is time-consuming, error-prone, and doesn't scale when you have dozens or hundreds of resources. Terraform introduced a step by step improvement on this approach with the config driven imports and then with &lt;code&gt;Terraform search&lt;/code&gt;. The intention with the skill is to handle these scenarios based on the user situation and version constraints they may have.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does it work ?
&lt;/h3&gt;

&lt;p&gt;Ideally, the pre-requisites around the version constraints are met and a user is able to leverage this skill to use Terraform's new Search functionality to automate the discovery and import:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automated Discovery&lt;/strong&gt;: Uses &lt;code&gt;.tfquery.hcl&lt;/code&gt; files to query for existing resources. These are provider dependent and not every provider has every list provider possible. AWSCC provider is an exception with support for more than 100s of resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Generation&lt;/strong&gt;: Creates Terraform resource blocks with current configurations automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Config driven import method&lt;/strong&gt;: Generates import blocks using the &lt;code&gt;terraform query&lt;/code&gt; subcommands.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I already had documented some instructions which I was using my agents to help import existing resources. All it needed was identifying the agent skills structure and then porting the necessary instructions when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Skill
&lt;/h2&gt;

&lt;p&gt;For testing, I needed to set up the &lt;code&gt;terraform-search-import&lt;/code&gt; skill in my local Kiro environment. Since this was a new skill which doesn't exist in the upstream package, I had to create it from scratch following the agent skill specification and using existing skills as reference. To be honest, the skills documentation is pretty straightforward when it comes to guiding you on what is expected.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note : If your &lt;code&gt;SKILL.md&lt;/code&gt; file is growing with examples and lots of external references, you should look at adding them under a directory called  &lt;code&gt;references&lt;/code&gt;. Agent skill specifications are able to load these reference documents in a lazy manner. Only when they are needed. This helps to reduce the context which is made available to the model. Overload of information doesn't always translate to the best results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Below were some of the steps specifically for kiro as the CLI references the skill documents as &lt;code&gt;Skill resources&lt;/code&gt;. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the skill directory structure:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.kiro/agents/terraform-search-import/references
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create the main skill file (&lt;code&gt;SKILL.md&lt;/code&gt;) with proper YAML frontmatter:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="s"&gt;---&lt;/span&gt;
   &lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terraform-search-import&lt;/span&gt;
   &lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Discover existing cloud resources using Terraform Search queries and bulk import them into Terraform management. Use when bringing unmanaged infrastructure under Terraform control, auditing cloud resources, or migrating to IaC.&lt;/span&gt;
   &lt;span class="s"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add any supporting documentation or examples in the &lt;code&gt;references/&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's be honest . Did I start this way ? Not really. I had 500+ lines of information in my original &lt;code&gt;SKILL.md&lt;/code&gt; file. One of the redundant items in my original &lt;code&gt;SKILL.md&lt;/code&gt; file were the scenarios I wanted to cover.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scenario where the list resource existed for a provider.

&lt;ul&gt;
&lt;li&gt;Follows the logic we outlined above.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Where a list resource didn't exist for the resource in a provider.

&lt;ul&gt;
&lt;li&gt;Gather the information using the Cloud provider's CLI or API and use the resource identity in a config driven import.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Where the Terraform version if below the required 1.14.0 version.

&lt;ul&gt;
&lt;li&gt;Gather the information using the Cloud provider's CLI or API and use the resource identity in a config driven import.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What list resources are supported ?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;How can we solve this ?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Well, you could have the Terraform MCP server look for the resources everytime we needed against a provider.
OR &lt;/li&gt;
&lt;li&gt;Look at the &lt;a href="https://github.com/quixoticmonk/extract-provider-supported-actions/blob/main/list-resources-report.json" rel="noopener noreferrer"&gt;supported list resources&lt;/a&gt; I maintain as an automated workflow.
OR&lt;/li&gt;
&lt;li&gt;Execute a deterministic script agains the providers in question. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you look at the &lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;Agent Skills specification&lt;/a&gt;, there is an additional optional directory a skill can hold.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/scripts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The repository I linked above for my list of provider supported list resources were essentially banking on the &lt;code&gt;terraform providers schema&lt;/code&gt; json file. The script in the repository identified the list/action resources for each provider schema we fed into the script. How do we get that ? Add a provider block into a .tf file and run &lt;code&gt;terraform init&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So what if I moved the list resource discovery into a deterministic script ? So the MCP server doesn't need to do this either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Skill in Practice
&lt;/h2&gt;

&lt;p&gt;So we have the following resources in the directory kiro CLI is expecting them to be in :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SKILL.md&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/references&lt;/code&gt; for edge cases and additional scenarios&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/scripts&lt;/code&gt; to hold scripts( if needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copying this into the &lt;code&gt;.kiro/agents/skills&lt;/code&gt; directory is all I needed to have kiro-cli identify it as an available skill on the next session. It helps that the agent configuration was using wild characters in the &lt;code&gt;skills&lt;/code&gt; resource block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"skill://~/.kiro/skills/**/SKILL.md"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Actual tests
&lt;/h3&gt;

&lt;p&gt;Lets run through couple of scenarios.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test 1 : Supported resources and TF version above 1.14.0&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;With the skill created and installed( or added to the directory ), I tested it with the request "Create the terraform configuration for importing the S3 buckets from my AWS account."&lt;/p&gt;

&lt;p&gt;Here's the complete session log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Create the terraform configuration &lt;span class="k"&gt;for &lt;/span&gt;importing the S3 buckets from my AWS account.
I&lt;span class="s1"&gt;'ll help you create Terraform configuration to import existing S3 buckets from your AWS account. Let me use the terraform-search-import skill to discover and import
your S3 buckets.
Reading file: /Users/user_name/.kiro/skills/terraform-search-import/SKILL.md, all lines (using tool: read)
Allow this action? Use '&lt;/span&gt;t&lt;span class="s1"&gt;' to trust (always allow) this tool for the session. [y/n/t]:

[iac] &amp;gt; t

✓ Successfully read 9116 bytes from /Users/user_name/.kiro/skills/terraform-search-import/SKILL.md

Completed in 0.1s
Now I'&lt;/span&gt;ll create a minimal Terraform configuration to discover and import your S3 buckets. First, &lt;span class="nb"&gt;let &lt;/span&gt;me check what list resources are available &lt;span class="k"&gt;for &lt;/span&gt;the AWS provider:
I&lt;span class="s1"&gt;'ll create the following file: /Users/user_name/Documents/2026/experiments/agent-skills/terraform.tf (using tool: write)
Purpose: Create basic Terraform configuration with AWS provider

1: terraform {
2: required_version = "&amp;gt;= 1.14"
3: required_providers {
4: aws = {
5: source = "hashicorp/aws"
6: version = "~&amp;gt; 6.0"
7: }
8: }
9: }
Allow this action? Use '&lt;/span&gt;t&lt;span class="s1"&gt;' to trust (always allow) this tool for the session. [y/n/t]:

[iac] &amp;gt; t

Creating: /Users/user_name/Documents/2026/experiments/agent-skills/terraform.tf

Completed in 0.3s
I will run the following command: cd /Users/user_name/Documents/2026/experiments/agent-skills &amp;amp;&amp;amp; terraform init (using tool: shell)
Purpose: Initialize Terraform to download AWS provider

Allow this action? Use '&lt;/span&gt;t&lt;span class="s1"&gt;' to trust (always allow) this tool for the session. [y/n/t]:

[iac] &amp;gt; t

Initializing the backend...
Initializing provider plugins...

Finding hashicorp/aws versions matching "~&amp;gt; 6.0"...
Installing hashicorp/aws v6.31.0...
Installed hashicorp/aws v6.31.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Completed in 25.674s
Now let me check what list resources are available for S3:
I will run the following command: cd /Users/user_name/Documents/2026/experiments/agent-skills &amp;amp;&amp;amp; terraform providers schema -json | jq '&lt;/span&gt;.provider_schemas.&lt;span class="s2"&gt;"registry.terraform.io/hashicorp/aws"&lt;/span&gt;.list_resource_schemas | keys | .[]&lt;span class="s1"&gt;' | grep -i s3 (using tool: shell)
Purpose: Check if S3 bucket list resources are supported

"aws_s3_bucket"
"aws_s3_bucket_acl"
"aws_s3_bucket_policy"
"aws_s3_bucket_public_access_block"
"aws_s3_object"

Completed in 2.69s
Great! The AWS provider supports aws_s3_bucket list resources. Now I'&lt;/span&gt;ll create the query file to discover your S3 buckets:
I&lt;span class="s1"&gt;'ll create the following file: /Users/user_name/Documents/2026/experiments/agent-skills/discovery.tfquery.hcl (using tool: write)
Purpose: Create Terraform query file to discover all S3 buckets

1: provider "aws" {
2: region = "us-east-1"
3: }
4:
5: list "aws_s3_bucket" "all" {
6: provider = aws
7: }
Creating: /Users/user_name/Documents/2026/experiments/agent-skills/discovery.tfquery.hcl

Completed in 0.1s
Now let'&lt;/span&gt;s run the query to discover your S3 buckets:
I will run the following &lt;span class="nb"&gt;command&lt;/span&gt;: &lt;span class="nb"&gt;cd&lt;/span&gt; /Users/user_name/Documents/2026/experiments/agent-skills &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; terraform query &lt;span class="o"&gt;(&lt;/span&gt;using tool: shell&lt;span class="o"&gt;)&lt;/span&gt;
Purpose: Execute Terraform query to discover S3 buckets

list.aws_s3_bucket.all &lt;span class="nv"&gt;account_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;############,bucket=agentcore-cicd-terraform-state-d1f38556,region=us-east-1 agentcore-cicd-terraform-state-d1f38556&lt;/span&gt;
list.aws_s3_bucket.all &lt;span class="nv"&gt;account_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;############,bucket=aws-glue-assets-############-us-east-1,region=us-east-1 aws-glue-assets-############-us-east-1&lt;/span&gt;
list.aws_s3_bucket.all &lt;span class="nv"&gt;account_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;############,bucket=awscc-tool-use-2fqr-assets,region=us-east-1 awscc-tool-use-2fqr-assets&lt;/span&gt;
list.aws_s3_bucket.all &lt;span class="nv"&gt;account_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;############,bucket=bedrock-s3-kb,region=us-east-1 bedrock-s3-kb&lt;/span&gt;
list.aws_s3_bucket.all &lt;span class="nv"&gt;account_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;############,bucket=bedrock-video-generation-us-east-1-ymeqc5,region=us-east-1 bedrock-video-generation-us-east-1-ymeqc5&lt;/span&gt;

Completed &lt;span class="k"&gt;in &lt;/span&gt;76.725s
Perfect! Now &lt;span class="nb"&gt;let&lt;/span&gt;&lt;span class="s1"&gt;'s generate the Terraform configuration for importing all these S3 buckets:
I will run the following command: cd /Users/user_name/Documents/2026/experiments/agent-skills &amp;amp;&amp;amp; terraform query -generate-config-out=s3-buckets.tf (using tool: shell)
Purpose: Generate Terraform configuration for importing S3 buckets

list.aws_s3_bucket.all account_id=############,bucket=agentcore-cicd-terraform-state-d1f38556,region=us-east-1 agentcore-cicd-terraform-state-d1f38556
list.aws_s3_bucket.all account_id=############,bucket=aws-glue-assets-############-us-east-1,region=us-east-1 aws-glue-assets-############-us-east-1
list.aws_s3_bucket.all account_id=############,bucket=awscc-tool-use-2fqr-assets,region=us-east-1 awscc-tool-use-2fqr-assets
list.aws_s3_bucket.all account_id=############,bucket=bedrock-s3-kb,region=us-east-1 bedrock-s3-kb
list.aws_s3_bucket.all account_id=############,bucket=bedrock-video-generation-us-east-1-ymeqc5,region=us-east-1 bedrock-video-generation-us-east-1-ymeqc5
list.aws_s3_bucket.all account_id=############,bucket=cdk-hnb659fds-assets-############-us-east-1,region=us-east-1 cdk-hnb659fds-assets-############-us-east-1
list.aws_s3_bucket.all account_id=############,bucket=cf-templates-1xfr5l1bee3rm-us-east-1,region=us-east-1 cf-templates-1xfr5l1bee3rm-us-east-1

Completed in 57.567s
Let me check the generated configuration file:
Reading file: /Users/user_name/Documents/2026/experiments/agent-skills/s3-buckets.tf, from line 1 to 50 (using tool: read)
✓ Successfully read 1221 bytes from /Users/user_name/Documents/2026/experiments/agent-skills/s3-buckets.tf

Completed in 0.0s
Perfect! I'&lt;/span&gt;ve successfully created a Terraform configuration to import all your S3 buckets. Here&lt;span class="s1"&gt;'s what was generated:

Files Created:
terraform.tf - Provider configuration
discovery.tfquery.hcl - Query file to discover S3 buckets
s3-buckets.tf - Generated configuration with 36 S3 buckets and their import blocks
What'&lt;/span&gt;s Included:
Resource blocks &lt;span class="k"&gt;for &lt;/span&gt;all 36 S3 buckets &lt;span class="k"&gt;in &lt;/span&gt;your account
Import blocks using identity-based import &lt;span class="o"&gt;(&lt;/span&gt;Terraform 1.12+&lt;span class="o"&gt;)&lt;/span&gt;
All current bucket configurations including tags, settings, and timeouts
Next Steps:
Review the generated configuration &lt;span class="k"&gt;in &lt;/span&gt;s3-buckets.tf
Clean up the configuration by removing computed attributes and null values
Run the import:
bash
terraform plan
terraform apply
The configuration includes buckets &lt;span class="k"&gt;for &lt;/span&gt;various services like:

Terraform state buckets
AWS service buckets &lt;span class="o"&gt;(&lt;/span&gt;Glue, SageMaker, CodePipeline&lt;span class="o"&gt;)&lt;/span&gt;
Application buckets
CDK and CloudFormation assets
All buckets are ready to be imported into Terraform state management using the modern identity-based import method.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire process took about 2 minutes and successfully discovered 36 S3 buckets across my account, generating complete Terraform configuration ready for import.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test 2 : TF version below 1.14.0&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Write the necessary Terraform configuration to import my existing S3 buckets from the AWS account into Terraform management. The S3 buckets to import have a prefix of awscc-tool. 

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; I&lt;span class="s1"&gt;'ll help you import your existing S3 buckets with the "awscc-tool" prefix into Terraform management. Let me use the terraform-search-import skill to discover and
import these resources.
Reading file: /Users/manuchn/.kiro/skills/terraform-search-import/SKILL.md, all lines (using tool: read)
 ✓ Successfully read 8591 bytes from /Users/manuchn/.kiro/skills/terraform-search-import/SKILL.md
 - Completed in 0.0s

&amp;gt; Now I'&lt;/span&gt;ll check what list resources are available &lt;span class="k"&gt;for &lt;/span&gt;S3 buckets and create the necessary configuration to discover and import your S3 buckets with the &lt;span class="s2"&gt;"awscc-tool"&lt;/span&gt;
prefix.
I will run the following &lt;span class="nb"&gt;command&lt;/span&gt;: terraform version &lt;span class="o"&gt;(&lt;/span&gt;using tool: shell&lt;span class="o"&gt;)&lt;/span&gt;
Purpose: Check Terraform version to ensure it supports Terraform Search &lt;span class="o"&gt;(&lt;/span&gt;requires &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 1.14&lt;span class="o"&gt;)&lt;/span&gt;

Terraform v1.13.0
on darwin_arm64
+ provider registry.terraform.io/hashicorp/aws v6.31.0

Your version of Terraform is out of &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; The latest version
is 1.14.4. You can update by downloading from https://developer.hashicorp.com/terraform/install
 - Completed &lt;span class="k"&gt;in &lt;/span&gt;0.136s

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Since your Terraform version is 1.13.0 &lt;span class="o"&gt;(&lt;/span&gt;below the required 1.14 &lt;span class="k"&gt;for &lt;/span&gt;Terraform Search&lt;span class="o"&gt;)&lt;/span&gt;, I&lt;span class="s1"&gt;'ll use the AWS CLI approach to discover your S3 buckets and generate the
import configuration manually.
Running aws cli command (using tool: aws):

Service name: s3api
Operation name: list-buckets
Parameters:
Region: us-east-1
Label: List all S3 buckets to find ones with awscc-tool prefix - Completed in 2.165s

&amp;gt; Perfect! I found 3 S3 buckets with the "awscc-tool" prefix:
1. awscc-tool-use-2fqr-assets
2. awscc-tool-use-ezct-artifacts
3. awscc-tool-use-ezct-assets

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

&lt;/div&gt;



&lt;p&gt;It correctly identified that the Terraform version was below 1.14.0 and the query command wouldn't be supported in that case. I skipped the rest of the steps in the snippet above as kiro went on to add the config driven import blocks based on the names retrieved with the S3 API. &lt;/p&gt;

&lt;h1&gt;
  
  
  Effectiveness of what you wrote
&lt;/h1&gt;

&lt;p&gt;As a non-native English speaker, I do struggle with finding the right balance in my writing- either over-explaniing or being too brief.So how do I make this effective ? This is an area I didn't have any experience or idea about. The &lt;a href="https://www.hashicorp.com/en/blog/introducing-hashicorp-agent-skills" rel="noopener noreferrer"&gt;HashiCorp blog &lt;/a&gt; about the agent-skills mentioned &lt;a href="https://tessl.io/" rel="noopener noreferrer"&gt;tessl&lt;/a&gt; being used to evaluate and improve the skills they had written. So after the initial bit of testing with the copied skill, I started looking at &lt;code&gt;tessl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You have a few ways to do this &lt;a href="https://docs.tessl.io/evaluate/evaluating-skills" rel="noopener noreferrer"&gt;evaluation&lt;/a&gt;. If you use the &lt;code&gt;tessl&lt;/code&gt; npm package, you can publish the skill on to the registry which is auto evaluated. Or as in my case, run the evaluation locally since I am submitting this as an additional skill to the existing agent-skills bundle under HashiCorp namespace.&lt;/p&gt;

&lt;p&gt;As you would have figured, I went the latter route.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;As with any npm library, it is as simple as :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @tessl/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review
&lt;/h3&gt;

&lt;p&gt;Let's review using the &lt;code&gt;tessl skill review&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tessl skill review  ~/.kiro/skills/terraform-search-import

Validation Checks

  ✔ skill_md_line_count - SKILL.md line count is 374 &lt;span class="o"&gt;(&lt;/span&gt;&amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; 500&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ frontmatter_valid - YAML frontmatter is valid
  ✔ name_field - &lt;span class="s1"&gt;'name'&lt;/span&gt; field is valid: &lt;span class="s1"&gt;'terraform-search-import'&lt;/span&gt;
  ✔ description_field - &lt;span class="s1"&gt;'description'&lt;/span&gt; field is valid &lt;span class="o"&gt;(&lt;/span&gt;227 chars&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ description_voice - &lt;span class="s1"&gt;'description'&lt;/span&gt; uses third person voice
  ✔ description_trigger_hint - Description includes an explicit trigger hint
  ✔ compatibility_field - &lt;span class="s1"&gt;'compatibility'&lt;/span&gt; field is valid &lt;span class="o"&gt;(&lt;/span&gt;104 chars&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ allowed_tools_field - &lt;span class="s1"&gt;'allowed-tools'&lt;/span&gt; field not present &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ metadata_version - &lt;span class="s1"&gt;'metadata.version'&lt;/span&gt; is present: 0.1.0
  ✔ metadata_field - &lt;span class="s1"&gt;'metadata'&lt;/span&gt; contains 2 entries
  ⚠ license_field - &lt;span class="s1"&gt;'license'&lt;/span&gt; field is missing
  ✔ frontmatter_unknown_keys - No unknown frontmatter keys found
  ✔ body_present - SKILL.md body is present
  ✔ body_examples - Examples detected &lt;span class="o"&gt;(&lt;/span&gt;code fence or &lt;span class="s1"&gt;'Example'&lt;/span&gt; wording&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ body_output_format - Output/return/format terms detected
  ✔ body_steps - Step-by-step structure detected &lt;span class="o"&gt;(&lt;/span&gt;ordered list&lt;span class="o"&gt;)&lt;/span&gt;

Overall: PASSED &lt;span class="o"&gt;(&lt;/span&gt;0 errors, 1 warnings&lt;span class="o"&gt;)&lt;/span&gt;

Judge Evaluation

  Description: 100%
    specificity: 3/3 - Lists multiple specific concrete actions: &lt;span class="s1"&gt;'Discover existing cloud resources'&lt;/span&gt;, &lt;span class="s1"&gt;'Terraform Search queries'&lt;/span&gt;, &lt;span class="s1"&gt;'bulk import them into Terraform management'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; These are clear, actionable capabilities.
    trigger_term_quality: 3/3 - Includes natural keywords &lt;span class="nb"&gt;users &lt;/span&gt;would say: &lt;span class="s1"&gt;'cloud resources'&lt;/span&gt;, &lt;span class="s1"&gt;'Terraform'&lt;/span&gt;, &lt;span class="s1"&gt;'import'&lt;/span&gt;, &lt;span class="s1"&gt;'unmanaged infrastructure'&lt;/span&gt;, &lt;span class="s1"&gt;'IaC'&lt;/span&gt;, &lt;span class="s1"&gt;'auditing'&lt;/span&gt;, &lt;span class="s1"&gt;'migrating'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Good coverage of terms &lt;span class="k"&gt;for &lt;/span&gt;infrastructure-as-code workflows.
    completeness: 3/3 - Clearly answers both what &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Discover existing cloud resources using Terraform Search queries and bulk import them'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; AND when &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Use when bringing unmanaged infrastructure under Terraform control, auditing cloud resources, or migrating to IaC'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
    distinctiveness_conflict_risk: 3/3 - Clear niche focused specifically on Terraform import/discovery workflows. The combination of &lt;span class="s1"&gt;'Terraform Search'&lt;/span&gt;, &lt;span class="s1"&gt;'bulk import'&lt;/span&gt;, and &lt;span class="s1"&gt;'unmanaged infrastructure'&lt;/span&gt; creates distinct triggers unlikely to conflict with general Terraform or cloud skills.

    Assessment: This is a well-crafted skill description that excels across all dimensions. It provides specific actions &lt;span class="o"&gt;(&lt;/span&gt;discover, search, bulk import&lt;span class="o"&gt;)&lt;/span&gt;, includes natural trigger terms &lt;span class="nb"&gt;users &lt;/span&gt;would actually say, explicitly states both capabilities and usage scenarios, and carves out a distinct niche within the Terraform ecosystem focused on import workflows.

  Content: 77%
    conciseness: 2/3 - The content is mostly efficient but includes some redundancy - the workflow is explained multiple &lt;span class="nb"&gt;times&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;overview section, &lt;span class="k"&gt;then &lt;/span&gt;detailed sections, &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;complete &lt;/span&gt;example&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Some sections like &lt;span class="s1"&gt;'When to Use'&lt;/span&gt; list items that are somewhat obvious given the skill description.
    actionability: 3/3 - Excellent executable examples throughout - &lt;span class="nb"&gt;complete &lt;/span&gt;HCL configurations, bash commands, and a full end-to-end workflow example. Code is copy-paste ready with realistic values and clear syntax.
    workflow_clarity: 3/3 - Clear 5-step workflow with explicit sequence. Includes validation checkpoint &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Review and refine generated resource and import blocks'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; and the &lt;span class="s1"&gt;'Best Practices'&lt;/span&gt; section emphasizes running plan before apply and backing up state. The &lt;span class="nb"&gt;complete &lt;/span&gt;example at the end reinforces the workflow.
    progressive_disclosure: 2/3 - Good structure with clear sections and a reference table, but the content is quite long &lt;span class="o"&gt;(&lt;/span&gt;~300 lines&lt;span class="o"&gt;)&lt;/span&gt; and could benefit from splitting detailed examples or the troubleshooting table into separate reference files. The external references at the top are appropriate but internal organization could be improved.

    Assessment: This is a well-structured, highly actionable skill with excellent executable examples and clear workflow guidance. The main weakness is length - the content could be more concise by removing some redundancy &lt;span class="o"&gt;(&lt;/span&gt;workflow explained multiple &lt;span class="nb"&gt;times&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; and potentially splitting detailed reference material into separate files. The safety considerations around state management are appropriately emphasized.

    Suggestions:
      - Consider moving the detailed &lt;span class="s1"&gt;'Query Examples'&lt;/span&gt; section to a separate EXAMPLES.md file and keeping only 1-2 examples inline
      - Remove the &lt;span class="s1"&gt;'When to Use'&lt;/span&gt; section as it duplicates the skill description and is self-evident from the content

Average Score: 89%

✔ Skill evaluation completed successfully!

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

&lt;/div&gt;



&lt;p&gt;Not bad for an initial skill reference. Re-run after making some changes it suggested.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ tessl skill review  ~/.kiro/skills/terraform-search-import

Validation Checks

  ✔ skill_md_line_count - SKILL.md line count is 369 &lt;span class="o"&gt;(&lt;/span&gt;&amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; 500&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ frontmatter_valid - YAML frontmatter is valid
  ✔ name_field - &lt;span class="s1"&gt;'name'&lt;/span&gt; field is valid: &lt;span class="s1"&gt;'terraform-search-import'&lt;/span&gt;
  ✔ description_field - &lt;span class="s1"&gt;'description'&lt;/span&gt; field is valid &lt;span class="o"&gt;(&lt;/span&gt;227 chars&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ description_voice - &lt;span class="s1"&gt;'description'&lt;/span&gt; uses third person voice
  ✔ description_trigger_hint - Description includes an explicit trigger hint
  ✔ compatibility_field - &lt;span class="s1"&gt;'compatibility'&lt;/span&gt; field is valid &lt;span class="o"&gt;(&lt;/span&gt;104 chars&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ allowed_tools_field - &lt;span class="s1"&gt;'allowed-tools'&lt;/span&gt; field not present &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ metadata_version - &lt;span class="s1"&gt;'metadata.version'&lt;/span&gt; is present: 0.1.0
  ✔ metadata_field - &lt;span class="s1"&gt;'metadata'&lt;/span&gt; contains 2 entries
  ⚠ license_field - &lt;span class="s1"&gt;'license'&lt;/span&gt; field is missing
  ✔ frontmatter_unknown_keys - No unknown frontmatter keys found
  ✔ body_present - SKILL.md body is present
  ✔ body_examples - Examples detected &lt;span class="o"&gt;(&lt;/span&gt;code fence or &lt;span class="s1"&gt;'Example'&lt;/span&gt; wording&lt;span class="o"&gt;)&lt;/span&gt;
  ✔ body_output_format - Output/return/format terms detected
  ✔ body_steps - Step-by-step structure detected &lt;span class="o"&gt;(&lt;/span&gt;ordered list&lt;span class="o"&gt;)&lt;/span&gt;

Overall: PASSED &lt;span class="o"&gt;(&lt;/span&gt;0 errors, 1 warnings&lt;span class="o"&gt;)&lt;/span&gt;

Judge Evaluation

  Description: 100%
    specificity: 3/3 - Lists multiple specific concrete actions: &lt;span class="s1"&gt;'Discover existing cloud resources'&lt;/span&gt;, &lt;span class="s1"&gt;'Terraform Search queries'&lt;/span&gt;, &lt;span class="s1"&gt;'bulk import them into Terraform management'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; These are clear, actionable capabilities.
    trigger_term_quality: 3/3 - Includes natural keywords &lt;span class="nb"&gt;users &lt;/span&gt;would say: &lt;span class="s1"&gt;'cloud resources'&lt;/span&gt;, &lt;span class="s1"&gt;'Terraform'&lt;/span&gt;, &lt;span class="s1"&gt;'import'&lt;/span&gt;, &lt;span class="s1"&gt;'unmanaged infrastructure'&lt;/span&gt;, &lt;span class="s1"&gt;'IaC'&lt;/span&gt;, &lt;span class="s1"&gt;'auditing'&lt;/span&gt;, &lt;span class="s1"&gt;'migrating'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Good coverage of terms &lt;span class="k"&gt;for &lt;/span&gt;infrastructure-as-code workflows.
    completeness: 3/3 - Clearly answers both what &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Discover existing cloud resources using Terraform Search queries and bulk import them'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; AND when &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Use when bringing unmanaged infrastructure under Terraform control, auditing cloud resources, or migrating to IaC'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
    distinctiveness_conflict_risk: 3/3 - Clear niche focused specifically on Terraform import/discovery workflows. The combination of &lt;span class="s1"&gt;'Terraform Search'&lt;/span&gt;, &lt;span class="s1"&gt;'bulk import'&lt;/span&gt;, and &lt;span class="s1"&gt;'unmanaged infrastructure'&lt;/span&gt; creates distinct triggers unlikely to conflict with general Terraform or cloud skills.

    Assessment: This is a well-crafted skill description that excels across all dimensions. It provides specific actions &lt;span class="o"&gt;(&lt;/span&gt;discover, search, bulk import&lt;span class="o"&gt;)&lt;/span&gt;, includes natural trigger terms &lt;span class="nb"&gt;users &lt;/span&gt;would actually say, explicitly states both capabilities and usage scenarios, and carves out a distinct niche within the Terraform ecosystem focused on import workflows.
  Content: 85%
    conciseness: 2/3 - The content is mostly efficient but includes some redundancy - the prerequisites section repeats the discovery script information, and some explanations &lt;span class="o"&gt;(&lt;/span&gt;like the query output format&lt;span class="o"&gt;)&lt;/span&gt; could be more compact. The post-generation cleanup section with before/after examples is valuable but slightly verbose.
    actionability: 3/3 - Excellent executable guidance throughout with &lt;span class="nb"&gt;complete&lt;/span&gt;, copy-paste ready HCL configurations and bash commands. The &lt;span class="nb"&gt;complete &lt;/span&gt;example at the end provides a full workflow with real commands. All code blocks are executable, not pseudocode.
    workflow_clarity: 3/3 - Clear decision tree upfront, explicit 5-step workflow, and the &lt;span class="nb"&gt;complete &lt;/span&gt;example shows the exact sequence with validation steps &lt;span class="o"&gt;(&lt;/span&gt;terraform plan before apply&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; The &lt;span class="s1"&gt;'Check Provider Support First'&lt;/span&gt; section with IMPORTANT callout establishes a critical validation checkpoint before proceeding.
    progressive_disclosure: 3/3 - Well-structured with clear sections progressing from prerequisites to basic to advanced usage. References external documentation appropriately &lt;span class="o"&gt;(&lt;/span&gt;HashiCorp docs, MANUAL-IMPORT.md &lt;span class="k"&gt;for &lt;/span&gt;unsupported resources&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Content is appropriately organized without being monolithic or requiring deep navigation.

    Assessment: This is a strong, well-structured skill that provides actionable guidance &lt;span class="k"&gt;for &lt;/span&gt;Terraform resource discovery and bulk import. The decision tree and explicit workflow steps make it easy to follow, and the code examples are &lt;span class="nb"&gt;complete &lt;/span&gt;and executable. Minor verbosity &lt;span class="k"&gt;in &lt;/span&gt;some sections &lt;span class="o"&gt;(&lt;/span&gt;prerequisites, cleanup examples&lt;span class="o"&gt;)&lt;/span&gt; prevents a perfect conciseness score, but overall the content earns its place.

Average Score: 93%

✔ Skill evaluation completed successfully!

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

&lt;/div&gt;



&lt;p&gt;I left it there without over optimizing it based on some feedback notes I saw. Why do I feel this is important ? Working with an open spec, a tool like this can guide you on how you can make your skills effective. Keep your tests handy and re-run after changes you make. Sometimes it is just a change in order which makes a world of difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learnings
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If I have to start on a new agent skill tomorrow ( without some basline ) I would probably one of the below approaches: 

&lt;ul&gt;
&lt;li&gt;tessl gives you &lt;code&gt;tessl skill new&lt;/code&gt; workflow with the npm library to get you started.&lt;/li&gt;
&lt;li&gt;Anthropic gives you a &lt;a href="https://skills.sh/anthropics/skills/skill-creator" rel="noopener noreferrer"&gt;skill creator skill&lt;/a&gt; which you can possible use to start. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;As with any of these instructions, you have to be clear about the versions your features are available in. &lt;code&gt;Terraform search&lt;/code&gt; requires the user to have both terraform versions above 1.14.0 and using a provider version which has list resource support. 

&lt;ul&gt;
&lt;li&gt;I had to explicitly mention to remove version constraints on the provider block to get the latest version always in the instructions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Compatibility requirements : I had mixed results with this parameter which I am keeping for now and will revisit later to refine further if needed.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It was a fun experiment to see some of my existing instructions I had in my documents could be used to create a skill to import existing resources under Terraform management. The effort required in this case was minimal considering I had the instructions in various markdowns in my machine. &lt;code&gt;tessl&lt;/code&gt; review helps to know what else could be optimized to efficiently manage what the model sees when it is made aware of the available skills. What's next ? I think the &lt;code&gt;ephemerality&lt;/code&gt; and &lt;code&gt;actions&lt;/code&gt; based approaches could use some nudge among practitioners.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note : As of today, my changes are on an &lt;a href="https://github.com/hashicorp/agent-skills/pull/23" rel="noopener noreferrer"&gt;active PR&lt;/a&gt; being reviewed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kiro CLI skill resources : &lt;a href="https://kiro.dev/docs/cli/custom-agents/configuration-reference/#skill-resources" rel="noopener noreferrer"&gt;https://kiro.dev/docs/cli/custom-agents/configuration-reference/#skill-resources&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Context engineering : &lt;a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" rel="noopener noreferrer"&gt;https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>hashicorp</category>
      <category>skills</category>
      <category>ai</category>
    </item>
    <item>
      <title>Kiro has Terraform skills</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Tue, 03 Feb 2026 04:14:53 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/kiro-has-terraform-skills-3h7n</link>
      <guid>https://forem.com/quixoticmonk/kiro-has-terraform-skills-3h7n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cross posted from &lt;a href="https://manuchandrasekhar.com/posts/2026/01-27-kiro-terraform-skills/" rel="noopener noreferrer"&gt;https://manuchandrasekhar.com/posts/2026/01-27-kiro-terraform-skills/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Powers and now Skills. Kiro's &lt;a href="https://kiro.dev/changelog/cli/1-24/" rel="noopener noreferrer"&gt;1.24.0 version &lt;/a&gt; just added support for &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;Skills&lt;/a&gt;, the new open spec for giving agents new capabilities and expertise. In a way. Let me explain.&lt;/p&gt;

&lt;p&gt;Naturally, I started using the kiro skills when the HashiCorp Sr PM for Agentic Workflows and a dear friend Gautam pinged asking if I tried the HashiCorp agent skills. Here is how I got along with it on kiro-cli which is my daily driver.&lt;/p&gt;

&lt;p&gt;HashiCorp's &lt;a href="https://github.com/hashicorp/agent-skills" rel="noopener noreferrer"&gt;agent-skills&lt;/a&gt; repository takes a more targeted approach - providing specialized skills for Terraform development that can be integrated into various AI coding assistants.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are HashiCorp Agent Skills?
&lt;/h2&gt;

&lt;p&gt;HashiCorp Agent Skills is a collection of specialized knowledge modules designed to enhance AI agents' capabilities with HashiCorp products. Currently focused on Terraform, these skills can be integrated into most AI development tools.&lt;/p&gt;

&lt;p&gt;The repository provides three main Terraform-focused plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;terraform-code-generation&lt;/strong&gt;: Core HCL code generation following HashiCorp style conventions and Terraform test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;terraform-module-generation&lt;/strong&gt;: Module refactoring and Terraform Stacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;terraform-provider-development&lt;/strong&gt;: If you are a provider developer or contributor, you should be looking at this. If you remember my HashiConf talks on &lt;a href="https://www.youtube.com/watch?v=97chxoMYMp0" rel="noopener noreferrer"&gt;Making AI work for you&lt;/a&gt; where we discussed how kiro ( then Q) was assisting provider development teams with Terraform actions which was an unknown construct at that point. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So why should you care ?
&lt;/h2&gt;

&lt;p&gt;Skills, originally introduced by Anthropic is a &lt;code&gt;new open spec for giving agents new capabilities and expertise&lt;/code&gt;. We can do with a lot more openness and consistency across these different ways of interacting with Generative AI agents. Sure, it is a markdown and you have the flexibility to add whatever details you need. But how much is always the question. And should all of it get loaded into context when the agent is starting up? If you use Terraform as a daily driver for your infrastructure provisioning, the HashiCorp suite of skills is something you should absolutely be considering with how it makes that context available for your agentic interactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you use it ?
&lt;/h2&gt;

&lt;p&gt;Most of the skills, rules, instructions, MCP server are supposed to make our questions without a lot of context build that up as you interact with the coding agent of your choice. Much of what I am saying here is available in the public documentation; hence adding only the relevant ones for kiro-cli.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Skills Installation&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;If you have npx on your machine, it is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add hashicorp/agent-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you can install a specific skill. Keep in mind that the installation step above does allow you to pick and choose the skills you want to install.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install a specific skill&lt;/span&gt;
npx skills add hashicorp/agent-skills/terraform/code-generation/skills/terraform-style-guide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skills terminal interface shows the source of the repository &lt;br&gt;
from which you are installing these skills.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uhuu2aefjzzisyykofa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uhuu2aefjzzisyykofa.png" alt="Skills installation interface showing repository source" width="766" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the ones you are interested in.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnk1uaf0zwwkq2fsvvoyu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnk1uaf0zwwkq2fsvvoyu.png" alt="Available skills" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now this is something I got confused about. I didn't have 28 agents ( or did I ?)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;◇  Detected 28 agents
│
◆  Install to
│  ● All agents (Recommended) (Install to all 28 detected agents)
│  ○ Select specific agents
└

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

&lt;/div&gt;



&lt;p&gt;These are not the custom kiro-cli agents, but the coding agents you use like Kiro, Kilo Code, Goose, Claude Code and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs2ys9t9p9l1ifvis3b3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs2ys9t9p9l1ifvis3b3.png" alt=" " width="620" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0llwh6nd4wq3ilopzl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0llwh6nd4wq3ilopzl0.png" alt="Agent selection interface showing detected coding agents" width="542" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note the directory it is referencing here. This is the .kiro directory in your user space on a Mac.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;◆  Select agents to install skills to (space to toggle)
│  ◻ Antigravity
│  ◻ Claude Code
│  ◻ Clawdbot
│  ◻ Cline
...
│  ◻ Goose
│  ◻ Kilo Code
│  ◼ Kiro CLI (.kiro/skills)
│  ◻ MCPJam
│  ◻ Mux
...
│  ◻ Zencoder
│  ◻ Neovate

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Scope:&lt;/strong&gt; I would choose Global here rather than having to install it in every project under a &lt;code&gt;.kiro&lt;/code&gt; directory. I used to do that for rules before.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybdmc3zbimyrqhkt2hqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybdmc3zbimyrqhkt2hqb.png" alt="Installation scope and method selection interface" width="620" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation method:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;◆  Installation method
│  ● Symlink (Recommended) (Single source of truth, easy updates)
│  ○ Copy to all agents
└

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

&lt;/div&gt;



&lt;p&gt;So what did we select until now?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;◇  Installation Summary ───────────────────╮
│                                          │
│  ~/.agents/skills/terraform-style-guide  │
│    symlink → Kiro CLI                    │
│    overwrites: Kiro CLI                  │
│                                          │
│  ~/.agents/skills/terraform-test         │
│    symlink → Kiro CLI                    │
│    overwrites: Kiro CLI                  │
│                                          │
│  ~/.agents/skills/refactor-module        │
│    symlink → Kiro CLI                    │
│    overwrites: Kiro CLI                  │
│                                          │
│  ~/.agents/skills/terraform-stacks       │
│    symlink → Kiro CLI                    │
│    overwrites: Kiro CLI                  │
│                                          │
├──────────────────────────────────────────╯
│
◆  Proceed with installation?
│  ● Yes / ○ No

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

&lt;/div&gt;



&lt;p&gt;Proceed with installation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;│
◇  Installed 4 skills to 1 agent ────────────╮
│                                            │
│  ✓ ~/.agents/skills/terraform-style-guide  │
│    symlink → Kiro CLI                      │
│  ✓ ~/.agents/skills/terraform-test         │
│    symlink → Kiro CLI                      │
│  ✓ ~/.agents/skills/refactor-module        │
│    symlink → Kiro CLI                      │
│  ✓ ~/.agents/skills/terraform-stacks       │
│    symlink → Kiro CLI                      │
│                                            │
├────────────────────────────────────────────╯

│
└  Done!

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

&lt;/div&gt;



&lt;p&gt;Are we done? With the skills installation. But not completely. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Kiro skills resource&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Reference: &lt;a href="https://kiro.dev/docs/cli/custom-agents/configuration-reference/#skill-resources" rel="noopener noreferrer"&gt;https://kiro.dev/docs/cli/custom-agents/configuration-reference/#skill-resources&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;kiro-cli (as of now) expects the skill to be referenced in a similar way as your rules or other markdowns. With a twist, the &lt;code&gt;skill://&lt;/code&gt; prefix makes kiro understand you are looking to load these dynamically when needed so that all of the text in all of the markdowns in there are not loaded always. If you are installing any skills for the first time, the npx install prompts you to install a &lt;code&gt;find_skills&lt;/code&gt; skill too :)&lt;/p&gt;

&lt;p&gt;In my Kiro CLI setup, I've configured a custom agent that combines whatever tools I use for Terraform development. kiro-cli lets you generate an agent allowing you to create the json file required for agent config in your &lt;code&gt;.kiro/agents&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Here's how it's configured on my end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"iac"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a specialist in infrastructure as code, especially with AWS."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a specialist in infrastructure as code (IaC), with deep expertise in AWS services and best practices. You help users design, implement, and manage cloud infrastructure using code-based approaches like Terraform, CloudFormation, CDK, and other IaC tools. You provide guidance on AWS architecture patterns, security best practices, cost optimization, and automation strategies. You can assist with writing, reviewing, and troubleshooting infrastructure code, as well as helping users understand AWS services and how to effectively use them in their infrastructure deployments."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"awslabs.aws-documentation-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"awslabs.aws-documentation-mcp-server@latest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FASTMCP_LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AWS_DOCUMENTATION_PARTITION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aws"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"terraform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"finch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--rm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hashicorp/terraform-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FASTMCP_LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"toolAliases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allowedTools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"skill://~/.kiro/skills/**/SKILL.md"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"toolsSettings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"useLegacyMcpJson"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;["skill://~/.kiro/skills/**/SKILL.md"],&lt;/code&gt; it is installed in my &lt;code&gt;.kiro&lt;/code&gt; directory under user. You could modify it based on your installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills in Action
&lt;/h2&gt;

&lt;p&gt;With the custom agent called &lt;code&gt;iac&lt;/code&gt;, I can either invoke the agent directly or switch to that agent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kiro-cli --agent iac
✓ terraform loaded in 0.54 s
✓ Framelink MCP for Figma loaded in 1.49 s
⚠ 2 of 3 mcp servers initialized. Servers still loading:
- awslabs.aws-documentation-mcp-server
⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀     ⢀⣴⣶⣶⣦⡀⠀⠀⠀⢀⣴⣶⣦⣄⡀⠀⠀⢀⣴⣶⣶⣦⡀⠀⠀⢀⣴⣶⣶⣶⣶⣶⣶⣶⣶⣶⣦⣄⡀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣶⣶⣶⣶⣶⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀    ⢰⣿⠋⠁⠈⠙⣿⡆⠀⢀⣾⡿⠁⠀⠈⢻⡆⢰⣿⠋⠁⠈⠙⣿⡆⢰⣿⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⣦⠀⠀⠀⠀⣴⡿⠟⠋⠁⠀⠀⠀⠈⠙⠻⢿⣦⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀    ⢸⣿⠀⠀⠀⠀⣿⣇⣴⡿⠋⠀⠀⠀⢀⣼⠇⢸⣿⠀⠀⠀⠀⣿⡇⢸⣿⠀⠀⠀⢠⣤⣤⣤⣤⣄⠀⠀⠀⠀⣿⡆⠀⠀⣼⡟⠀⠀⠀⠀⣀⣀⣀⠀⠀⠀⠀⢻⣧⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀    ⢸⣿⠀⠀⠀⠀⣿⡿⠋⠀⠀⠀⢀⣾⡿⠁⠀⢸⣿⠀⠀⠀⠀⣿⡇⢸⣿⠀⠀⠀⢸⣿⠉⠉⠉⣿⡇⠀⠀⠀⣿⡇⠀⣼⡟⠀⠀⠀⣰⡿⠟⠛⠻⢿⣆⠀⠀⠀⢻⣧⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀    ⢸⣿⠀⠀⠀⠀⠙⠁⠀⠀⢀⣼⡟⠁⠀⠀⠀⢸⣿⠀⠀⠀⠀⣿⡇⢸⣿⠀⠀⠀⢸⣿⣶⣶⡶⠋⠀⠀⠀⠀⣿⠇⢰⣿⠀⠀⠀⢰⣿⠀⠀⠀⠀⠀⣿⡆⠀⠀⠀⣿⡆
⠀⠀⠀⠀⠀⠀⠀    ⢸⣿⠀⠀⠀⠀⠀⠀⠀⠀⠹⣷⡀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⣿⡇⢸⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣼⠟⠀⢸⣿⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⣿⡇⠀⠀⠀⣿⡇
⠀⠀⠀⠀⠀⠀⠀    ⢸⣿⠀⠀⠀⠀⠀⣠⡀⠀⠀⠹⣷⡄⠀⠀⠀⢸⣿⠀⠀⠀⠀⣿⡇⢸⣿⠀⠀⠀⠀⣤⣄⠀⠀⠀⠀⠹⣿⡅⠀⠀⠸⣿⠀⠀⠀⠸⣿⠀⠀⠀⠀⠀⣿⠇⠀⠀⠀⣿⠇
⠀⠀⠀⠀⠀⠀⠀    ⢸⣿⠀⠀⠀⠀⣾⡟⣷⡀⠀⠀⠘⣿⣆⠀⠀⢸⣿⠀⠀⠀⠀⣿⡇⢸⣿⠀⠀⠀⠀⣿⡟⣷⡀⠀⠀⠀⠘⣿⣆⠀⠀⢻⣧⠀⠀⠀⠹⣷⣦⣤⣤⣾⠏⠀⠀⠀⣼⡟
⠀⠀⠀⠀⠀⠀⠀    ⢸⣿⠀⠀⠀⠀⣿⡇⠹⣷⡀⠀⠀⠈⢻⡇⠀⢸⣿⠀⠀⠀⠀⣿⡇⢸⣿⠀⠀⠀⠀⣿⡇⠹⣷⡀⠀⠀⠀⠈⢻⡇⠀⠀⢻⣧⠀⠀⠀⠀⠉⠉⠉⠀⠀⠀⠀⣼⡟
⠀⠀⠀⠀⠀⠀⠀    ⠸⣿⣄⡀⢀⣠⣿⠇⠀⠙⣷⡀⠀⢀⣼⠇⠀⠸⣿⣄⡀⢀⣠⣿⠇⠸⣿⣄⡀⢀⣠⣿⠇⠀⠙⣷⡀⠀⠀⢀⣼⠇⠀⠀⠀⠻⣷⣦⣄⡀⠀⠀⠀⢀⣠⣴⣾⠟
⠀⠀⠀⠀⠀⠀⠀    ⠀⠈⠻⠿⠿⠟⠁⠀⠀⠀⠈⠻⠿⠿⠟⠁⠀⠀⠈⠻⠿⠿⠟⠁⠀⠀⠈⠻⠿⠿⠟⠁⠀⠀⠀⠈⠻⠿⠿⠟⠁⠀⠀⠀⠀⠀⠈⠙⠻⠿⠿⠿⠿⠟⠋⠁

╭─────────────────────────────── Did you know? ────────────────────────────────╮
│                                                                              │
│     Enable workspace checkpoints to snapshot &amp;amp; restore changes. Just run     │
│                 kiro-cli settings chat.enableCheckpoint true                 │
│                                                                              │
╰──────────────────────────────────────────────────────────────────────────────╯

Model: claude-sonnet-4 (/model to change)

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

&lt;/div&gt;



&lt;p&gt;I haven't yet found the command which told me what skills are available on load time. But introspect works in those cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[iac] &amp;gt; introspect are there any skills available for me to use ?

Introspecting to get you the right information (using tool: introspect) - Completed in 0.1s

&amp;gt; Based on the context entries you provided, yes, you have several skills available:

Available Skills:

1. find-skills - Helps you discover and install additional agent skills when you ask questions like "how do I do X" or "find a skill for X"
2. refactor-module - Transforms monolithic Terraform configurations into reusable, maintainable modules following HashiCorp's best practices
3. terraform-stacks - Comprehensive guide for working with HashiCorp Terraform Stacks, including creating and managing .tfcomponent.hcl and .tfdeploy.hcl files
4. terraform-style-guide - Generates Terraform HCL code following HashiCorp's official style conventions and best practices
5. terraform-test - Comprehensive guide for writing and running Terraform tests, including creating .tftest.hcl files and test scenarios

These skills are stored in your ~/.kiro/skills/ directory and are designed to help with infrastructure as code tasks, particularly around Terraform development and best practices.

If you're looking for additional skills beyond these, you can use the find-skills skill to discover and install more capabilities based on your specific needs.
 ▸ Time: 12s

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;What do you do? You ask your question which you know will trigger the skill which can answer this. I haven't figured out how the default &lt;code&gt;kiro_default&lt;/code&gt; agent can get these without creating another custom agent. Maybe that is not possible since we need the agent configuration to include skill resources right now.&lt;/p&gt;

&lt;p&gt;Let's do this without calling our agent first time. For the eagle eyed and folks familiar with TF stacks, the tfstacks.hcl is being referenced which is still the old norm of creating stacks (Yes, I am on the TF MCP server 0.3.x version now)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How do I create a Terraform stacks config for a VPC config from the private registry manu.example.com ?

&amp;gt; I'll help you create a Terraform stacks configuration for a VPC from your private registry. Let me first get the latest Terraform provider version and then show you the minimal configuration.
Running tool get_latest_provider_version with the param (from mcp server: terraform)
 ⋮  {
 ⋮    "namespace": "hashicorp",
 ⋮    "name": "aws"
 ⋮  }

Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]:

&amp;gt; y

 - Completed in 3.778s

&amp;gt; Here's the minimal Terraform stacks configuration for a VPC from your private registry:

deployments.tfdeploy.hcl
hcl
deployment "vpc" {
  inputs = {
    regions = ["us-east-1"]
  }
}

vpc.tfstack.hcl
hcl
required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~&amp;gt; 6.28.0"
  }
}

variable "regions" {
  type = set(string)
}

component "vpc" {
  source = "manu.example.com/your-namespace/vpc-module"

  for_each = var.regions


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

&lt;/div&gt;



&lt;p&gt;Invoke the agent &lt;code&gt;kiro-cli --agent iac&lt;/code&gt; or &lt;code&gt;/agent swap iac&lt;/code&gt; from within the CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How do I create a Terraform stacks config for a VPC config from the private registry manu.example.com ?

&amp;gt; I'll help you create a Terraform Stacks configuration for a VPC from your private registry. Let me first check the terraform-stacks skill for the specific syntax.
Reading file: /Users/abc123/.kiro/skills/terraform-stacks/SKILL.md, all lines (using tool: read)
✓ Successfully read 14349 bytes from /Users/abc123/.kiro/skills/terraform-stacks/SKILL.md
- Completed in 0.0s

&amp;gt; Based on the Terraform Stacks documentation, here's how to create a Stack configuration for a VPC from your private registry manu.example.com:

Component Configuration (components.tfcomponent.hcl):

hcl
component "vpc" {
 source  = "manu.example.com/vpc/aws"
 version = "1.0.0"

 inputs = {
   cidr_block = var.vpc_cidr
   region     = var.aws_region
 }

 providers = {
   aws = provider.aws.this
 }
}


Provider Configuration (providers.tfcomponent.hcl):

hcl
required_providers {
 aws = {
   source  = "hashicorp/aws"
   version = "~&amp;gt; 5.7.0"
 }
}

provider "aws" "this" {
 config {
   region = var.aws_region
   assume_role_with_web_identity {
     role_arn           = var.role_arn
     web_identity_token = var.identity_token
   }
 }
}


Variables (variables.tfcomponent.hcl):

hcl
variable "vpc_cidr" {
 type = string
}

...


Deployment (deployments.tfdeploy.hcl):

hcl
identity_token "aws" {
 audience = ["aws.workload.identity"]
}

deployment "production" {
 inputs = {
   vpc_cidr       = "10.0.0.0/16"
   aws_region     = "us-west-1"
   role_arn       = "arn:aws:iam::123456789012:role/hcp-terraform-stacks"
   identity_token = identity_token.aws.jwt
 }
}


Key points for private registry sources:
- Use the full registry hostname in the source: manu.example.com/vpc/aws
- Include a version constraint
- Ensure you're authenticated to the private registry before running terraform stacks providers-lock
▸ Time: 19s

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bloopers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without the agent invoked.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Do I have any skills available ?

&amp;gt; Based on my review, you do not have any specific skills in the documents.

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Looking Forward
&lt;/h2&gt;

&lt;p&gt;With only Terraform in the current skill list, take a look at the branches and you will see some of the groundwork done before and how it evolved into what is available as version &lt;code&gt;0.0.1&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I do have an open question to the skills developers or contributors. &lt;code&gt;How do we make sure that the responses we get for a set of questions after making changes to the existing skills are not worsened after those changes ? How can we automatically validate these are goind in the wrong direction&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The future of infrastructure development is looking increasingly collaborative - with AI agents that understand not just syntax, but the deeper patterns and practices that make infrastructure reliable, secure, and maintainable. Yes, I had the kiro write the above line ;)&lt;/p&gt;

&lt;p&gt;Other notable skills for your Terraform/OpenTofu exploits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/antonbabenko/terraform-skill" rel="noopener noreferrer"&gt;Anton's Terraform skills&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>skills</category>
      <category>kiro</category>
      <category>aws</category>
    </item>
    <item>
      <title>Stategraph: Deploy on AWS with Terraform Part 1</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Tue, 03 Feb 2026 03:41:40 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/stategraph-deploy-on-aws-with-terraform-part-1-g01</link>
      <guid>https://forem.com/quixoticmonk/stategraph-deploy-on-aws-with-terraform-part-1-g01</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Crossposted from &lt;a href="https://manuchandrasekhar.com/posts/2026/02-01-stategraph-aws-deployment/" rel="noopener noreferrer"&gt;https://manuchandrasekhar.com/posts/2026/02-01-stategraph-aws-deployment/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're following the Infrastructure as Code space, Josh Pollara is a name you've likely encountered. While I haven't personally tried &lt;a href="https://terrateam.io/" rel="noopener noreferrer"&gt;terrateam&lt;/a&gt;, I've been following what he and his co-founders have been building. One of the most common questions I get from customers using Terraform is around state management. Most AWS deployments default to S3 as a backend (with DynamoDB for locking in pre-1.10 Terraform versions). The &lt;a href="https://stategraph.com/" rel="noopener noreferrer"&gt;idea of exploring another mechanism in the state management space intrigued me&lt;/a&gt;, especially with promises around faster plans through their upcoming &lt;code&gt;Velocity&lt;/code&gt; feature. The &lt;code&gt;First Principles&lt;/code&gt; &lt;a href="https://stategraph.com/first-principles" rel="noopener noreferrer"&gt;page&lt;/a&gt; is worth reading, and this stood out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Infrastructure is a graph. Store it as a graph.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I started with their backend implementation using the Docker Compose quickstart. I'll save the nuances of the transactional nature of state changes and metadata tracking for a future post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dev setup with Docker(Finch) compose
&lt;/h2&gt;

&lt;p&gt;The Docker Compose setup was straightforward for local development. Initially, I struggled with configuring this as a Terraform state backend. Stategraph implements the &lt;a href="https://stategraph.com/docs/backend" rel="noopener noreferrer"&gt;HTTP backend protocol&lt;/a&gt;, requiring an endpoint and authentication mechanism. My confusion was primarily because I skipped the &lt;code&gt;Creating an API key&lt;/code&gt; section in the &lt;a href="https://stategraph.com/docs/quickstart/state-backend#step-1-create-an-api-key" rel="noopener noreferrer"&gt;quickstart guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The documentation could benefit from emphasizing the API key requirement earlier in the flow for users who might skip ahead (like me).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  backend "http" {
    address = "http://myrest.api.com/foo"
    username = "session"
    password = "&amp;lt;your-api-key&amp;gt;"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that the user settings does give you an option to create a service account for CICD pipelines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo3bs40aoc2tg3p9cij3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo3bs40aoc2tg3p9cij3.png" alt="Stategraph API Key" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For security, avoid hardcoding credentials in your backend configuration. Use environment variables instead. The environment variables &lt;code&gt;TF_HTTP_USERNAME&lt;/code&gt; and &lt;code&gt;TF_HTTP_PASSWORD&lt;/code&gt; are your best bet here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  backend "http" {
    address = "http://localhost:8080/api/v1/states/backend/&amp;lt;group-id&amp;gt;"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the &lt;code&gt;group-id&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;group-id&lt;/code&gt; is a unique identifier that Stategraph assigns to organize and locate your Terraform state files. Think of it as a folder ID or address where your infrastructure state is stored. In HCP Terraform terms, it's similar to a workspace or project identifier. This is the first time I noticed the stategraph CLI.&lt;/p&gt;

&lt;p&gt;For MacOs users, the installation is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap stategraph/stategraph
brew &lt;span class="nb"&gt;install &lt;/span&gt;stategraph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate the group-id using these commands (detailed in &lt;a href="https://stategraph.com/docs/quickstart/state-backend#step-2-create-a-state" rel="noopener noreferrer"&gt;Create a state&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get your tenant ID&lt;/span&gt;

stategraph user tenants list

&lt;span class="c"&gt;# Create a new state (returns group_id)&lt;/span&gt;
stategraph states create &lt;span class="nt"&gt;--tenant&lt;/span&gt; &amp;lt;tenant_id&amp;gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"stategraph-demo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stategraph-demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"group_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"550e8434-t59b-56d4-a716-329055440073"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initial Web Interface Impressions
&lt;/h3&gt;

&lt;p&gt;The Stategraph web interface impressed me with its clean design. Even with just two state files in my initial setup, the interface clearly prioritized giving visibility into individual resources within state files and across multiple states. The screenshots below show the different views available:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State Overview:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbd9mruinb64ezphf92ub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbd9mruinb64ezphf92ub.png" alt="Stategraph Initial Setup" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control Plan Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnn5771v91u3s3zccisi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvnn5771v91u3s3zccisi.png" alt="Stategraph Initial Setup" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State Metadata:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0x61imlr32ffwk1q8jo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0x61imlr32ffwk1q8jo.png" alt="Stategraph Initial Setup" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment configuration using Terraform
&lt;/h2&gt;

&lt;p&gt;The local Docker Compose setup was just the beginning. I wanted to deploy Stategraph into a production-ready environment using ECS with an external PostgreSQL database on RDS. After several iterations and troubleshooting various configuration issues, I have a working deployment using &lt;a href="https://github.com/stategraph/releases/releases/tag/0.1.16" rel="noopener noreferrer"&gt;Stategraph server version 0.1.16&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The complete Terraform configuration I used is available at: &lt;a href="https://github.com/quixoticmonk/stategraph-infra" rel="noopener noreferrer"&gt;Stategraph ECS Infrastructure&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The infrastructure configuration creates a Stategraph environment with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ECS EC2&lt;/strong&gt;: Runs the Stategraph container (&lt;code&gt;ghcr.io/stategraph/stategraph-server&lt;/code&gt;) &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFront&lt;/strong&gt;: CDN for HTTPS termination and global distribution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Load Balancer&lt;/strong&gt;: Routes traffic to ECS tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS PostgreSQL 17&lt;/strong&gt;: Database for state storage and transaction history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC&lt;/strong&gt;: Isolated network with public/private subnets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets Manager&lt;/strong&gt;: Secure storage for database password and OAuth secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before deploying, you'll need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS credentials&lt;/strong&gt; configured (via environment variables, IAM roles, or AWS profiles)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt; &amp;gt;= 1.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google OAuth Application&lt;/strong&gt; - Set up at &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Create OAuth 2.0 Client ID for web application&lt;/li&gt;
&lt;li&gt;Configure authorized redirect URIs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Required Variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"google_oauth_client_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Google OAuth Client ID"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"google_oauth_client_secret"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Google OAuth Client Secret"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Container Configuration
&lt;/h3&gt;

&lt;p&gt;Stategraph runs on port 8180 internally with nginx as a reverse proxy on port 8080. Key environment variables include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"STATEGRAPH_UI_BASE"&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stategraph_url&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"STATEGRAPH_PORT"&lt;/span&gt; 
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"8180"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"STATEGRAPH_OAUTH_TYPE"&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"oidc"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"STATEGRAPH_OAUTH_OIDC_ISSUER_URL"&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://accounts.google.com"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;secrets&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DB_PASS"&lt;/span&gt;
    &lt;span class="nx"&gt;valueFrom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_secretsmanager_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db_password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"STATEGRAPH_OAUTH_CLIENT_SECRET"&lt;/span&gt;
    &lt;span class="nx"&gt;valueFrom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_secretsmanager_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_client_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I used &lt;code&gt;oidc&lt;/code&gt; as the OAuth type instead of &lt;code&gt;google&lt;/code&gt; due to configuration issues with the latter. I have been lazy here not to go back and check what I missed. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Initialize and Deploy:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Follow the standard Terraform workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   terraform init
   terraform plan
   terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Configure OAuth:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After deployment, configure your Google OAuth application with the redirect URI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   terraform output google_oauth_redirect_uri
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The callback URL will be in the format: &lt;code&gt;https://abc.cloudfront.net/oauth2/oidc/callback&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Access Stategraph:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Retrieve the Stategraph URL from Terraform outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Get the HTTPS URL (via CloudFront)&lt;/span&gt;
   terraform output stategraph_url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stategraph UI
&lt;/h3&gt;

&lt;p&gt;Once deployed, the initial setup flow includes several steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;License Prompt&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuitq38z1f18s8xx375f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuitq38z1f18s8xx375f.png" alt="Stategraph Get License" width="800" height="769"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enter Existing License&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwj8noils7bdxd3h5y30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwj8noils7bdxd3h5y30.png" alt="Stategraph Existing License" width="800" height="871"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Information&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi93z2sepoqh7yhvj5vy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi93z2sepoqh7yhvj5vy0.png" alt="Stategraph User Info" width="800" height="831"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Authentication&lt;/strong&gt; (Note: I misconfigured &lt;code&gt;STATEGRAPH_OAUTH_DISPLAY_NAME&lt;/code&gt; - it should be &lt;code&gt;Google&lt;/code&gt; rather than the full message shown)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mmdnetsc9rglor45eed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mmdnetsc9rglor45eed.png" alt="Stategraph Google Auth" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Main Web Interface&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwgnvvna1p1q2s88k38e2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwgnvvna1p1q2s88k38e2.png" alt="Stategraph State UI" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  State Migration
&lt;/h2&gt;

&lt;p&gt;Obviously the next thing to do was migrate the state to Stategraph itself. For the Docker compose local development setup, I was copying and pasting without reading half of it. Now came the actual setup where I noticed the additional variables I need to keep in mind. You can generate an API KEY from the user settings and set these environment vars as you would need it for stategraph CLI command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export STATEGRAPH_API_BASE="&amp;lt;stategragh_url&amp;gt;"
export STATEGRAPH_API_KEY="&amp;lt;api_key&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once done, retrieve the tenant id&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stategraph user tenants list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And create the state container. This is where I think they should have a glossary of terms in reference to stategraph. We as a community have made some words difficult to use in many other contexts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stategraph states create --tenant &amp;lt;tenant_id&amp;gt; --name "stategraph-ecs"
{
 "created_at": "2026-02-02T05:34:21Z",
 "group_id": "&amp;lt;group_id&amp;gt;",
 "id": "...",
 "name": "stategraph-ecs",
 "workspace": "default"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the Terraform block to use the same backend. I did backup the state before doing it though the configuration didn't have a lot of components and I could have manually cleaned them up if I messed it up.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;terraform init&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initializing the backend...
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "local" backend to the
  newly configured "http" backend. No existing state was found in the newly
  configured "http" backend. Do you want to copy this state to the new "http"
  backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value:

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

&lt;/div&gt;



&lt;p&gt;Enter &lt;code&gt;yes&lt;/code&gt;. Here goes nothing..&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully configured the backend "http"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Reusing previous version of hashicorp/random from the dependency lock file
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v6.28.0
- Using previously-installed hashicorp/random v3.8.1

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvjddrzau5unekg8ydgm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvjddrzau5unekg8ydgm.png" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run a plan to check everything is ok.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;random_password.db_password[0]: Refreshing state... [id=none]
random_id.secret_suffix: Refreshing state... [id=vKBxqA]
data.aws_availability_zones.available: Reading...
aws_secretsmanager_secret.db_password: Refreshing state... [id=arn:aws:secretsmanager:us-east-1:############:secret:stategraph-db-password-bca071a8-MgkbzS]
aws_ecs_cluster.main: Refreshing state... [id=arn:aws:ecs:us-east-1:############:cluster/stategraph]
data.aws_ami.ecs_optimized: Reading...
....
aws_cloudfront_distribution.stategraph: Refreshing state... [id=E2CDRVGI3JY6BT]
aws_db_instance.postgres: Refreshing state... [id=db-QRFNQQCUDBMW4HXCODVUHWKTXE]
aws_ecs_task_definition.stategraph: Refreshing state... [id=stategraph]
aws_ecs_service.stategraph: Refreshing state... [id=arn:aws:ecs:us-east-1:############:service/stategraph/stategraph]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences,
so no changes are needed.

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

&lt;/div&gt;



&lt;p&gt;The Resource count on the pane looks off by 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhi0ux0jbzi5chehckhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhi0ux0jbzi5chehckhp.png" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you dig into the &lt;code&gt;inspect state&lt;/code&gt; , you have the 48 resources listed. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqb2jaezdilae3389t5gj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqb2jaezdilae3389t5gj.png" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  State Overview
&lt;/h2&gt;

&lt;p&gt;I still need to dive deep into what some of these data points mean, but they are there for you if you want them. The view is scoped to the state you were looking at; in my case the stategraph deployment itself. The page does give you quick jump off points to Search and Inventory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Few8pp3kejjkfv0eeh7bf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Few8pp3kejjkfv0eeh7bf.png" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pp7sycp929say9wwtb4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pp7sycp929say9wwtb4.png" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dependency blast radius and Top resource types !!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flv25fhlus149bwg8lsna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flv25fhlus149bwg8lsna.png" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Now what are Orphaned candidates ? :thinkingface:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Inventory
&lt;/h2&gt;

&lt;p&gt;If you select one of the resource types from state overview, it takes  you to the inventory of currently managed resources. This is sweet !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxucvmoclk031an2l2etg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxucvmoclk031an2l2etg.png" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Querying Resources
&lt;/h3&gt;

&lt;p&gt;The interface already gives you some sample queries and it worked out well with my stategraph infrastructure here. I am not going to draw parallels just yet, the query interface along with the examples were a nice a touch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue0xakembs6yrgbyey7x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue0xakembs6yrgbyey7x.png" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For your recurring queries :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6svfezk2r5v3ps225ic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6svfezk2r5v3ps225ic.png" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I did feel like a kid during Christmas with a candy box all for himself ;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffm6wm9bp8tpnqazs3sca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffm6wm9bp8tpnqazs3sca.png" width="347" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and Troubleshooting
&lt;/h3&gt;

&lt;p&gt;Monitor your deployment through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Logs&lt;/strong&gt;: &lt;code&gt;/ecs/stategraph&lt;/code&gt; log group&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS Service Events&lt;/strong&gt;: Check for deployment issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target Group Health&lt;/strong&gt;: Monitor ALB target group status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common issues to watch for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database connectivity (security groups, RDS availability)&lt;/li&gt;
&lt;li&gt;OAuth configuration (redirect URIs, client secrets)&lt;/li&gt;
&lt;li&gt;Load balancer health checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment Challenges
&lt;/h3&gt;

&lt;p&gt;I encountered couple of challenges during my ECS Terraform route:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version Compatibility&lt;/strong&gt;: Stategraph iterates releases frequently (almost daily). I struggled to get ALB health checks passing on versions before 0.1.15 due to ECS task failures during database migration tasks, even though database connectivity was confirmed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compute Choice&lt;/strong&gt;: I initially started with Fargate but switched to EC2 instances for some troubleshooting.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Stategraph brings a fresh perspective to infrastructure state management that complement the existing ecosystem well. The visual interface and query capabilities will offer valuable insights that practitioners will appreciate, especially in organizations with a huge number of workloads targeting multiple platforms.&lt;/p&gt;

&lt;p&gt;If you are looking to get started , the &lt;a href="https://github.com/quixoticmonk/stategraph-infra" rel="noopener noreferrer"&gt;Terraform configuration here&lt;/a&gt; should give you a fully stable infrastructure once you have a Google OAuth client ID. Or swap out the authentication mechanism based on your preference.  &lt;/p&gt;

&lt;p&gt;Here are the key resources I referenced during configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stategraph.com/docs" rel="noopener noreferrer"&gt;Stategraph Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stategraph.com/docs/authentication/google-oauth" rel="noopener noreferrer"&gt;Stategraph OAuth Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stategraph.com/docs/reference/environment-variables" rel="noopener noreferrer"&gt;Stategraph Environment Variables&lt;/a&gt; - Comprehensive list of configuration options (I appreciate having all environment variables documented in one place!)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt; - for OAuth setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are so many components to this along with the orchestration side using &lt;code&gt;Terrateam&lt;/code&gt; which I am excited to try. More of it in the next set of posts.&lt;/p&gt;

</description>
      <category>stategraph</category>
      <category>terraform</category>
      <category>state</category>
      <category>iac</category>
    </item>
    <item>
      <title>Terraform Module Guard: Multi-Engine Policy Validation for Module Sources</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Thu, 13 Nov 2025 04:11:47 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/terraform-module-guard-multi-engine-policy-validation-for-module-sources-3mb1</link>
      <guid>https://forem.com/quixoticmonk/terraform-module-guard-multi-engine-policy-validation-for-module-sources-3mb1</guid>
      <description>&lt;p&gt;It's November already !!! Are you in the naughty list or nice list ?&lt;/p&gt;

&lt;p&gt;One of my wishlist items for HCP Terraform or Terraform enterprise has always been the ability to restrict what modules or providers can be used based on some list. Yes, if you have used GitHub actions the desire to have something similar comes from that interface of allowed actions. While that's a wishlist item, I have had clients who had asked for mechanisms to do this using their existing policy as code frameworks. And when I say Policy as Code frameworks or tools, I mean some of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;checkov&lt;/li&gt;
&lt;li&gt;opa&lt;/li&gt;
&lt;li&gt;sentinel&lt;/li&gt;
&lt;li&gt;Or a simple bash script&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're using HCP Terraform with its enterprise features or the community edition with open-source tooling, you need consistent ways to validate module origins. This challenge led me to create &lt;a href="https://github.com/quixoticmonk/terraform-module-guard" rel="noopener noreferrer"&gt;terraform-module-guard&lt;/a&gt;, a collection of policies that validate Terraform module sources across multiple policy engines. The reason it exists is for purely selfish reasons as I often find myself trying to find that last time I used it which is in my filesystem somewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Let's dive into the problem statement further.Terraform modules can be sourced from various locations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public registries (registry.terraform.io)&lt;/li&gt;
&lt;li&gt;Private registries&lt;/li&gt;
&lt;li&gt;Git repositories&lt;/li&gt;
&lt;li&gt;Local file paths or even HTTP URLs if I am not wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without proper controls, teams might inadvertently use modules from untrusted sources, potentially introducing security vulnerabilities or compliance violations. Different organizations use different policy engines based on their Terraform deployment model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HCP Terraform users&lt;/strong&gt; typically leverage Sentinel or OPA for policy enforcement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community edition users&lt;/strong&gt; often rely on open-source tools like OPA, Checkov, or pre-commit hooks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My intention
&lt;/h2&gt;

&lt;p&gt;My intention here is purely as a frame of reference of what can be done to solve this problem across some common PaC frameworks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub repo : &lt;a href="https://github.com/quixoticmonk/terraform-module-guard" rel="noopener noreferrer"&gt;terraform-module-guard&lt;/a&gt; provides identical policy logic implemented across different engines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start with the configuration elements. In the repo ,&lt;code&gt;allowed-sources.yaml&lt;/code&gt; represents an example of what is allowed when considering the sources of the modules. I am using the inline versions of these for OPA and Sentinel. There might be ways to load the file into the evaluations. I might look into that later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;allowed_sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloudposse/*"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;terraform-aws-modules/*"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws-ia/*"&lt;/span&gt;
  &lt;span class="na"&gt;git&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github.com/cloudposse/*"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github.com/terraform-aws-modules/*"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github.com/aws-ia/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is merely an example of the keys and values I wanted to consider. You may have variations of this which you can absolutely add and update the policies with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Set
&lt;/h3&gt;

&lt;p&gt;The repository includes test files that demonstrate the policy validation across all engines. These examples were tested against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Checkov&lt;/strong&gt;: v3.2.458&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OPA&lt;/strong&gt;: v1.9.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ Good Module Sources
&lt;/h3&gt;

&lt;p&gt;Let's define some passing examples. &lt;code&gt;examples/pass/pass.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/vpc/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"label"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws-ia/label/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.0.5"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"cloudposse_module"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloudposse/label/null"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.25.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"git_terraform_aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"git::ssh://git@github.com/terraform-aws-modules/terraform-aws-s3-bucket.git?ref=v4.0.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"git_cloudposse"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"git::ssh://git@github.com/cloudposse/terraform-null-label.git?ref=0.25.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Bad Module Sources
&lt;/h3&gt;

&lt;p&gt;Similarly some failing examples. Yes, I do have a passing source mixed in to validate that it does get flagged as passed. (Blame the QA in me) These should be present in the repo as &lt;code&gt;examples/fail/fail.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"quixoticmonk_glue_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"quixoticmonk/glue/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.0.3"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"quixoticmonk_glue_2"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"quixoticmonk/glue/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.0.3"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"docker_secret"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bendrucker/docker-secret/kubernetes"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-docker-secret"&lt;/span&gt;
  &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
  &lt;span class="nx"&gt;registries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"docker.io"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"user"&lt;/span&gt;
      &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"pass"&lt;/span&gt;
      &lt;span class="nx"&gt;email&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"git_quixoticmonk_s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"git::ssh://git@github.com/quixoticmonk/terraform-aws-s3.git?ref=main"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# This should PASS - allowed source&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"allowed_module"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/vpc/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Sentinel (HCP Terraform)
&lt;/h3&gt;

&lt;p&gt;For teams using HCP Terraform, you can use the Sentinel policy referenced &lt;a href="https://github.com/quixoticmonk/terraform-module-guard/blob/main/sentinel/module-source-policy.sentinel" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Keep in mind that this uses a &lt;code&gt;allowed_$$_sources&lt;/code&gt; input inside the policy itself. There may be a better way to define the list of sources and my sentinel kungfu is not that strong , yet. I should probably ask the sentinel team if there is a way to manage the sources.&lt;/p&gt;

&lt;p&gt;You can either add this policy as part of a policyset via VCS connection or create a policy inline in the HCP Terraform UI and enforce it on a workspace.&lt;/p&gt;

&lt;p&gt;For inline policies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Workspaces&lt;/code&gt; -&amp;gt; &lt;code&gt;Policies&lt;/code&gt; -&amp;gt; &lt;code&gt;Create a new policy&lt;/code&gt; -&amp;gt; Select &lt;code&gt;Sentinel&lt;/code&gt; and add the policy in the &lt;code&gt;policy code (Sentinel)&lt;/code&gt; section&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9f5iquil9xv4y6hiqrp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9f5iquil9xv4y6hiqrp0.png" alt=" " width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a Terraform workspace which has some of these modules being called in the configuration. Post plan, you should be able to see the details on Sentinel which should look something like below :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1877m28ywepr33q9c605.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1877m28ywepr33q9c605.png" alt=" " width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This policy validates module sources during plan and apply operations, blocking runs that use unauthorized modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Checkov
&lt;/h3&gt;

&lt;p&gt;If you have used Terraform and built a CICD pipeline for provisioning infrastructure using Terraform, you would have heard about Checkov which has become a standard for static analysis of the Terraform configuration. I do think it can get noisy without the severity classification. But I personally love the custom checks option which I keep using across the board for many situations where I need some new policies specific to my customer or something unique for my situation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run specific module source validation&lt;/span&gt;
checkov &lt;span class="nt"&gt;-f&lt;/span&gt; main.tf &lt;span class="nt"&gt;--external-checks-dir&lt;/span&gt; checkov/ &lt;span class="nt"&gt;--check&lt;/span&gt; CKV_TF_MODULE_SOURCE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;examples/pass/pass.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Passed checks: 5, Failed checks: 0, Skipped checks: 0

Check: CKV_TF_MODULE_SOURCE: "Ensure module source is from allowed list"
    PASSED for resource: vpc
    PASSED for resource: label
    PASSED for resource: cloudposse_module
    PASSED for resource: git_terraform_aws
    PASSED for resource: git_cloudposse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running against &lt;code&gt;examples/fail/fail.tf&lt;/code&gt;. Yes, I did sneak in a passing source to verify if this actually does what I am expecting it to do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Passed checks: 1, Failed checks: 4, Skipped checks: 0

Check: CKV_TF_MODULE_SOURCE: "Ensure module source is from allowed list"
    PASSED for resource: allowed_module

    FAILED for resource: quixoticmonk_glue_1
    File: /fail.tf:4-7
        4 | module "quixoticmonk_glue_1" {
        5 |   source = "quixoticmonk/glue/aws"
        6 |   version = "~&amp;gt; 0.0.3"
        7 | }

    FAILED for resource: quixoticmonk_glue_2
    File: /fail.tf:9-12
        9  | module "quixoticmonk_glue_2" {
        10 |   source = "quixoticmonk/glue/aws"
        11 |   version = "~&amp;gt; 0.0.3"
        12 | }

    FAILED for resource: docker_secret
    File: /fail.tf:14-27
        14 | module "docker_secret" {
        15 |   source  = "bendrucker/docker-secret/kubernetes"
        16 |   version = "1.0.0"

    FAILED for resource: git_quixoticmonk_s3
    File: /fail.tf:30-32
        30 | module "git_quixoticmonk_s3" {
        31 |   source = "git::ssh://git@github.com/quixoticmonk/terraform-aws-s3.git?ref=main"
        32 | }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Open Policy Agent (OPA)
&lt;/h3&gt;

&lt;p&gt;If you prefer OPA , you could use the example policy from the &lt;code&gt;opa&lt;/code&gt; directory against a Terraform plan json. The steps would look like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate Terraform plan in JSON format&lt;/span&gt;
terraform init
terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tfplan.binary
terraform show &lt;span class="nt"&gt;-json&lt;/span&gt; tfplan.binary &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tfplan.json

&lt;span class="c"&gt;# Execute policy against Terraform plan JSON&lt;/span&gt;
opa &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--decision&lt;/span&gt; terraform/module_sources/deny &lt;span class="nt"&gt;--bundle&lt;/span&gt; opa/ tfplan.json

&lt;span class="c"&gt;# Fail on violations (recommended for CI/CD)&lt;/span&gt;
opa &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--decision&lt;/span&gt; terraform/module_sources/deny &lt;span class="nt"&gt;--bundle&lt;/span&gt; opa/ &lt;span class="nt"&gt;--fail-non-empty&lt;/span&gt; tfplan.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And what do we see...&lt;/p&gt;

&lt;p&gt;For the pass scenarios present in &lt;code&gt;examples/pass/opa/input.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"examples/pass/opa/input.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;violations.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;code:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running against &lt;code&gt;examples/fail/opa/input.json&lt;/code&gt; with &lt;code&gt;--fail-non-empty&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"examples/fail/opa/input.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Module 'docker_secret' uses disallowed source: bendrucker/docker-secret/kubernetes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Module 'git_quixoticmonk_s3' uses disallowed source: git::ssh://git@github.com/quixoticmonk/terraform-aws-s3.git?ref=main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Module 'quixoticmonk_glue_1' uses disallowed source: quixoticmonk/glue/aws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Module 'quixoticmonk_glue_2' uses disallowed source: quixoticmonk/glue/aws"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;code:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exit code on &lt;code&gt;opa exec&lt;/code&gt; is really helpful when you are running this in a CICD pipeline if you are writing tests to validate your policies as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Pre-commit Hooks
&lt;/h3&gt;

&lt;p&gt;You could technically include any of the above in the pre-commit step as a hook. I decided to create a bash script which effectively does some string manipulation to verify if the source is allowed. I had Amazon Q whip up this quick script to verify if the module source is in the allowed list or not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;validate-module-sources&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate module sources&lt;/span&gt;
        &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./pre-commit/validate-module-sources.sh&lt;/span&gt;
        &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
        &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;\.tf$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test Results:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running against &lt;code&gt;examples/pass/pass.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ No violations found - all module sources are allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running against &lt;code&gt;examples/fail/fail.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: Disallowed module source in examples/fail/fail.tf: quixoticmonk/glue/aws
ERROR: Disallowed module source in examples/fail/fail.tf: quixoticmonk/glue/aws
ERROR: Disallowed module source in examples/fail/fail.tf: bendrucker/docker-secret/kubernetes
ERROR: Disallowed module source in examples/fail/fail.tf: git::ssh://git@github.com/quixoticmonk/terraform-aws-s3.git?ref=main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How can you get started ?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/quixoticmonk/terraform-module-guard/" rel="noopener noreferrer"&gt;repository&lt;/a&gt; includes examples to test and play around with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;examples/pass/&lt;/code&gt; - Valid module sources that should pass validation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;examples/fail/&lt;/code&gt; - Invalid sources that should trigger policy violations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're using HCP Terraform's enterprise features or building with community tools, you should have the ability to implement consistent module source validation across your entire Terraform ecosystem.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>policyascode</category>
      <category>checkov</category>
      <category>sentinel</category>
    </item>
    <item>
      <title>Terratags v0.6.0: Remote Configuration and Google Cloud Support</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Sat, 18 Oct 2025 14:35:09 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/terratags-v060-remote-configuration-and-google-cloud-support-3gd0</link>
      <guid>https://forem.com/quixoticmonk/terratags-v060-remote-configuration-and-google-cloud-support-3gd0</guid>
      <description>&lt;p&gt;Terratags continues to evolve, and v0.6.0 brings two significant features: &lt;strong&gt;remote configuration support&lt;/strong&gt; and &lt;strong&gt;Google Cloud Provider support&lt;/strong&gt;. These additions make Terratags extend its reach beyond AWS and Azure to cover the major cloud providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's New in v0.6.0
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Remote Configuration Support
&lt;/h3&gt;

&lt;p&gt;One of the requested features was the ability to centralize configuration management. This allows team to avoid duplicating tag requirements across multiple repositories and maintain consistency across their infrastructure.&lt;/p&gt;

&lt;p&gt;v0.6.0 introduces remote configuration support, allowing you to load your Terratags configuration from:&lt;/p&gt;

&lt;h4&gt;
  
  
  HTTP/HTTPS URLs
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terratags &lt;span class="nt"&gt;-config&lt;/span&gt; https://example.com/configs/terratags.yaml &lt;span class="nt"&gt;-dir&lt;/span&gt; ./infra
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Git Repositories
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# HTTPS with branch/tag specification&lt;/span&gt;
terratags &lt;span class="nt"&gt;-config&lt;/span&gt; https://github.com/org/configs.git//terratags.yaml?ref&lt;span class="o"&gt;=&lt;/span&gt;main &lt;span class="nt"&gt;-dir&lt;/span&gt; ./infra

&lt;span class="c"&gt;# SSH support for private repositories&lt;/span&gt;
terratags &lt;span class="nt"&gt;-config&lt;/span&gt; git@github.com:org/configs.git//path/to/config.yaml?ref&lt;span class="o"&gt;=&lt;/span&gt;v1.0.0 &lt;span class="nt"&gt;-dir&lt;/span&gt; ./infra
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is particularly powerful for organizations that want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version their tagging policies alongside their infrastructure code&lt;/li&gt;
&lt;li&gt;Use different configurations for different environments (dev, staging, prod)&lt;/li&gt;
&lt;li&gt;Maintain audit trails of configuration changes&lt;/li&gt;
&lt;li&gt;Leverage Git's branching and tagging for configuration management&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Version Pinning and Environment-Specific Configs
&lt;/h4&gt;

&lt;p&gt;The Git integration supports referencing specific branches, tags, or commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Production uses stable tagged version&lt;/span&gt;
terratags &lt;span class="nt"&gt;-config&lt;/span&gt; https://github.com/org/configs.git//prod-config.yaml?ref&lt;span class="o"&gt;=&lt;/span&gt;v2.1.0

&lt;span class="c"&gt;# Development uses latest from main branch&lt;/span&gt;
terratags &lt;span class="nt"&gt;-config&lt;/span&gt; https://github.com/org/configs.git//dev-config.yaml?ref&lt;span class="o"&gt;=&lt;/span&gt;main

&lt;span class="c"&gt;# Specific commit for reproducible builds&lt;/span&gt;
terratags &lt;span class="nt"&gt;-config&lt;/span&gt; https://github.com/org/configs.git//config.yaml?ref&lt;span class="o"&gt;=&lt;/span&gt;abc123def
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Google Cloud Provider Support
&lt;/h3&gt;

&lt;p&gt;The second major addition is full support for Google Cloud Platform. GCP uses 'labels' instead of 'tags' for resource metadata.&lt;/p&gt;

&lt;h4&gt;
  
  
  Basic GCP Label Validation
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_compute_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web_server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"web-server-01"&lt;/span&gt;
  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"e2-medium"&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-a"&lt;/span&gt;

  &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
    &lt;span class="nx"&gt;project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"web-app"&lt;/span&gt;
    &lt;span class="nx"&gt;owner&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"devops@example.com"&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"web-server-01"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Google Provider default_labels Support
&lt;/h4&gt;

&lt;p&gt;Just like AWS and Azapi provider's &lt;code&gt;default_tags&lt;/code&gt; , Google provider's &lt;code&gt;default_labels&lt;/code&gt; are fully supported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"google"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-project-id"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1"&lt;/span&gt;

  &lt;span class="nx"&gt;default_labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
    &lt;span class="nx"&gt;owner&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"team-platform"&lt;/span&gt;
    &lt;span class="nx"&gt;managed_by&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_storage_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"data_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"company-data-bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"US"&lt;/span&gt;

  &lt;span class="c1"&gt;# Only need to specify labels not covered by default_labels&lt;/span&gt;
  &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"data-bucket"&lt;/span&gt;
    &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"analytics"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the bucket will have all required labels: &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;project&lt;/code&gt; from the resource-level labels, and &lt;code&gt;environment&lt;/code&gt;, &lt;code&gt;owner&lt;/code&gt;, and &lt;code&gt;managed_by&lt;/code&gt; from the provider's default_labels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Cloud Tagging Consistency
&lt;/h2&gt;

&lt;p&gt;With v0.6.0, Terratags now supports all three major cloud providers with their respective tagging mechanisms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS&lt;/strong&gt;: &lt;code&gt;tags&lt;/code&gt; with &lt;code&gt;default_tags&lt;/code&gt; support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure&lt;/strong&gt;: &lt;code&gt;tags&lt;/code&gt; with azapi &lt;code&gt;default_tags&lt;/code&gt; support
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud&lt;/strong&gt;: &lt;code&gt;labels&lt;/code&gt; with &lt;code&gt;default_labels&lt;/code&gt; support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means you can maintain consistent tagging policies across your entire multi-cloud infrastructure using a single tool and configuration format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validation in CI/CD
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions example&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate AWS Resources&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terratags -config https://example.com/aws-tags.yaml -dir ./aws-infra&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate GCP Resources&lt;/span&gt;  
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terratags -config https://example.com/gcp-labels.yaml -dir ./gcp-infra&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate Azure Resources&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terratags -config https://example.com/azure-tags.yaml -dir ./azure-infra&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pattern Validation
&lt;/h3&gt;

&lt;p&gt;The advanced pattern validation introduced in earlier versions works consistently across all providers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Works for AWS tags, Azure tags, and GCP labels&lt;/span&gt;
&lt;span class="na"&gt;required_tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^(dev|test|staging|prod)$"&lt;/span&gt;

  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;.[a-zA-Z]{2,}$"&lt;/span&gt;

  &lt;span class="na"&gt;cost_center&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^CC-[0-9]{4}$"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Getting Started with v0.6.0
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Using Homebrew (recommended)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;terratags/tap/terratags

&lt;span class="c"&gt;# Or download from GitHub releases&lt;/span&gt;
&lt;span class="c"&gt;# https://github.com/terratags/terratags/releases/tag/v0.6.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Quick Start with Remote Config
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set up a remote configuration&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://example.com/terratags.yaml&lt;/span&gt;
&lt;span class="na"&gt;required_tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^(dev|test|staging|prod)$"&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;.[a-zA-Z]{2,}$"&lt;/span&gt;
  &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run validation&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terratags &lt;span class="nt"&gt;-config&lt;/span&gt; https://example.com/terratags.yaml &lt;span class="nt"&gt;-dir&lt;/span&gt; ./infra &lt;span class="nt"&gt;-report&lt;/span&gt; report.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set up pre-commit hooks&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/terratags/terratags&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v0.6.0&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terratags&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;--config=https&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;//example.com/terratags.yaml&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;There are a few items in the report generation which I want to look to implement, without breaking existing structure and including all the metadata. &lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/terratags/terratags" rel="noopener noreferrer"&gt;https://github.com/terratags/terratags&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://terratags.github.io/terratags/" rel="noopener noreferrer"&gt;https://terratags.github.io/terratags/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote Config Examples&lt;/strong&gt;: &lt;a href="https://terratags.github.io/terratags/examples/remote_config/" rel="noopener noreferrer"&gt;https://terratags.github.io/terratags/examples/remote_config/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release Notes&lt;/strong&gt;: &lt;a href="https://github.com/terratags/terratags/releases/tag/v0.6.0" rel="noopener noreferrer"&gt;https://github.com/terratags/terratags/releases/tag/v0.6.0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're using Terratags and have been waiting for centralized configuration management, v0.6.0 is definitely worth the upgrade. As always, I'd love to hear about your experience and any feedback you have!&lt;/p&gt;

</description>
      <category>gcp</category>
      <category>hashicorp</category>
      <category>tags</category>
      <category>terratags</category>
    </item>
    <item>
      <title>Exploring terraform provider capabilities with schema analysis</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Thu, 09 Oct 2025 17:56:44 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/exploring-terraform-provider-capabilities-with-schema-analysis-1bmd</link>
      <guid>https://forem.com/quixoticmonk/exploring-terraform-provider-capabilities-with-schema-analysis-1bmd</guid>
      <description>&lt;p&gt;Terraform has become the de facto name when it comes to tools which help you model infrastructure in a declarative language, the idea of which we call as Infrastructure as Code. At a very high level, Terraform is a Go binary which knows how to do some things well. And providers are that much needed &lt;code&gt;context&lt;/code&gt; which terraform needs to work with a platform. The platform can be a cloud service provider or an artifactory platform like jFrog and so on. Most developers only scratch the surface of what Terraform providers offer. While everyone knows about basic resources and datasources available within a provider, anything beyond that is still not easily discovered ( unless the documentation surfaces them up well). &lt;/p&gt;

&lt;p&gt;You're deploying infrastructure with Terraform and need to invalidate a CloudFront cache after your S3 bucket updates (Like with this blog). You've heard Terraform has a new concept called "actions" for this, but how do you discover what's available? In this post, we'll explore how to use the &lt;code&gt;terraform providers&lt;/code&gt; subcommand to identify some of the core capabilities which might exist with the provider you normally interact with. &lt;/p&gt;

&lt;p&gt;Note: With the availability of Terraform MCP servers, these might be easier than how it was earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;p&gt;The sections below lists commands which can provide you more details around the core capabilities( for lack of better terms) that exist with &lt;code&gt;aws&lt;/code&gt; provider. You may use the provider that you use in your daily life.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;jq for maneuvering around the json schema file&lt;/li&gt;
&lt;li&gt;terraform binary :1.14.0-beta versions to gather some of the attributes&lt;/li&gt;
&lt;li&gt;The latest version of your provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  providers schema command
&lt;/h2&gt;

&lt;p&gt;If you have ever run the &lt;code&gt;terraform providers&lt;/code&gt; command, you will see the list of options available; out of which we are going to dive into the &lt;code&gt;schema&lt;/code&gt; subcommand. Terraform's &lt;code&gt;providers schema&lt;/code&gt; command exposes every capability your provider supports in a structured JSON format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ terraform providers --help

Options:

  -test-directory=path  Set the Terraform test directory, defaults to "tests".

Subcommands:
    lock      Write out dependency locks for the configured providers
    mirror    Save local copies of all required provider plugins
    schema    Show schemas for the providers used in the configuration

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

&lt;/div&gt;



&lt;p&gt;Warning: If you run the command &lt;code&gt;terraform provider schema -json&lt;/code&gt; on your terminal expect a ball of text to fill your terminal screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Setup
&lt;/h2&gt;

&lt;p&gt;Let's start with a minimal Terraform configuration to explore the AWS &amp;amp; AWSCC provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;awscc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/awscc"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"awscc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running &lt;code&gt;terraform init&lt;/code&gt;, we can extract the complete provider schema using the below command. I am going to pipe the results out to a json file so that I can perform my analysis on the available file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform providers schema &lt;span class="nt"&gt;-json&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; aws_schema.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provider Capability Categories
&lt;/h2&gt;

&lt;p&gt;First, let's see what capability types are available. Over the course of time, Terraform introduced many schema capabilities/types that providers adopted. To get the list of capabilities, all we need to do is find the keys inside &lt;code&gt;provider_schemas&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;At the time of writing this, the AWS provider supports 8 distinct capability types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"] | keys'&lt;/span&gt; aws_schema.json

&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"action_schemas"&lt;/span&gt;,
  &lt;span class="s2"&gt;"data_source_schemas"&lt;/span&gt;,
  &lt;span class="s2"&gt;"ephemeral_resource_schemas"&lt;/span&gt;,
  &lt;span class="s2"&gt;"functions"&lt;/span&gt;,
  &lt;span class="s2"&gt;"list_resource_schemas"&lt;/span&gt;,
  &lt;span class="s2"&gt;"provider"&lt;/span&gt;,
  &lt;span class="s2"&gt;"resource_identity_schemas"&lt;/span&gt;,
  &lt;span class="s2"&gt;"resource_schemas"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What about the AWSCC provider ? It supports 5 at the moment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/awscc"] | keys'&lt;/span&gt; aws_schema.json
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"data_source_schemas"&lt;/span&gt;,
  &lt;span class="s2"&gt;"list_resource_schemas"&lt;/span&gt;,
  &lt;span class="s2"&gt;"provider"&lt;/span&gt;,
  &lt;span class="s2"&gt;"resource_identity_schemas"&lt;/span&gt;,
  &lt;span class="s2"&gt;"resource_schemas"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Let's get some numbers
&lt;/h3&gt;

&lt;p&gt;Now let's find how many resources exist in each of the provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].resource_schemas | keys | length'&lt;/span&gt; aws_schema.json
1544


❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/awscc"].resource_schemas | keys | length'&lt;/span&gt; aws_schema.json
1214
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are &lt;code&gt;1544&lt;/code&gt; &amp;amp; &lt;code&gt;1214&lt;/code&gt; resources in the AWS and AWSCC providers respectively at this point.  The key thing to note here is that you can replace the capability type you want to review and gather the specific detaills of those types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="c"&gt;# Count data sources  &lt;/span&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].data_source_schemas | keys | length'&lt;/span&gt; aws_schema.json

&lt;span class="c"&gt;# Count functions&lt;/span&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].functions | keys | length'&lt;/span&gt; aws_schema.json

&lt;span class="c"&gt;# Count action schemas&lt;/span&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].action_schemas | keys | length'&lt;/span&gt; aws_schema.json

&lt;span class="c"&gt;# Count ephemeral resources&lt;/span&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].ephemeral_resource_schemas | keys | length'&lt;/span&gt; aws_schema.json

&lt;span class="c"&gt;# Count resource identity schemas&lt;/span&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].resource_identity_schemas | keys | length'&lt;/span&gt; aws_schema.json

&lt;span class="c"&gt;# Count list resources&lt;/span&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].list_resource_schemas | keys | length'&lt;/span&gt; aws_schema.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For ease of associating them, I have just mapped them to the capability type. You can probably do this with jq as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"action_schemas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;actions&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"data_source_schemas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;622&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;sources&lt;/span&gt;&lt;span class="w"&gt;  
  &lt;/span&gt;&lt;span class="s2"&gt;"ephemeral_resource_schemas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ephemeral&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resources&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"functions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;functions&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"list_resource_schemas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resources&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;configuration&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"resource_identity_schemas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;212&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;identity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;schemas&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"resource_schemas"&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;544&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resources&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dive deep into a type
&lt;/h2&gt;

&lt;p&gt;So all these commands are great at presenting you the details in a provider schema. Why do I need it again ? HashiConf '25 introduced two core capabilities with provider support. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Terraform search&lt;/code&gt; : Query a list of resources of certain type(s) and provide a means to import them to your state file , if you so desire.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Terraform actions&lt;/code&gt; : A HCL native way of managing pre/post- operations on resources during the lifecycle management of resources. In short, the idea is to give an alternative to some of the bespoke scripts and local-exec provisioners running in the modules you know of. This blog itself uses a cache invalidation step with CloudFront once the deployment is complete. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of my biggest pain points with the provider documentation currently is with how these types are listed. Or rather not listed. AWS provider groups capability types by the resource types like &lt;code&gt;S3 bucket&lt;/code&gt; and so there is no easy way to identify the number of actions or list resources supported across the  provider. Once I know that a resource type exists, I can search for that one and identify the attributes to use.&lt;/p&gt;

&lt;p&gt;Can we get that information from the &lt;code&gt;providers schema&lt;/code&gt;? Yes, you can. Let's start with the action schema.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; What action resources are supported in AWS provider ?&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].action_schemas | keys'&lt;/span&gt; aws_schema.json

&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"aws_cloudfront_create_invalidation"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_codebuild_start_build"&lt;/span&gt;, 
  &lt;span class="s2"&gt;"aws_ec2_stop_instance"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_events_put_events"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_lambda_invoke"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_ses_send_email"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_sfn_start_execution"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_sns_publish"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; What about list resources ?&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].list_resource_schemas | keys'&lt;/span&gt; aws_schema.json
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"aws_batch_job_queue"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what if I want to know the supported actions or list resources across other providers ? You can easily change the provider configuration in the &lt;code&gt;providers.tf&lt;/code&gt; file I had above to include your provider details and run the same commands. OR, you can take a look at my GitHub repo &lt;a href="https://github.com/quixoticmonk/extract-provider-supported-actions" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Yes, it is named provider actions before I realized I could do this for all types ( actions, list, functions, ephemeral and so on)&lt;/p&gt;

&lt;p&gt;To drive the point home: &lt;/p&gt;

&lt;h3&gt;
  
  
  Discover available provider functions:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all functions&lt;/span&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].functions | keys'&lt;/span&gt; aws_schema.json

&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"arn_build"&lt;/span&gt;,      // Construct ARNs programmatically
  &lt;span class="s2"&gt;"arn_parse"&lt;/span&gt;,      // Parse ARN components
  &lt;span class="s2"&gt;"trim_iam_role_path"&lt;/span&gt;  // Clean IAM role paths
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ephemeral Resources: Temporary Data
&lt;/h2&gt;

&lt;p&gt;Discover ephemeral resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all ephemeral resources&lt;/span&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].ephemeral_resource_schemas | keys'&lt;/span&gt; aws_schema.json


&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"aws_cognito_identity_openid_token_for_developer_identity"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_eks_cluster_auth"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_kms_secrets"&lt;/span&gt;, 
  &lt;span class="s2"&gt;"aws_lambda_invocation"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_secretsmanager_random_password"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_secretsmanager_secret_version"&lt;/span&gt;,
  &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Review attributes in a specific resource
&lt;/h2&gt;

&lt;p&gt;It doesn't stop there. You have the ability to dive deeper into a resource in a provider and understand the list of attributes expected and if needed pull the required attributes alone. How will you do it ?&lt;/p&gt;

&lt;p&gt;Let's check how many attributes are marked as required on &lt;code&gt;aws_s3_bucket&lt;/code&gt; resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
❯ jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/aws"].resource_schemas.aws_s3_bucket.block.attributes | to_entries | map(select(.value.required == true)) | map(.key)'&lt;/span&gt; aws_schema.json

&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="c"&gt;# None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The next time you look at a provider and want to identify what it offers, I hope you will give this a try.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Terraform MCP server is adding the actions and list resources discovery into the latest iteration from the looks of it. Hopefully that makes this simpler if you use a Gen AI agent in your work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To explore your provider schemas; replace the  with yours once the provider block is updated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initialize your Terraform configuration&lt;/span&gt;
terraform init

&lt;span class="c"&gt;# Extract complete schema&lt;/span&gt;
terraform providers schema &lt;span class="nt"&gt;-json&lt;/span&gt; ❯ provider_schema.json

&lt;span class="c"&gt;# Explore !!!&lt;/span&gt;
jq &lt;span class="s1"&gt;'.provider_schemas | keys'&lt;/span&gt; provider_schema.json
jq &lt;span class="s1"&gt;'.provider_schemas["registry.terraform.io/hashicorp/&amp;lt;provider_name&amp;gt;"] | keys'&lt;/span&gt; provider_schema.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;GitHub repo with List/Actions : &lt;a href="https://github.com/quixoticmonk/extract-provider-supported-actions" rel="noopener noreferrer"&gt;Extract actions/list&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hashicorp</category>
      <category>provider</category>
      <category>aws</category>
      <category>schemas</category>
    </item>
    <item>
      <title>Mastering Terraform Stacks: Post-GA Command References Part 1</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Thu, 02 Oct 2025 16:17:44 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/mastering-terraform-stacks-post-ga-command-references-part-1-1b1h</link>
      <guid>https://forem.com/quixoticmonk/mastering-terraform-stacks-post-ga-command-references-part-1-1b1h</guid>
      <description>&lt;p&gt;This post builds on my previous article &lt;a href="https://manuchandrasekhar.com/posts/2025/08-25-tfstacks/" rel="noopener noreferrer"&gt;Terraform Stacks: A Revisit&lt;/a&gt;, where I reviewed what Terraform Stacks had evolved to from an idea or beta. The concept of having that layer of abstraction around deployment was novel and I am still looking forward to some of the improvements we will see in the future. In this post, I am looking to review the &lt;code&gt;terraform stacks&lt;/code&gt; subcommand in detail based on the updates from HashiConf. Some of the updates actually broke some live demo plans of some fellow HashiCorp ambassadors on Day 2 of HashiConf :) You know who you are.&lt;/p&gt;

&lt;p&gt;Anyone wanting a good reference of the changes from beta to General availability(GA) should read the official documentation &lt;a href="https://developer.hashicorp.com/terraform/language/stacks/update-GA" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I do feel there are some elements missing or scattered around the documentation elsewhere which could have been surfaced up better. At a high level, &lt;code&gt;terraform stacks&lt;/code&gt; subcommand provides a comprehensive set of options organized into primary operations for configuration, deployment groups, and deployment runs. &lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites and Setup
&lt;/h2&gt;

&lt;p&gt;Before diving into the commands, you'll need an active HCP Terraform setup. This includes setting up an HCP Terraform organization, creating a project ( or use the &lt;code&gt;Default Project&lt;/code&gt; ) within that organization, and generating a token with appropriate permissions for stack operations. The CLI relies on specific environment variables including &lt;code&gt;TF_STACKS_ORGANIZATION_NAME&lt;/code&gt;,&lt;code&gt;TF_STACKS_PROJECT_NAME&lt;/code&gt; ,&lt;code&gt;TF_STACKS_STACK_NAME&lt;/code&gt; , and &lt;code&gt;TF_TOKEN_app_terraform_io&lt;/code&gt; for authentication and targeting the correct resources if not explicitly provided in the commands as arguments or options.&lt;/p&gt;

&lt;p&gt;Building on the examples from my previous post, we'll explore the foundational commands that every Terraform Stacks workflow begins with. The terraform stacks init command prepares your configuration directory, while providers-lock ensures consistent provider versions across environments. The validate command checks your configuration syntax before deployment. These commands form the foundation that enables the more advanced operations covered in the following sections.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;  I have &lt;code&gt;terraform&lt;/code&gt; aliased to &lt;code&gt;tf&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you run &lt;code&gt;terraform stacks&lt;/code&gt; post GA, you would see these additional subcommands for &lt;code&gt;configuration&lt;/code&gt;, &lt;code&gt;deployment-group&lt;/code&gt; and &lt;code&gt;deployment-run&lt;/code&gt;. It would be a miss not to introduce these without which the post will be difficult to comprehend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks
Usage: terraform stacks [global options] &amp;lt;command&amp;gt; [args]

The available commands for execution are listed below.

Primary Commands:
  init              Prepare the configuration directory for further commands
  providers-lock    Write out dependency locks for the configured providers
  validate          Check whether the configuration is valid
  create            Create a stack
  list              List stacks for a given organization and/or project.
  version           Show the current Stacks Plugin version
  fmt               Reformat your Terraform Stacks configuration to a canonical format


Sub-commands:
  configuration     Manage stack configuration
  deployment-group  Manage deployment groups
  deployment-run    Manage deployment runs


Global options (use these before the subcommand, if any):
  -chdir=DIR              Switch to a different working directory before executing the
                          given subcommand.
  -plugin-cache-dir=DIR   Override the default directory where the stack plugin binary is cached.
  -stacks-version         An alias for the "version" subcommand.
  -no-color=BOOL          Disable color output. Must be explicitly set to true or false.

Usage help:
  -usage                  Show this usage output or the usage for a specified subcommand.
                          Use after the command: terraform stacks &amp;lt;command&amp;gt; -usage


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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Components : is a replacement of modules in a way; though it is more than modules.Translates to what infrastructure, or components, should be part of the Stack.&lt;/li&gt;
&lt;li&gt;Deployments : Where and how many times to deploy the infrastructure in the Stack.&lt;/li&gt;
&lt;li&gt;Deployment Groups : Combine a set of deployment targets into a group. Allows to set some specific approval rules for each deployment groups based on specific contextual conditions.&lt;/li&gt;
&lt;li&gt;Configuration : Everything including the above with the terraform version , lock files which make up a Stack&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  General notes before we dive in
&lt;/h2&gt;

&lt;p&gt;I do like the -json usage across the subcommands. May be we are gearing up to a usage of these commands from an agent :)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Can we get some short form syntaxes for these subcommand options already please ?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create a Stack from your terminal
&lt;/h2&gt;

&lt;p&gt;I somehow feel this command is a little incomplete as it stands now. I am ignoring the global options as they are the same 4 always across Stacks' subcommands. Lets create a Stack in three different variants and see what it translates to in the HCP Terraform UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks  create  -organization-name ne-devops -project-name "Default Project" -stack-name "manu-option1"
Creating stack...
Stack: Created stack 'manu-option1'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3n94tgsxwfbtmgff55ek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3n94tgsxwfbtmgff55ek.png" alt=" " width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reviewing the settings..&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23m72u9dmajg1v89myxo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23m72u9dmajg1v89myxo.png" alt=" " width="800" height="681"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 2
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks  create  -organization-name ne-devops -project-name "Default Project" -stack-name "manu-option2" -with-template
Creating stack...
Stack: Created stack 'manu-option2'
Creating example stacks configuration...
Stack: Created example stack configuration for 'manu-option2'

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

&lt;/div&gt;


&lt;p&gt;The result is ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yrq0gp251f5oti67m6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yrq0gp251f5oti67m6a.png" alt=" " width="798" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reviewing the settings...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dcolh1ypgterb0nwck0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dcolh1ypgterb0nwck0.png" alt=" " width="800" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I do not see a difference in the stack created on the HCP Terraform UI with the &lt;code&gt;-with-template&lt;/code&gt; option supplied with the &lt;code&gt;terraform stacks create&lt;/code&gt; command. Now the argument reads as below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; -with-template      Generate boilerplate configuration

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

&lt;/div&gt;



&lt;p&gt;May be it is a placeholder for more things to come.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3
&lt;/h3&gt;

&lt;p&gt;Though this is more of a variant only in terms of how the data is presented back to the user on the completion of a command, I see the value when it comes to passing these logs to some external systems or even to an operations agent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks  create  -organization-name ne-devops -project-name "Default Project" -stack-name "manu-option3" -json
{"@level":"info","@message":"Creating stack","@module":"terraform.stacks.ui","@timestamp":"2025-10-01T21:29:06.137667-04:00","format_version":"1.0","type":"create_in_progress"}
{"@level":"info","@message":"Created stack","@module":"terraform.stacks.ui","@timestamp":"2025-10-01T21:29:06.421967-04:00","format_version":"1.0","type":"create_success"}

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  What am I missing ?
&lt;/h3&gt;

&lt;p&gt;The stacks which are created are barebones at this point. What would make this command wholesome is the ability to associate the stacks with a VCS connection and to be able to start that first run of the configuration fetch that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  List the stacks
&lt;/h2&gt;

&lt;p&gt;Lets run the &lt;code&gt;stacks list&lt;/code&gt; without any arguments on a new terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks list
╷
│ Error: Missing required flag `-organization-name`
│
│ Please provide the `-organization-name` flag with the name of the target organization.
│ Alternatively, set the ENV VAR 'TF_STACKS_ORGANIZATION_NAME'.
╵
Failure! Could not list stacks

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

&lt;/div&gt;



&lt;p&gt;As expected, we do see the need to specify the Organization name.&lt;/p&gt;

&lt;h3&gt;
  
  
  tabular format
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
tf stacks list -organization-name ne-devops
        Stack Name        |         Stack ID         |        Created At        |        Updated At        |
       manu-option2       |   st-Y2J3wa6yKBSP83Hc    |    Oct 02, 2025 13:02    |    Oct 02, 2025 13:02    |
       manu-option1       |   st-31HY1439bDRVawex    |    Oct 02, 2025 13:00    |    Oct 02, 2025 13:00    |
        app-stack         |   st-g7uPLPR4pnWyvT7e    |    Oct 01, 2025 17:44    |    Oct 01, 2025 17:44    |
   manuchn-stacks-2025    |   st-jcgFCdyai89kcn5S    |    Sep 30, 2025 14:08    |    Sep 30, 2025 14:08    |
     pet-nulls-stack      |   st-uyBoA811rPgNzvw3    |    Sep 08, 2024 04:41    |    Sep 08, 2024 04:41    |
  external_module_stack   |   st-wfXoXu2a56n3GC4Y    |    Mar 09, 2024 22:00    |    Mar 09, 2024 23:09    |
     pet-nulls-stack      |   st-M6kzAWsw7e7Ldep8    |    Feb 28, 2024 22:57    |    Feb 28, 2024 22:59    |

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  json format
&lt;/h3&gt;

&lt;p&gt;I was thinking the &lt;code&gt;stacks list&lt;/code&gt; would benefit from a column &lt;code&gt;project&lt;/code&gt; till I ran the &lt;code&gt;-json&lt;/code&gt; format. The data doesn't seem to be present in the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks list -organization-name ne-devops -json
{"format_version":"1.0","organization_name":"ne-devops","project_id":"","stacks":[{"created_at":"2025-10-02T13:02:49Z","id":"st-Y2J3wa6yKBSP83Hc","name":"manu-option2","updated_at":"2025-10-02T13:02:49Z"},{"created_at":"2025-10-02T13:00:22Z","id":"st-31HY1439bDRVawex","name":"manu-option1","updated_at":"2025-10-02T13:00:22Z"},{"created_at":"2025-10-01T17:44:34Z","id":"st-g7uPLPR4pnWyvT7e","name":"app-stack","updated_at":"2025-10-01T17:44:34Z"},{"created_at":"2025-09-30T14:08:37Z","id":"st-jcgFCdyai89kcn5S","name":"manuchn-stacks-2025","updated_at":"2025-09-30T14:08:37Z"},{"created_at":"2024-09-08T04:41:32Z","id":"st-uyBoA811rPgNzvw3","name":"pet-nulls-stack","updated_at":"2024-09-08T04:41:32Z"},{"created_at":"2024-03-09T22:00:44Z","id":"st-wfXoXu2a56n3GC4Y","name":"external_module_stack","updated_at":"2024-03-09T23:09:22Z"},{"created_at":"2024-02-28T22:57:52Z","id":"st-M6kzAWsw7e7Ldep8","name":"pet-nulls-stack","updated_at":"2024-02-28T22:59:54Z"}]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Filter by project
&lt;/h3&gt;

&lt;p&gt;All the stacks I had listed were part of the &lt;code&gt;Default Project&lt;/code&gt; anyway. So it is not not going to show any difference here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ tf stacks list -organization-name ne-devops -project-name "Default Project"
        Stack Name        |         Stack ID         |        Created At        |        Updated At        |
       manu-option2       |   st-Y2J3wa6yKBSP83Hc    |    Oct 02, 2025 13:02    |    Oct 02, 2025 13:02    |
       manu-option1       |   st-31HY1439bDRVawex    |    Oct 02, 2025 13:00    |    Oct 02, 2025 13:00    |
        app-stack         |   st-g7uPLPR4pnWyvT7e    |    Oct 01, 2025 17:44    |    Oct 01, 2025 17:44    |
   manuchn-stacks-2025    |   st-jcgFCdyai89kcn5S    |    Sep 30, 2025 14:08    |    Sep 30, 2025 14:08    |
  external_module_stack   |   st-wfXoXu2a56n3GC4Y    |    Mar 09, 2024 22:00    |    Mar 09, 2024 23:09    |
     pet-nulls-stack      |   st-M6kzAWsw7e7Ldep8    |    Feb 28, 2024 22:57    |    Feb 28, 2024 22:59    |


❯ tf stacks list -organization-name ne-devops -project-name "experiments"
        Stack Name        |         Stack ID         |        Created At        |        Updated At        |


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

&lt;/div&gt;



&lt;p&gt;Tying this back to the HCP Terraform UI; why is Stacks not an entity of its own similar to the workspaces ?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjux3hhrhduk3jmj9uin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjux3hhrhduk3jmj9uin.png" alt=" " width="270" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration related
&lt;/h2&gt;

&lt;p&gt;As we are peeling the layers of the Stacks onion, we have till now looked at adding the Stacks frame onto the HCP Terraform organization. But it is missing any sort of configuration items to provision any infrastructure to a target platform.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I have configured a variableset on my HCP Terraform organization with an IAM role which allows any stacks in my org to provision some resources ( S3, VPCs) if they fall under my HCP Terraform organization. I am not going to dive into that as that OIDC based setup hasn't changed from the time of beta.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The commands below are run on a directory which has my &lt;a href="https://github.com/quixoticmonk/hashiconf-stack" rel="noopener noreferrer"&gt;HashiConf-Stack&lt;/a&gt; repo cloned. So this command is supposed to supplement the &lt;code&gt;stacks create&lt;/code&gt; in my mind. For most of the commands below, a stack is the frame of reference. So you need either a combination of &lt;code&gt;organization-name&lt;/code&gt;, &lt;code&gt;project-name&lt;/code&gt;, &lt;code&gt;stack-name&lt;/code&gt; or &lt;code&gt;stack-id&lt;/code&gt;. I do wonder why we need the project name since we can list the stacks without a project reference in the &lt;code&gt;stacks list&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  list
&lt;/h3&gt;

&lt;p&gt;I am going to use one of the stack ids from the create operations we did earlier. As expected, there are no configuration items associated with the stack &lt;code&gt;manu-option1&lt;/code&gt; at this point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; tf stacks configuration list -stack-id st-31HY1439bDRVawex
     Sequence Number      |     Configuration ID     |


&amp;gt; tf stacks configuration list -stack-id st-31HY1439bDRVawex --json
{"configurations":null,"format_version":"1.0","stack_id":"st-31HY1439bDRVawex"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Upload
&lt;/h3&gt;

&lt;p&gt;Running this from a non-stacks directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks configuration upload -stack-id st-31HY1439bDRVawex
╷
│ Error: Missing .terraform-version file
│
│ Terraform Stacks requires a .terraform-version file alongside the root .tfcomponent.hcl files.
╵
╷
│ Error: Failed to open source bundle
│
│ Failed to open source bundle: cannot read manifest: open /Users/manuchn/Documents/2025/TFC/Hashi/experiments/manuchandrasekhar.com/.terraform/modules/terraform-sources.json: no such file or directory. You may need to run `terraform stacks init`.
╵
failed to upload stack configuration

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

&lt;/div&gt;



&lt;p&gt;So , you need to initialize the stack , provide a lock configuration and a terraform version. We didn't specify a terraform version when we created the stack using the cli, which I believe doesn't have a version association till a configuration is uploaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks init
Provider "hashicorp/aws" already locked at "5.100.0".
Provider "hashicorp/random" already locked at "3.7.2".
Downloading "hashicorp/aws"@"5.100.0"....
Downloading "hashicorp/random"@"3.7.2"....
Success! Configuration has been initialized and more commands can now be executed.

❯ tf stacks configuration upload -stack-id st-31HY1439bDRVawex
Uploading stack configuration...

Configuration for Stack (id: 'st-31HY1439bDRVawex') was uploaded
Configuration ID: stc-S5Sr3y57SWDLd5G5
Sequence Number: 1

See run at: https://app.terraform.io/app/ne-devops/projects/prj-qbcM8hb5k4EUCGj4/stacks/st-31HY1439bDRVawex/configurations-ga/stc-S5Sr3y57SWDLd5G5


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

&lt;/div&gt;



&lt;p&gt;The url which has the configuration or run leads you to the page below if you open the configuration 1&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2x408imd3pawvik9xa4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2x408imd3pawvik9xa4.png" alt=" " width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;  You do not see an apply creating resources as I had &lt;code&gt;destroy=true&lt;/code&gt; set on the deployments for the first time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Subsequently a &lt;code&gt;configuration list&lt;/code&gt; renders the below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks configuration list -stack-id st-31HY1439bDRVawex
     Sequence Number      |     Configuration ID     |
            1             |   stc-S5Sr3y57SWDLd5G5   |

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;  I have seen some glitches here where the &lt;code&gt;Configurations&lt;/code&gt; remain blank even when there is an active configuration. This could be some caching issues on my browser. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's re-run the configuration upload with the &lt;code&gt;destroy=true&lt;/code&gt; removed in the &lt;code&gt;tfdeploy.hcl&lt;/code&gt; file. I did make a mistake on my configuration and had to run the configuration upload twice. So you will see configuration versions up to 3 in the list when we do those. With the auto-approve on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdebiiyu9682tv0bn75x0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdebiiyu9682tv0bn75x0.png" alt=" " width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Diving into the deployment in HCP Terraform, you can see that the resources have been created in my target AWS account using the configuration upload without a VCS connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcirirbf56oxur2mps9uy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcirirbf56oxur2mps9uy.png" alt=" " width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The state from the Stacks standpoint is slightly different from how you are used to from a workspace or pre-GA.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F51i1yqb3h6805sb6b8pl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F51i1yqb3h6805sb6b8pl.png" alt=" " width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The command &lt;code&gt;configurations upload&lt;/code&gt; has this option &lt;code&gt;speculative&lt;/code&gt; which can prevent triggering any deployments via this configuration upload. Lets try this again with the option set to true.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tf stacks configuration upload -stack-id st-31HY1439bDRVawex -speculative
Uploading stack configuration...

Speculative Configuration for Stack (id: 'st-31HY1439bDRVawex') was uploaded
Configuration ID: stc-PbHnPRArTLBABzCC

See run at: https://app.terraform.io/app/ne-devops/projects/prj-qbcM8hb5k4EUCGj4/stacks/st-31HY1439bDRVawex/configurations-ga/stc-PbHnPRArTLBABzCC

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo5efsg1jw6g21kld25s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo5efsg1jw6g21kld25s.png" alt=" " width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My issue here is with the phrasing on the option &lt;code&gt;speculative&lt;/code&gt;. You are triggering a deployment run under a configuration &lt;code&gt;null&lt;/code&gt;. I believe this maps to a speculative plan run on workspaces. I think this should be updated to state that explicitly than using the word deployment here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-speculative: Upload will be speculative and will not trigger any deployments.

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I hope they have an option to see this as a json with some download options.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Watch
&lt;/h3&gt;

&lt;p&gt;This is your command line equivalent of live loading on the HCP Terraform UI or the &lt;code&gt;kubectl watch&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; tf stacks configuration upload -stack-id st-31HY1439bDRVawex
Uploading stack configuration...

Configuration for Stack (id: 'st-31HY1439bDRVawex') was uploaded
Configuration ID: stc-e6dfkfXL3HgDq1U2
Sequence Number: 4

See run at: https://app.terraform.io/app/ne-devops/projects/prj-qbcM8hb5k4EUCGj4/stacks/st-31HY1439bDRVawex/configurations-ga/stc-e6dfkfXL3HgDq1U2

❯ tf stacks configuration watch -configuration-id stc-e6dfkfXL3HgDq1U2

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

&lt;/div&gt;



&lt;p&gt;Entering the command launches you to a watch interface which looks to provide you with a real time update on what is happening with the configuration or deployment. Keep in mind that I have the auto-approve set on the deployment groups which is making the response below skip a &lt;code&gt;Pending&lt;/code&gt; state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
[Configuration Sequence Number: 4]
  ✓ Configuration: 'stc-e6dfkfXL3HgDq1U2'                   [Completed]        [24s]
    - Deployment Group: 'test_stack'                        [Deploying]        [33s]

Press q to quit

....


[Configuration Sequence Number: 4]
  ✓ Configuration: 'stc-e6dfkfXL3HgDq1U2'                   [Completed]        [24s]
    ✓ Deployment Group: 'test_stack'                        [Succeeded]        [37s]

Press q to quit


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

&lt;/div&gt;



&lt;p&gt;There are additional commands in the configuration side including &lt;code&gt;fetch&lt;/code&gt; which requires a VCS connection. I am going to bundle that along the VCS based setup in a different post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;

&lt;p&gt;I could possibly have set these below environment variables than having to add those options in all my commands. But I preferred to know what I am working with , this time around. Maybe in a CI based environment, the environment variable setup would come in handy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_STACKS_ORGANIZATION_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ne-devops"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_STACKS_PROJECT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Default Project"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_STACKS_STACK_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"app-stack"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With the recent updates , Terraform Stacks provides a unified CLI experience for managing deployments directly against HCP Terraform organizations, offering an alternative to VCS-based deployment workflows. I hope that current implementation gaps will be addressed as platform teams adopt Stacks for managing baseline infrastructure that development teams build upon. As I have always said, Stacks introduces new approaches for provisioning foundational infrastructure across target platforms. However, documented post-GA limitations currently restrict broader adoption in scenarios like AWS OU-based deployments that would typically use StackSets or something similar. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HCP Terraform supports up to a maximum of 20 deployments for a Stack.&lt;/li&gt;
&lt;li&gt;Deployment groups currently support a single deployment for a Stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I planned to write about deployment groups and runs. But with the length of the post, I felt it is better to break this into a few different posts.&lt;/p&gt;

</description>
      <category>hashicorp</category>
      <category>hashiconf</category>
      <category>stacks</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Terraform stacks: A revisit</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Mon, 01 Sep 2025 22:14:31 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/terraform-stacks-a-revisit-462l</link>
      <guid>https://forem.com/quixoticmonk/terraform-stacks-a-revisit-462l</guid>
      <description>&lt;p&gt;So it's been almost a year since I published the article on Stacks. I even presented on Stacks on two occassions with HashiCorp engineers. The promise of delivering that multi-region/account/environment experience in a native Terraform language was something exciting. So with HashiConf coming up in 4 weeks, where is it now ? What changes have I seen in the recent days ?&lt;/p&gt;

&lt;p&gt;Before we jump into the details, lets pull up the definition of Stacks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Terraform Stacks lets you group and manage your Terraform configurations as repeatable units of infrastructure. Each stack defines what to deploy, where to deploy it, and how to keep it in sync—making it easier to manage complex environments, reuse modules, and automate deployments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Spoiler alert ; below seems to be a subtle way to say Stacks is going GA soon around HashiConf. Yaaay !!!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Starting September 25, 2025, Stacks usage will count toward your HCP Terraform resource usage limits&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Terraform stacks subcommand
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/hashicorp/terraform/releases/tag/v1.13.0" rel="noopener noreferrer"&gt;Terraform binary recently released&lt;/a&gt; support for &lt;code&gt;stacks&lt;/code&gt; which was available via the custom &lt;a href="https://developer.hashicorp.com/terraform/language/stacks/reference/tfstacks-cli" rel="noopener noreferrer"&gt;tfstacks cli&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This did give the impression that &lt;code&gt;Stacks&lt;/code&gt; is possibly coming to the community edition, but the usage details indicate the opposite.&lt;/p&gt;

&lt;p&gt;Unlike other CLI utilities or subcommands, &lt;code&gt;terraform stacks&lt;/code&gt; uses a &lt;code&gt;usage&lt;/code&gt; option than help. You could very well type &lt;code&gt;terraform stacks&lt;/code&gt; whcih will give you the same output in your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform stacks -usage
Usage: terraform stacks [global options] &amp;lt;command&amp;gt; [args]

The available commands for execution are listed below.

Primary Commands:
  init              Prepare the configuration directory for further commands
  providers-lock    Write out dependency locks for the configured providers
  validate          Check whether the configuration is valid
  version           Show the current Stacks Plugin version
  fmt               Reformat your Terraform Stacks configuration to a canonical format


Sub-commands:


Global options (use these before the subcommand, if any):
  -chdir=DIR              Switch to a different working directory before executing the
                          given subcommand.
  -plugin-cache-dir=DIR   Override the default directory where the stack plugin binary is cached.
  -stacks-version         An alias for the "version" subcommand.
  -no-color=BOOL          Disable color output. Must be explicitly set to true or false.

Usage help:
  -usage                  Show this usage output or the usage for a specified subcommand.
                          Use after the command: terraform stacks &amp;lt;command&amp;gt; -usage

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

&lt;/div&gt;



&lt;p&gt;Let's see how this translates to the tfstacks-cli options.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tfstacks init&lt;/code&gt; -&amp;gt; &lt;code&gt;terraform stacks init&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tfstacks validate&lt;/code&gt; -&amp;gt; &lt;code&gt;terraform stacks validate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tfstacks providers lock&lt;/code&gt; -&amp;gt; &lt;code&gt;terraform stacks providers-lock&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tfstacks fmt&lt;/code&gt; -&amp;gt; &lt;code&gt;terraform stacks fmt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am not really sure what the &lt;code&gt;stacks version&lt;/code&gt; means per se. I was initially thinking there was a corelation between this and terraform version. But the output makes me think this has to do with the Stacks plugin version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform stacks -stacks-version

Terraform Stacks Plugin version: v1.0.0
on darwin_arm64

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

&lt;/div&gt;



&lt;p&gt;The one missing option is in regards to the plan&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tfstacks plan -organization=REQUIRED_ORG_NAME -stack=REQUIRED_STACK_ID -deployment=REQUIRED_DEPLOYMENT [-hostname=hostname]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HCP Terraform
&lt;/h3&gt;

&lt;p&gt;Interstingly there seems to be a manual upload option which is coming to HCP Terraform which could be a replacement of the &lt;code&gt;terraform plan&lt;/code&gt; workflow we are familiar with. Though , it seems like the user interface is ahead of the command implementation on the stacks API. The linked documentation reverts you back to the same page in the Stacks setup (for now). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdft2l2dp323kdxgc4vd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdft2l2dp323kdxgc4vd.png" alt=" " width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HCP Terraform limits the number of deployments up to a maximum of 20 deployments.&lt;br&gt;
    * One of the usecases I felt Stacks would be great for was multi account deployments for platform specific infrastructure. So, if a deployment is a map to an account; this limit might be a hurdle in some cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkw0m46c4h8m1ywhkdu4o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkw0m46c4h8m1ywhkdu4o.png" alt=" " width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Terraform support
&lt;/h2&gt;

&lt;p&gt;I might have overlooked this or never really found a use as I wasn't using Stacks as much as I would have wanted to. &lt;a href="https://registry.terraform.io/providers/hashicorp/tfe/latest/docs/resources/stack" rel="noopener noreferrer"&gt;tfe_stack&lt;/a&gt; seems to support setting up a Stack in HCP Terraform on Stacks enabled projects or organizations using Terraform itself.&lt;/p&gt;
&lt;h2&gt;
  
  
  Linked stacks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reference : &lt;a href="https://developer.hashicorp.com/terraform/language/stacks/deploy/pass-data" rel="noopener noreferrer"&gt;Pass data between Stacks&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is similar to what we had with run trigers on HCP Terraform workspaces. What if you had infrastructure stacks which depended on each other ? Linked stacks give you an option to do exactly that. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Declare an &lt;code&gt;upstream_input&lt;/code&gt; block in your Stack’s deployment configuration to read values from another Stack's &lt;code&gt;publish_output&lt;/code&gt; block. Adding an upstream_input block creates a dependency on the upstream Stack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The example they have is around an application stack depending on the networking infrastructure which is provisioned using a different stack. Rather than just being able to use a datasource to reference existing resources, any changes to the networking stack retriggers the downstream stack , in case that update to the networking stack somehow affects the infrastructure in the downstream. This helps with the situation where a redeployment trigger would help get the updated values of networking infra in the application Stack.&lt;/p&gt;

&lt;p&gt;Output reference in Stacks UI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxkikxjgs52gpoidc3xn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxkikxjgs52gpoidc3xn.png" alt=" " width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Downstream link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwkqtwlp42fvh32fmrepz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwkqtwlp42fvh32fmrepz.png" alt=" " width="800" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will dive into this a little bit more in another post. If you are looking to use the linked stacks in the mean time, keep these in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The output definition in your upstream stacks needs the type defined. The type definition is not something I am used to adding on an output. 
&lt;img src="/tfstacks-upstream-outputdef.png"&gt;
&lt;/li&gt;
&lt;li&gt;An empty value in upstream stack which coincided with a missing definition on my end kept failing on the downstream stack with a &lt;code&gt;missing metadata&lt;/code&gt; with no details in the diagnostics. Updating the type input on the outputs fixed the issues, but it didn't retrigger my downstream_stack.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Changes in the file
&lt;/h2&gt;

&lt;p&gt;Though the documentation still references &lt;code&gt;tfstack.hcl&lt;/code&gt; extensions for anything outside of the deployment configurations, &lt;code&gt;terraform stacks&lt;/code&gt; command says otherwise . I think it is a matter of time before this makes into the documentation. Does it make much difference to the user experience ? Probably not. &lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;terraform stacks validate&lt;/code&gt; on a Stacks configuration similar to one below; you get the error which indicates that the file names are not matching what it expects it to be.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; tree &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
├── components.tfstack.hcl
├── deployments.tfdeploy.hcl
├── infrastructure
│   └── main.tf
├── modules
│   └── random
│       └── main.tf
├── providers.tfstack.hcl
└── variables.tfstack.hcl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deprecation message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;│ Warning: Deprecated filename usage
│
│ This configuration is using the deprecated .tfstack.hcl or .tfstack.json file extensions. This will not be supported in a future version of Terraform, please update your files to use the latest .tfcomponent.hcl or .tfcomponent.json file extensions.

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxh6iuhl5tlfz7sk52kge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxh6iuhl5tlfz7sk52kge.png" alt=" " width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Considering this is still a beta, I expect we will see more changes on these and the APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Will there be a migrate or convert operation from existing workspsace based deployment to Stacks based deployment ?&lt;/li&gt;
&lt;li&gt;Will we start getting the same ecosystem built around Stacks like with workspaces?

&lt;ul&gt;
&lt;li&gt;Policy As Code : For folks using tools outside of the HCP Terraform, that could continue to work. If an organization is invested in Sentinel, how would that work going forward ?&lt;/li&gt;
&lt;li&gt;Day 2 operations, health checks and notifications on workflow operations.&lt;/li&gt;
&lt;li&gt;Deployments to regulated or private environments using Cloud agents or alike.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Language server support for the Stacks blocks : component, deployment and named provider blocks.&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>stacks</category>
      <category>hashicorp</category>
    </item>
    <item>
      <title>Terratags Update: Advanced Pattern Validation, Multi-Provider Support, and Enhanced Reporting</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Fri, 25 Jul 2025 02:14:19 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/terratags-update-advanced-pattern-validation-multi-provider-support-and-enhanced-reporting-3gdc</link>
      <guid>https://forem.com/quixoticmonk/terratags-update-advanced-pattern-validation-multi-provider-support-and-enhanced-reporting-3gdc</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3trz2frwpbv8jonw1zz0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3trz2frwpbv8jonw1zz0.png" alt=" " width="400" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since my initial post about Terratags in May 2025, the project has evolved significantly. What started as a weekend project to enforce basic tag presence on AWS resources has grown into a tagging validation tool with advanced pattern matching, multi-provider support, and updated reporting capabilities. Some of you reached out and opened issues/enhancement requests around some of the feature set currently available v0.4.0. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's New: Major Updates Since v0.1.0
&lt;/h2&gt;

&lt;p&gt;The latest version of Terratags (v0.4.0) introduces several features that address some of the enhancement requests that came my way:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Advanced Pattern Validation with Regex Support
&lt;/h3&gt;

&lt;p&gt;The most significant addition is &lt;strong&gt;pattern validation&lt;/strong&gt; - the ability to validate not just tag presence, but also tag values using regular expressions. This addresses a gap where teams need to enforce specific naming conventions, email formats, or business rules for tag values. I was talking to a customer who wanted something similar few months back to match up to their CMDB naming standards and so on. It did influence some of the work here.&lt;/p&gt;

&lt;h4&gt;
  
  
  Before: Simple Presence Validation
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;required_tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Environment&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Owner&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Project&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Now: Advanced Pattern Validation
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;required_tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Strict environment values&lt;/span&gt;
  &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^(dev|test|staging|prod)$"&lt;/span&gt;

  &lt;span class="c1"&gt;# Valid email for ownership&lt;/span&gt;
  &lt;span class="na"&gt;Owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;.[a-zA-Z]{2,}$"&lt;/span&gt;

  &lt;span class="c1"&gt;# Project code format&lt;/span&gt;
  &lt;span class="na"&gt;Project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[A-Z]{2,4}-[0-9]{3,6}$"&lt;/span&gt;

  &lt;span class="c1"&gt;# No whitespace in names&lt;/span&gt;
  &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;S+$"&lt;/span&gt;

  &lt;span class="c1"&gt;# Simple presence validation (no pattern)&lt;/span&gt;
  &lt;span class="na"&gt;Team&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Multi-Provider Support: Beyond AWS
&lt;/h3&gt;

&lt;p&gt;Terratags now supports additional providers, significantly expanding its utility:&lt;/p&gt;

&lt;h4&gt;
  
  
  AWS Cloud Control (AWSCC) Provider Support
&lt;/h4&gt;

&lt;p&gt;The AWSCC provider uses a different tag format than the standard AWS provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Standard AWS Provider&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;
    &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# AWSCC Provider&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"awscc_apigateway_rest_api"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment"&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Project"&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo"&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terratags now handles both formats seamlessly, with automatic detection and validation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Azure Providers Support
&lt;/h4&gt;

&lt;p&gt;Support for both &lt;strong&gt;azurerm&lt;/strong&gt; and &lt;strong&gt;azapi&lt;/strong&gt; providers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Azurerm Provider&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_resource_group"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-resources"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"West Europe"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Production"&lt;/span&gt;
    &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terratags"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# azapi Provider with default_tags support&lt;/span&gt;
&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"azapi"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Production"&lt;/span&gt;
    &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terratags"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The azapi provider even supports &lt;code&gt;default_tags&lt;/code&gt; similar to the AWS provider, making tag management more consistent across cloud providers. The issue related to this is kept open as I learn more about the Azure side of providers in Terraform.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Enhanced Reporting and Visualization
&lt;/h3&gt;

&lt;p&gt;The HTML reporting system has been modified with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visual indicators&lt;/strong&gt; for compliant, non-compliant, and exempt resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detailed breakdown&lt;/strong&gt; of tag status for each resource&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag source tracking&lt;/strong&gt; (resource-level vs provider default_tags)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exemption details&lt;/strong&gt; including reasons for exemptions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Summary statistics&lt;/strong&gt; including exempt resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag violation counts&lt;/strong&gt; by tag name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwbohijkmpnp9tnwi3om.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwbohijkmpnp9tnwi3om.png" alt=" " width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Pre-commit Hook Integration
&lt;/h3&gt;

&lt;p&gt;As much as I like to take credit for thinking about adding a pre-commit hook integration, I didn't have it in my list of things to do initially. But making this available across the hooks space makes it a lot more valuable and it was easily justifiable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/terratags/terratags&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v0.4.0&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terratags&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;
          &lt;span class="nv"&gt;--config=terratags.yaml&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="nv"&gt;--exemptions=exemptions.yaml&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="nv"&gt;--remediate&lt;/span&gt;
        &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This enables automatic tag validation before commits, preventing non-compliant resources from entering your codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Pattern Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Environment Validation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^(dev|test|staging|prod)$"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Matches: &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;prod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❌ Rejects: &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;, &lt;code&gt;DEV&lt;/code&gt;, &lt;code&gt;Test&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cost Center Format
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;CostCenter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^CC-[0-9]{4}$"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Matches: &lt;code&gt;CC-1234&lt;/code&gt;, &lt;code&gt;CC-5678&lt;/code&gt;, &lt;code&gt;CC-9012&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❌ Rejects: &lt;code&gt;CC123&lt;/code&gt;, &lt;code&gt;CC-12345&lt;/code&gt;, &lt;code&gt;cc-1234&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Code Format
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[A-Z]{2,4}-[0-9]{3,6}$"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Matches: &lt;code&gt;WEB-123456&lt;/code&gt;, &lt;code&gt;DATA-567890&lt;/code&gt;, &lt;code&gt;SEC-123456&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❌ Rejects: &lt;code&gt;web-123&lt;/code&gt;, &lt;code&gt;PROJECT&lt;/code&gt;, &lt;code&gt;ABC-12&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Email Validation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;.[a-zA-Z]{2,}$"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;✅ Matches: &lt;code&gt;devops@company.com&lt;/code&gt;, &lt;code&gt;team.lead@company.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❌ Rejects: &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;user@domain&lt;/code&gt;, &lt;code&gt;@company.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Backward Compatibility and Migration
&lt;/h2&gt;

&lt;p&gt;One of the key design principles for these updates was maintaining backward compatibility. All existing simple configurations continue to work unchanged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This continues to work exactly as before&lt;/span&gt;
&lt;span class="na"&gt;required_tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Environment&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Owner&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can migrate to advanced features incrementally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Mixed format - gradually add patterns&lt;/span&gt;
&lt;span class="na"&gt;required_tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;  &lt;span class="c1"&gt;# Just presence validation&lt;/span&gt;
  &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^(dev|test|staging|prod)$"&lt;/span&gt;  &lt;span class="c1"&gt;# Pattern validation&lt;/span&gt;
  &lt;span class="na"&gt;Owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;  &lt;span class="c1"&gt;# Just presence validation for now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Future Roadmap: What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add Google provider support.&lt;/li&gt;
&lt;li&gt;Review any open issues with module level validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're new to Terratags or want to upgrade:&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Using Homebrew (recommended)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;terratags/tap/terratags
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Basic Usage with Pattern Validation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a configuration file with patterns:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;required_tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^(dev|test|staging|prod)$"&lt;/span&gt;
  &lt;span class="na"&gt;Owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;.[a-zA-Z]{2,}$"&lt;/span&gt;
  &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run validation:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terratags &lt;span class="nt"&gt;-config&lt;/span&gt; config.yaml &lt;span class="nt"&gt;-dir&lt;/span&gt; ./infra &lt;span class="nt"&gt;-report&lt;/span&gt; report.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set up pre-commit hooks:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/terratags/terratags&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v0.4.0&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terratags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/terratags/terratags" rel="noopener noreferrer"&gt;https://github.com/terratags/terratags&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://terratags.github.io/terratags/" rel="noopener noreferrer"&gt;https://terratags.github.io/terratags/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pattern Matching Guide&lt;/strong&gt;: &lt;a href="https://terratags.github.io/terratags/pattern-matching/" rel="noopener noreferrer"&gt;https://terratags.github.io/terratags/pattern-matching/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-commit Integration&lt;/strong&gt;: &lt;a href="https://terratags.github.io/terratags/pre-commit/" rel="noopener noreferrer"&gt;https://terratags.github.io/terratags/pre-commit/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd love to hear about your experience with Terratags, especially how you're using the new pattern validation features. Feel free to reach out with feedback and suggestions !&lt;/p&gt;

</description>
      <category>aws</category>
      <category>hashicorp</category>
      <category>terraform</category>
      <category>tags</category>
    </item>
    <item>
      <title>Building "Python a Snake Game" with Amazon Q Developer CLI: A Journey Back to Gaming Fundamentals</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Sat, 14 Jun 2025 05:05:22 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/building-python-a-snake-game-with-amazon-q-developer-cli-a-journey-back-to-gaming-fundamentals-38jf</link>
      <guid>https://forem.com/quixoticmonk/building-python-a-snake-game-with-amazon-q-developer-cli-a-journey-back-to-gaming-fundamentals-38jf</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8j22vz370xuclh24nj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8j22vz370xuclh24nj5.png" alt="Image description" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Participating in the AWS Build Games Challenge: Build Classics with Amazon Q Developer CLI&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;There's something magical about the simplicity of classic games. While modern gaming pushes the boundaries of graphics, physics, and complexity, sometimes the most engaging experiences come from the fundamentals. That's exactly what I discovered when I decided to recreate the timeless Snake game as "Python a Snake Game" using Python and Pygame, with assistance from Amazon Q Developer CLI.&lt;/p&gt;

&lt;p&gt;This project is my entry for the &lt;a href="https://community.aws/content/2y6egGcPAGQs8EwtQUM9KAONojz/build-games-challenge-build-classics-with-amazon-q-developer-cli" rel="noopener noreferrer"&gt;AWS Build Games Challenge&lt;/a&gt;, where developers are challenged to build classic games using Amazon Q Developer CLI as their coding companion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Snake? The Perfect Starting Point
&lt;/h2&gt;

&lt;p&gt;Snake isn't just a game—it's a rite of passage in my mind. It made sense to build it with two intentions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show whats possible with code to a 10 year old who loves video games.&lt;/li&gt;
&lt;li&gt;Relive the button mashing experience I had with those old Nokia phones from my childhood, Nokia 3310 I think,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I never thought about this this way, Snake teaches fundamental game development concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Game loops and state management&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collision detection&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-time input handling&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic object growth and movement&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Score tracking and game over conditions&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These core concepts form the foundation of virtually every game, making Snake an ideal project for both learning and showcasing development skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why pygame? 
&lt;/h2&gt;

&lt;p&gt;Thats the closest to a gaming library I have ever used and this being the summer vacation time in US; I felt this was the perfect time to introduce programming to my 10 year old. And yes, meeting &lt;code&gt;Al Sweigart&lt;/code&gt; in a PyOhio few years back when I was still understanding the language made want to go back to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of Prompting
&lt;/h2&gt;

&lt;p&gt;One of the most fascinating aspects of working with Amazon Q Developer CLI was discovering how the quality of the initial prompt directly impacts the final product. Rather than simply asking for "a Snake game," I crafted a structured prompt that became the blueprint for the entire project.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Initial Vision
&lt;/h3&gt;

&lt;p&gt;My original request was straightforward but comprehensive: I wanted to create "Python a Snake Game"—a classic Snake game implementation using Python and Pygame that would showcase both nostalgic gameplay and modern development practices. The key was being specific about the experience I wanted to create—not just a functional game, but an enjoyable one.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Comprehensive Structured Prompt: A Development Blueprint
&lt;/h3&gt;

&lt;p&gt;What emerged from this collaboration was a detailed structured prompt that served as both specification and guide. This wasn't just a list of features—it was a comprehensive development framework that addressed every aspect of game creation: &lt;a href="https://github.com/quixoticmonk/python/blob/main/snake_game_prompt.md" rel="noopener noreferrer"&gt;Prompt&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From Prompt to Working Game: The Development Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Following the Blueprint
&lt;/h3&gt;

&lt;p&gt;The beauty of having a  structured prompt became evident during implementation. Instead of making ad-hoc decisions or getting stuck on design choices, I had a clear roadmap to follow. Every feature request in the prompt translated directly into code structure and functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success Criteria as Development Milestones:&lt;/strong&gt;&lt;br&gt;
The detailed structured prompt defined specific success criteria that became my development checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Game runs without errors in 800x600 window&lt;/li&gt;
&lt;li&gt;✅ Responsive arrow key controls&lt;/li&gt;
&lt;li&gt;✅ Proper collision detection&lt;/li&gt;
&lt;li&gt;✅ Score tracking functionality
&lt;/li&gt;
&lt;li&gt;✅ Clean game over and restart mechanics&lt;/li&gt;
&lt;li&gt;✅ Smooth gameplay experience&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Technical Implementation Highlights
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Following the Blueprint
&lt;/h3&gt;

&lt;p&gt;The structured prompt's emphasis on object-oriented design resulted in clean, maintainable code structure.&lt;/p&gt;

&lt;p&gt;The game architecture follows clean object-oriented principles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Snake&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Initialize snake in center with 3 segments
&lt;/span&gt;        &lt;span class="n"&gt;start_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GRID_WIDTH&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;start_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GRID_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;start_x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INITIAL_SNAKE_LENGTH&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RIGHT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Technical Decisions
&lt;/h3&gt;

&lt;p&gt;I ended up with some of the below defaults after a couple of iterations when it looked completely different than I thought.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Window Size&lt;/strong&gt;: I chose 800x600 pixels—a browser-friendly size that's large enough for comfortable gameplay but small enough to fit on any screen. This decision reflects modern web-first thinking while maintaining the classic feel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grid System&lt;/strong&gt;: The 20x20 pixel cell system provides the perfect balance between smooth movement and the chunky, retro aesthetic that makes Snake instantly recognizable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frame Rate&lt;/strong&gt;: At 10 FPS, the game maintains that classic Snake tempo—fast enough to be engaging, slow enough to be controllable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Game Features That Matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  User Experience Focus
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pause Functionality&lt;/strong&gt;: Press 'P' to pause/resume—essential for longer games&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Restart&lt;/strong&gt;: Space or Enter to quickly restart after game over&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual Clarity&lt;/strong&gt;: Distinct colors for snake head (bright green), body (darker green), and food (red)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Score Tracking&lt;/strong&gt;: Real-time score display with 10 points per food item&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Controls That Feel Right
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Arrow Keys: Snake movement
P: Pause/Resume
ESC: Quit game
SPACE/ENTER: Restart after game over
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Technical Highlights
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Efficient Collision Detection
&lt;/h3&gt;

&lt;p&gt;The game uses grid-based collision detection, checking if the snake's head position matches food coordinates or intersects with walls/body segments. This approach is both computationally efficient and perfectly suited to the grid-based movement system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snake Growth
&lt;/h3&gt;

&lt;p&gt;Rather than constantly resizing arrays, the snake growth system uses a "grow_pending" counter, adding segments only when needed and maintaining optimal performance even with very long snakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned: The Art of AI-Assisted Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Power of Detailed Prompting
&lt;/h3&gt;

&lt;p&gt;This project taught me that the quality of AI-assisted development is directly proportional to the quality of the initial prompt. The comprehensive structured prompt didn't just describe what I wanted—it provided:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context and Constraints&lt;/strong&gt;: Specifying the 800x600 browser-friendly window size and 20x20 pixel grid system gave clear boundaries for design decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success Metrics&lt;/strong&gt;: Defining specific criteria like "responsive arrow key controls" and "smooth gameplay experience" provided measurable goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation Philosophy&lt;/strong&gt;: Emphasizing object-oriented design and best practices ensured the code would be maintainable and extensible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Experience Focus&lt;/strong&gt;: Including details about pause functionality, visual feedback, and error handling elevated the project from functional to polished.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Iterative Refinement Process
&lt;/h3&gt;

&lt;p&gt;Working with Amazon Q Developer CLI revealed the importance of iterative refinement in AI-assisted development:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initial Prompt → Comprehensive Structured Prompt → Implementation → Refinement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each stage built upon the previous one, with the comprehensive structured prompt serving as the crucial bridge between high-level vision and detailed implementation. This process ensured that the final product wasn't just functional, but thoughtfully designed and professionally executed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplicity is Sophisticated
&lt;/h3&gt;

&lt;p&gt;Building Snake reinforced that great games don't need complex graphics or mechanics. The challenge lies in perfecting the fundamentals—responsive controls, fair difficulty progression, and satisfying feedback loops.&lt;/p&gt;

&lt;h3&gt;
  
  
  User Experience in Retro Games
&lt;/h3&gt;

&lt;p&gt;Even classic games benefit from modern UX thinking. Features like pause functionality, clear visual feedback, and intuitive restart mechanisms make the difference between a nostalgic novelty and a genuinely enjoyable experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;One of the most valuable aspects of the comprehensive structured prompt was its forward-thinking approach. It included optional enhancements that could extend the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sound effects for eating food and game over events&lt;/li&gt;
&lt;li&gt;Different difficulty levels with variable speed settings&lt;/li&gt;
&lt;li&gt;Power-ups or special food items with unique effects&lt;/li&gt;
&lt;li&gt;Snake skin patterns or visual themes&lt;/li&gt;
&lt;li&gt;Multiplayer support for competitive play&lt;/li&gt;
&lt;li&gt;Mobile-friendly touch controls adaptation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;"Python a Snake Game" is available with full source code, requirements file, and detailed setup instructions. Whether you're learning game development, exploring Python and Pygame, or just want to enjoy some nostalgic gaming, this Snake implementation offers a polished, accessible experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/quixoticmonk/python" rel="noopener noreferrer"&gt;https://github.com/quixoticmonk/python&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Quick start&lt;/span&gt;
git clone https://github.com/quixoticmonk/python.git
&lt;span class="nb"&gt;cd &lt;/span&gt;python/snake_q
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python snake_game.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion: The Future of AI-Assisted Game Development
&lt;/h2&gt;

&lt;p&gt;Participating in the AWS Build Games Challenge with Amazon Q Developer CLI has been an enlightening experience that goes far beyond building a simple game. It demonstrated that effective AI-assisted development is fundamentally about collaboration—between human creativity and AI capability, between high-level vision and detailed implementation, between classic game design and modern development practices.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Broader Impact
&lt;/h3&gt;

&lt;p&gt;This project represents more than just a nostalgic coding exercise. It showcases how AI-assisted development can make high-quality software creation more accessible while maintaining professional standards. The detailed structured prompt approach could be applied to any software project, from web applications to mobile apps to enterprise systems.&lt;/p&gt;

&lt;p&gt;"Python a Snake Game" may be a simple game, but building it well requires attention to detail. With Amazon Q Developer CLI as a development partner, the journey from concept to polished game becomes not just faster, but more enjoyable and educational.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This project was built as part of the AWS Build Games Challenge. The complete source code, comprehensive structured prompt, documentation, and setup instructions are available in the project repository: &lt;a href="https://github.com/quixoticmonk/python" rel="noopener noreferrer"&gt;https://github.com/quixoticmonk/python&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Game Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Language: Python 3.11+&lt;/li&gt;
&lt;li&gt;Framework: Pygame 2.0+&lt;/li&gt;
&lt;li&gt;Window Size: 800x600 pixels&lt;/li&gt;
&lt;li&gt;Grid System: 20x20 pixel cells&lt;/li&gt;
&lt;li&gt;Frame Rate: 10 FPS&lt;/li&gt;
&lt;li&gt;Features: Pause, restart, score tracking, collision detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready to play? The snake is waiting! 🐍&lt;/p&gt;

&lt;p&gt;*Want to see how detailed prompting can transform your development process? Check out the comprehensive structured prompt in the repository: &lt;a href="https://github.com/quixoticmonk/python" rel="noopener noreferrer"&gt;https://github.com/quixoticmonk/python&lt;/a&gt; .&lt;/p&gt;

</description>
      <category>amazonq</category>
      <category>buildgameschallenge</category>
      <category>amazonqdevcli</category>
      <category>promptdriven</category>
    </item>
    <item>
      <title>Simplifying Policy Management in HCP Terraform with the Policysets Module</title>
      <dc:creator>quixoticmonk</dc:creator>
      <pubDate>Sun, 01 Jun 2025 19:35:15 +0000</pubDate>
      <link>https://forem.com/quixoticmonk/simplifying-policy-management-in-hcp-terraform-with-the-policysets-module-4pg3</link>
      <guid>https://forem.com/quixoticmonk/simplifying-policy-management-in-hcp-terraform-with-the-policysets-module-4pg3</guid>
      <description>&lt;p&gt;As organizations scale their infrastructure with Terraform, policy management becomes increasingly important. Whether you're enforcing security standards, compliance requirements, or operational best practices, having a streamlined way to manage and deploy policies is essential.&lt;/p&gt;

&lt;p&gt;HashiCorp and AWS have worked on publishing some pre-built sentinel policies for AWS provider which you can find below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/policies/hashicorp/CIS-Policy-Set-for-AWS-Terraform" rel="noopener noreferrer"&gt;CIS benchmark&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/policies/hashicorp/FSBP-Policy-Set-for-AWS-Terraform/1.0.0" rel="noopener noreferrer"&gt;Foundation Security Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recently faced a challenge: I needed to import locally available Sentinel and OPA policies to HCP Terraform, as well as policies available in public GitHub repositories. After exploring various approaches, I decided to create a reusable Terraform module to solve this problem once and for all. As with most of these modules or utilities, I start off with something I have come across and post them out as public if I think others might benefit. I understand you can do this via a VCS connection with your repository ( requires a GitHub app connection) or a forked repository of a public policyset. And I do remember some sentinel based ones available across the HashiCorp repositories, but they didn't cover the OPA ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Policysets Module
&lt;/h2&gt;

&lt;p&gt;The result of this effort is a Terraform module that makes it easy to import and manage policy sets in HCP Terraform. The module is now available in the Terraform Registry at &lt;a href="https://registry.terraform.io/modules/quixoticmonk/policysets/tfe/latest" rel="noopener noreferrer"&gt;quixoticmonk/policysets&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple Policy Sources&lt;/strong&gt;: Import policies from local directories or public Git repositories&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework Support&lt;/strong&gt;: Works with both Sentinel and OPA policy frameworks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible Targeting&lt;/strong&gt;: Apply policies globally or to specific workspaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configurable Settings&lt;/strong&gt;: Customize policy evaluation settings to match your requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Use the Module
&lt;/h2&gt;

&lt;p&gt;Let's walk through some common scenarios for using this module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: Importing Local Sentinel Policies
&lt;/h3&gt;

&lt;p&gt;If you have Sentinel policies in a local directory that you want to apply to all workspaces in your organization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"sentinel_policy_set"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"quixoticmonk/policysets/tfe"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-sentinel-policies"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sentinel policies for cost control"&lt;/span&gt;
  &lt;span class="nx"&gt;organization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-organization"&lt;/span&gt;
  &lt;span class="nx"&gt;policy_kind&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sentinel"&lt;/span&gt;

  &lt;span class="nx"&gt;policy_source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local"&lt;/span&gt;
  &lt;span class="nx"&gt;local_policies_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/policies"&lt;/span&gt;

  &lt;span class="c1"&gt;# Apply globally to all workspaces&lt;/span&gt;
  &lt;span class="nx"&gt;global&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure your local policies directory has the proper structure, including a &lt;code&gt;sentinel.hcl&lt;/code&gt; file that defines the policy sets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Importing Local OPA Policies
&lt;/h3&gt;

&lt;p&gt;For OPA policies stored locally that you want to apply to specific workspaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"opa_policy_set"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"quixoticmonk/policysets/tfe"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-opa-policies"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OPA policies for compliance"&lt;/span&gt;
  &lt;span class="nx"&gt;organization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-organization"&lt;/span&gt;
  &lt;span class="nx"&gt;policy_kind&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"opa"&lt;/span&gt;

  &lt;span class="nx"&gt;policy_source&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local"&lt;/span&gt;
  &lt;span class="nx"&gt;local_policies_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/policies"&lt;/span&gt;

  &lt;span class="nx"&gt;workspace_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ws-v93RUQTMfJxoX18s"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your OPA policies directory should include a &lt;code&gt;policies.hcl&lt;/code&gt; file that defines the policy configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Importing Policies from a Public GitHub Repository
&lt;/h3&gt;

&lt;p&gt;If you want to use policies from a public GitHub repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"policy_set"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"quixoticmonk/policysets/tfe"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"latest"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cis-policies"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sentinel policies supporting CIS framework"&lt;/span&gt;
  &lt;span class="nx"&gt;organization&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-organization"&lt;/span&gt;
  &lt;span class="nx"&gt;policy_kind&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sentinel"&lt;/span&gt;
  &lt;span class="nx"&gt;policy_source&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"git"&lt;/span&gt;
  &lt;span class="nx"&gt;git_url&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/quixoticmonk/policy-library-CIS-Policy-Set-for-AWS-Terraform.git"&lt;/span&gt;
  &lt;span class="nx"&gt;git_branch&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt;
  &lt;span class="nx"&gt;git_policies_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./"&lt;/span&gt;

  &lt;span class="c1"&gt;# Apply globally to all workspaces&lt;/span&gt;
  &lt;span class="nx"&gt;global&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;When using the &lt;code&gt;git&lt;/code&gt; source option, the module will clone the repository to a temporary directory within the module. Make sure the Git command-line tool is available in your environment.&lt;/li&gt;
&lt;li&gt;For the &lt;code&gt;local&lt;/code&gt; source option, the specified directory must exist and contain valid policy files.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;global&lt;/code&gt; and &lt;code&gt;workspace_ids&lt;/code&gt; options are mutually exclusive - you can either apply policies globally or to specific workspaces. This forced me to have two different policysets based on the scope of the policies.&lt;/li&gt;
&lt;li&gt;When using &lt;code&gt;policy_kind = "opa"&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;agent_enabled&lt;/code&gt; parameter is always set to &lt;code&gt;true&lt;/code&gt;, regardless of the input value&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;overridable&lt;/code&gt; parameter is only valid for OPA policies and ignored for Sentinel policies&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;policy_tool_version&lt;/code&gt; was another input which had to be reviewed since the only common default which could be used was &lt;code&gt;latest&lt;/code&gt;. I preferred a single input than two different inputs for opa or sentinel and then enforcing rules on top of it.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Module Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Terraform &amp;gt;= 1.0.0&lt;/li&gt;
&lt;li&gt;TFE Provider &amp;gt;= 0.40.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;To get started with this module:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the module to your Terraform configuration as shown in the examples above&lt;/li&gt;
&lt;li&gt;Configure your HCP Terraform provider with appropriate credentials

&lt;ul&gt;
&lt;li&gt;TFE_TOKEN as an environment variable with permissions to create policysets&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;terraform init&lt;/code&gt; to download the module&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;terraform plan&lt;/code&gt; to see what changes will be made&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;terraform apply&lt;/code&gt; to create the policy set in your HCP Terraform organization&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;For the git based path, I want to consider if a curl or wget equivalent is better than expecting git being available in every machine this module is run on. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Managing policies at scale doesn't have to be complicated. With this module, you can easily import and manage policies from various sources, ensuring consistent governance across your Terraform deployments.&lt;/p&gt;

&lt;p&gt;The module is open source and available on the Terraform Registry. I welcome feedback, issues, and contributions to make it even better!&lt;/p&gt;

&lt;p&gt;Check out the full documentation and source code at &lt;a href="https://registry.terraform.io/modules/quixoticmonk/policysets/tfe/latest" rel="noopener noreferrer"&gt;quixoticmonk/policysets/tfe&lt;/a&gt; to get started.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>tfe</category>
      <category>sentinel</category>
      <category>opa</category>
    </item>
  </channel>
</rss>
