<?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>DEV Community: Nirmal Chhodvadiya</title>
    <description>The latest articles on DEV Community by Nirmal Chhodvadiya (@nirmalpatel008).</description>
    <link>https://dev.to/nirmalpatel008</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F258365%2F1d41c732-bf02-4080-a5b7-f6c8c5b13211.jpg</url>
      <title>DEV Community: Nirmal Chhodvadiya</title>
      <link>https://dev.to/nirmalpatel008</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nirmalpatel008"/>
    <language>en</language>
    <item>
      <title>Query before you mutate: how agents should touch your infrastructure</title>
      <dc:creator>Nirmal Chhodvadiya</dc:creator>
      <pubDate>Tue, 01 Sep 2026 09:53:58 +0000</pubDate>
      <link>https://dev.to/nirmalpatel008/query-before-you-mutate-how-agents-should-touch-your-infrastructure-1h25</link>
      <guid>https://dev.to/nirmalpatel008/query-before-you-mutate-how-agents-should-touch-your-infrastructure-1h25</guid>
      <description>&lt;h2&gt;
  
  
  The problem with plan, review, apply
&lt;/h2&gt;

&lt;p&gt;Infrastructure-as-code was designed around human-speed changes: write a plan, review it, apply it, and use state snapshots to know what was managed before. That works best when writers are few and changes are infrequent.&lt;/p&gt;

&lt;p&gt;Agents change those assumptions. They run continuously and multiple actors can touch the same resources at the same time. Drift is no longer exceptional. It is unavoidable and relentless.&lt;/p&gt;

&lt;p&gt;Canonical IaC tools like Terraform still matter because they represent intent. What's weakened is their ability to reflect current reality. Agents rarely own the whole picture. They usually operate in narrow scopes, working on a slice of infrastructure that other agents, other tools, or humans are also touching. In that world, an agent will regularly encounter infrastructure that was mutated out of band, outside whatever IaC system nominally manages it.&lt;/p&gt;

&lt;p&gt;Query-before-mutation handles this case. Read live cloud state, compare it with policy, apply a bounded policy gate, mutate only what is out of policy, then verify. Each run starts from reality rather than a cached view.&lt;/p&gt;

&lt;p&gt;The demo takes about ten minutes and uses Google Cloud Storage bucket encryption, but the pattern is provider-agnostic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query before mutation
&lt;/h2&gt;

&lt;p&gt;Correctness comes from querying live state before acting. Safety comes from something separate: policy gates that bound what a single run is allowed to do. Location locks, resource caps, budget ceilings, allowlists. Two patterns, two jobs. Agentic query-before-mutation complements centralised IaC approaches leveraging snapshots of state. Policy gates replace the review meeting.&lt;/p&gt;

&lt;p&gt;The combined loop is correct by default because it reads reality, and safe by default because it cannot do more than the gate allows. Agents can run it continuously. Humans can run it manually. The output converges either way.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stackql&lt;/code&gt; implements this pattern. Any resource on any supported cloud can be read with SQL and mutated with SQL where the provider exposes mutation methods. Kubernetes solved reconciliation inside the cluster, but nothing reconciles across your cloud. That gap is where this pattern belongs. The demo uses Google Cloud Storage because the mutation surface is clean, but the shape is the same for AWS S3, Vault secrets, GitHub org settings, or anything else an agent needs to reason about.&lt;/p&gt;

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

&lt;p&gt;You need three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker (to run &lt;code&gt;stackql&lt;/code&gt; without installing anything else)&lt;/li&gt;
&lt;li&gt;A Google Cloud project with credentials that can read buckets and update encryption config, plus an existing Cloud KMS key you want to enforce&lt;/li&gt;
&lt;li&gt;About ten minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clone the tutorial folder from the &lt;code&gt;stackql&lt;/code&gt; repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/stackql/stackql.git
&lt;span class="nb"&gt;cd &lt;/span&gt;stackql/docs/tutorials/query-before-mutation-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the sample environment file and add your service account credentials:&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;cp&lt;/span&gt; .env.sample .env
&lt;span class="c"&gt;# Then edit .env to set:&lt;/span&gt;
&lt;span class="c"&gt;# GOOGLE_CREDENTIALS=&amp;lt;service-account-json-on-one-line&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bring up an interactive &lt;code&gt;stackql&lt;/code&gt; shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; bash stackql
&lt;span class="c"&gt;# then inside the container:&lt;/span&gt;
stackql shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pull the Google provider from the registry. This only needs to happen once per shell session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;REGISTRY&lt;/span&gt; &lt;span class="n"&gt;PULL&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt; &lt;span class="n"&gt;v26&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00432&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query the live state
&lt;/h2&gt;

&lt;p&gt;Ask the GCS API which buckets exist and what encryption is currently applied to each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encryption&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buckets&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your-project-id'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output on a project with a mix of encryption configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| name                       | location | encryption                                    |
| demo-app-bucket1           | US       | null                                          |
| demo-app-bucket2           | US       | null                                          |
| stackql-demo-src-bucket    | US       | null                                          |
| stackql-encrypted-bucket-1 | US       | {"defaultKmsKeyName":"projects/.../keys/..."} |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three buckets show encryption as null, meaning Google-managed encryption keys (GMEK), the default. One already uses a customer-managed key. Under a policy that requires customer-managed keys, three buckets are out of policy.&lt;/p&gt;

&lt;p&gt;A state snapshot can tell you what Terraform previously configured. This query tells you what GCS reports now, regardless of who created or changed the bucket. If ten agents and two humans are all touching this project at various points during the day, the query still returns the current truth every time you run it. That's the property the pattern is built on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply a policy gate
&lt;/h2&gt;

&lt;p&gt;Three buckets don't match policy. Before mutating, the agent needs to prove it's allowed to. That's what the policy gate does.&lt;/p&gt;

&lt;p&gt;A gate is anything that bounds what a single run of the agent can do. Scope by project or location, cap the number of resources changed, or enforce an explicit allowlist. The shape depends on how much freedom the agent has and how much you're willing to lose in one bad run.&lt;/p&gt;

&lt;p&gt;For this demo, two gates cover most of the risk. The first is a location lock. The agent should only ever act on buckets in the location it was configured for. This is a WHERE clause on every query and mutation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'US'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trivial when the project's buckets are in one location. In production, agents move between locations based on task, and touching a bucket in the wrong region can mean acting on resources in an environment the agent has no business in. One clause, but it's the difference between a bounded action and an unbounded one.&lt;/p&gt;

&lt;p&gt;The second gate is a count cap. Before running a mutation across multiple buckets, ask the API how many the mutation would touch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buckets&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your-project-id'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'US'&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;encryption&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the query returns 20 buckets and the agent's policy expects to be operating in a project with 3 to 5 non-compliant buckets, something has changed. Maybe an account merger, maybe a script created buckets out of band, maybe the credentials point to the wrong project entirely. The agent shouldn't proceed. It should log the discrepancy and wait for a human to look.&lt;/p&gt;

&lt;p&gt;Gates like these aren't clever. They're routine, boring, and easy to skip. They're also the reason a query-before-mutation loop can run without a human review meeting attached to every apply. The gate carries the weight the meeting used to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutate to converge
&lt;/h2&gt;

&lt;p&gt;The gates passed. For each bucket without a customer-managed key, apply the policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buckets&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;data__encryption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{"defaultKmsKeyName":"projects/your-project-id/locations/us/keyRings/your-ring/cryptoKeys/your-key"}'&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'demo-app-bucket1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things worth noting. The &lt;code&gt;data__encryption&lt;/code&gt; prefix is how &lt;code&gt;stackql&lt;/code&gt; passes fields into the request body, distinguishing them from URL parameters. The &lt;code&gt;WHERE&lt;/code&gt; clause uses &lt;code&gt;bucket&lt;/code&gt; rather than &lt;code&gt;name&lt;/code&gt;, because &lt;code&gt;bucket&lt;/code&gt; is the identifier the underlying patch method requires.&lt;/p&gt;

&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The operation was despatched successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In an agent loop, this runs as a bounded iteration over the non-compliant buckets returned by the earlier query, applying the same UPDATE to each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify convergence
&lt;/h2&gt;

&lt;p&gt;A successful mutation is not enough. Query the bucket again to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encryption&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buckets&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'demo-app-bucket1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| name             | encryption                                                                    |
| demo-app-bucket1 | {"defaultKmsKeyName":"projects/your-project-id/locations/us/keyRings/..."}    |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bucket converged from GMEK to the required customer-managed key.&lt;/p&gt;

&lt;p&gt;Run the loop again and something interesting happens: the mutation is a no-op. The SELECT finds no drift, the count check passes trivially, no UPDATE fires. That's idempotence by design. The agent can run every minute, every hour, every day, and it does the same right thing whether the target is already compliant or has just drifted. No state snapshot needed to remember what it did last time. Every run starts from live reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same pattern from an agent
&lt;/h2&gt;

&lt;p&gt;Everything above is you typing SQL into a shell. The same pattern maps directly to an agent through &lt;code&gt;stackql&lt;/code&gt;'s MCP server, listed in the Claude directory at &lt;a href="https://claude.ai/directory/ant.dir.gh.stackql.stackql" rel="noopener noreferrer"&gt;claude.ai/directory/ant.dir.gh.stackql.stackql&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install it, add your GCP credentials, and ask Claude in plain English:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;List all GCS buckets in the project stackql-demo, and for each 
show whether encryption is a customer-managed key or the 
Google-managed default.
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffrdt153155w6m794anoz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffrdt153155w6m794anoz.png" alt="Claude Desktop response showing stackql MCP query result with bucket encryption table" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claude uses the &lt;code&gt;stackql&lt;/code&gt; MCP server to run the same underlying SELECT you saw earlier, then formats the result into a readable table. The query, gate, mutate, and verify loop runs the same way an agent runs it, just as reasoning steps in a conversation instead of shell commands.&lt;/p&gt;

&lt;p&gt;Notice what the agent adds that the raw shell output doesn't: it separates CMEK buckets from Google-managed default ones, calls out which KMS key each CMEK bucket uses, and flags a caveat you didn't ask about (the difference between bucket-default encryption and per-object encryption). That's the value of running the pattern through an agent rather than a script. Same query, same result, plus the reasoning layer on top.&lt;/p&gt;

&lt;p&gt;The shell walkthrough above was for you to see the pattern happen line by line. The MCP integration is how the pattern runs end to end in an actual agent's workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this goes next
&lt;/h2&gt;

&lt;p&gt;Query before mutation is one instance of a broader pattern. Six patterns, really, that hold together as a working model for how agents should operate on infrastructure. Query before mutation is one. Idempotent assertions, bi-temporal views, agents as event sources, universal interfaces, and policy gates are the other five. Together they replace the assumptions the plan-review-apply model was built on.&lt;/p&gt;

&lt;p&gt;The full argument is in Jeff Aven's talk from AI Engineer Melbourne, "Treating Infrastructure as Data: Building an AI-Native Control Plane." If the pattern in this tutorial made sense to you, the talk is the map for what else fits with it.&lt;/p&gt;

&lt;p&gt;If this tutorial was useful, star the &lt;a href="https://github.com/stackql/stackql" rel="noopener noreferrer"&gt;stackql repo&lt;/a&gt;. Every star helps other developers and agents find the pattern, and helps the project climb the MCP registry where more agents can discover it.&lt;/p&gt;

&lt;p&gt;Next in this series: idempotent assertions with &lt;code&gt;stackql-deploy&lt;/code&gt;. Same shape, different pattern, applied to declarative infrastructure that runs continuously without breaking on repeat.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>ai</category>
      <category>mcp</category>
      <category>gcp</category>
    </item>
    <item>
      <title>Auditing three clouds without writing three scripts</title>
      <dc:creator>Nirmal Chhodvadiya</dc:creator>
      <pubDate>Tue, 18 Aug 2026 12:49:44 +0000</pubDate>
      <link>https://dev.to/nirmalpatel008/auditing-three-clouds-without-writing-three-scripts-5fhc</link>
      <guid>https://dev.to/nirmalpatel008/auditing-three-clouds-without-writing-three-scripts-5fhc</guid>
      <description>&lt;p&gt;If you've audited buckets in more than one cloud, you already know what this is like. Three consoles. Three CLIs. Three auth patterns. And a bit of glue code to pull the outputs together every time someone asks a question that spans all three.&lt;/p&gt;

&lt;p&gt;The question is usually simple. "Which buckets are public across all our clouds?" You end up running three separate scripts, then reconciling three different output shapes just to give one answer.&lt;/p&gt;

&lt;p&gt;This tutorial doesn't make that faster. It replaces it. One query, one view, all three clouds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What StackQL is, in three sentences
&lt;/h2&gt;

&lt;p&gt;StackQL is SQL for cloud APIs. You write a normal SQL query, and it makes the API calls to AWS, GCP, Azure, or dozens of other providers to get the answer back as rows and columns.&lt;/p&gt;

&lt;p&gt;The important part for this tutorial: it queries the live provider APIs on every run. There's no local database, no cache, no sync job to schedule. What you see is the current state of your cloud, right now.&lt;/p&gt;

&lt;p&gt;It exists because someone got tired of writing the same audit logic three times.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll need
&lt;/h2&gt;

&lt;p&gt;You'll need Docker installed and read-only credentials for whichever clouds you want to audit. All three are optional. If you leave a cloud's credentials blank, it gets skipped.&lt;/p&gt;

&lt;p&gt;For AWS, GCP, and Azure, the audit needs a service account or role that can list storage buckets and read their configuration. The audit-action repo has the exact permissions per provider &lt;a href="https://github.com/stackql/stackql-audit-action/blob/main/docs/required-auth.md" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the audit
&lt;/h2&gt;

&lt;p&gt;The tutorial folder has three files: a docker-compose file, an &lt;code&gt;.env.audit.example&lt;/code&gt; template, and a README. Grab the folder from &lt;a href="https://github.com/stackql/stackql/tree/main/docs/tutorials/preview-bucket-01" rel="noopener noreferrer"&gt;&lt;code&gt;docs/tutorials/preview-bucket-01/&lt;/code&gt;&lt;/a&gt; in the &lt;code&gt;stackql/stackql&lt;/code&gt; repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Copy the env template&lt;/strong&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="nb"&gt;cp&lt;/span&gt; ./audit/.env.audit.example ./audit/.env.audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;.env.audit&lt;/code&gt; and fill in credentials for whichever clouds you want. The variables are named for what they are: &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;, &lt;code&gt;AWS_REGION&lt;/code&gt;, &lt;code&gt;AZURE_TENANT_ID&lt;/code&gt;, &lt;code&gt;AZURE_CLIENT_ID&lt;/code&gt;, &lt;code&gt;AZURE_CLIENT_SECRET&lt;/code&gt;, &lt;code&gt;GOOGLE_CREDENTIALS&lt;/code&gt; (the service account JSON, on one line), and &lt;code&gt;GOOGLE_ORG_ID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you're only auditing one or two clouds, leave the others blank.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Pull the StackQL Docker image&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.bucket.audit.yaml pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tutorial pins to &lt;code&gt;stackql/stackql:v0.10.601&lt;/code&gt;. The audit uses views under &lt;code&gt;stackql_preview.*&lt;/code&gt; which are still evolving, so pinning matters for reproducibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Run the audit&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.bucket.audit.yaml run &lt;span class="nt"&gt;--rm&lt;/span&gt; stackql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a modest account this takes about 30 seconds. Output goes straight to your terminal as a table.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the output tells you
&lt;/h2&gt;

&lt;p&gt;When the audit finishes, you get a single table printed to your terminal. Something like this (bucket names are placeholders):&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpchvx5vwuvhftu3n9bib.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpchvx5vwuvhftu3n9bib.png" alt="Terminal screenshot showing StackQL audit output as a table with columns for provider, bucket name, encryption class, public flag, and HTTPS enforcement. Rows include AWS, GCP, and Azure buckets with a mix of provider-managed and customer-managed encryption, and one public bucket flagged per AWS and GCP." width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example output from the audit. Bucket configuration across three cloud providers in one normalised table.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The point isn't the specific findings. It's the shape.&lt;/p&gt;

&lt;p&gt;Three different resource types (S3 buckets, GCS buckets, and Azure Storage Accounts) normalised into one table with the same columns. Encryption class, public flag, HTTPS enforcement. Side by side, for every cloud you provided credentials for.&lt;/p&gt;

&lt;p&gt;A few patterns to notice in the table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public buckets across providers.&lt;/strong&gt; Filter by &lt;code&gt;public = true&lt;/code&gt; and you have the answer to your security team's question, in every cloud, in one place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption side by side.&lt;/strong&gt; The &lt;code&gt;encryption_class&lt;/code&gt; column shows which buckets use provider-managed keys versus customer-managed. Different providers have different naming conventions internally; StackQL normalises them to the same two categories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS enforcement in one column.&lt;/strong&gt; Whether it's S3 bucket policies, GCS uniform access, or Azure secure transfer, same column, same values, no translation needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what SQL for cloud APIs actually delivers. Not just SQL as a query language, but a uniform data model across providers, so you can ask one question and get one answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance and scope
&lt;/h2&gt;

&lt;p&gt;StackQL runs each provider's query in parallel using the credentials you provided. There's no local database being populated and no background sync; every run hits the live APIs and returns what's there right now.&lt;/p&gt;

&lt;p&gt;On the test account this took about 30 seconds. The query is scoped to one AWS region and one GCP organization, which keeps the API footprint bounded and reflects the current preview default. Streaming output and broader audits (across regions, deeper across resource types) are in flight from the StackQL team.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this covers today
&lt;/h2&gt;

&lt;p&gt;Today, this audit covers storage buckets across AWS, GCP, and Azure. One region at a time for AWS, one organization at a time for GCP. Entitlements, IAM, and other resource types are on the roadmap as separate audits under &lt;code&gt;stackql_preview.*&lt;/code&gt; and will get their own tutorials as they land.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/stackql/stackql" rel="noopener noreferrer"&gt;&lt;code&gt;github.com/stackql/stackql&lt;/code&gt;&lt;/a&gt; is the flagship repo. If this tutorial was worth reading, give it a star. It's genuinely the thing that tells the team to build more tutorials like this one, and it helps other engineers with the same three-console problem find the project.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudsecurity</category>
      <category>stackql</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
