<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Aws-Nuke on PHP Boy Scout</title><link>https://blog-570662.gitlab.io/tags/aws-nuke/</link><description>Recent content in Aws-Nuke on PHP Boy Scout</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>Matt Cockayne</copyright><lastBuildDate>Tue, 05 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog-570662.gitlab.io/tags/aws-nuke/index.xml" rel="self" type="application/rss+xml"/><item><title>The cleanup tool that almost deleted its own hands</title><link>https://blog-570662.gitlab.io/the-cleanup-tool-that-almost-deleted-its-own-hands/</link><pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate><guid>https://blog-570662.gitlab.io/the-cleanup-tool-that-almost-deleted-its-own-hands/</guid><description>&lt;img src="https://blog-570662.gitlab.io/the-cleanup-tool-that-almost-deleted-its-own-hands/cover-the-cleanup-tool-that-almost-deleted-its-own-hands.png" alt="Featured image of post The cleanup tool that almost deleted its own hands" /&gt;&lt;p&gt;The first time I pointed aws-nuke at a real account, the dry-run printed hundreds of lines of angry red text and my stomach dropped. Then I read it properly, and two things turned out to be true at once. Almost all of that red was noise. And the one operation I genuinely should have worried about wasn&amp;rsquo;t red at all.&lt;/p&gt;
&lt;h2 id="a-tool-whose-whole-job-is-destruction"&gt;A tool whose whole job is destruction
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/ekristen/aws-nuke" target="_blank" rel="noopener"
 &gt;aws-nuke&lt;/a&gt; deletes everything in an AWS account. That&amp;rsquo;s the point of it: when you spin up a throwaway account to try something, aws-nuke is how you tear it back down to nothing afterwards rather than leaving resources quietly billing forever. go-tool-base&amp;rsquo;s bootstrap renders a scoped aws-nuke config for exactly this, from a &lt;code&gt;nuke-config&lt;/code&gt; module, so the teardown is described in code rather than typed by hand at the worst possible moment.&lt;/p&gt;
&lt;p&gt;A tool that deletes everything is a tool you run in dry-run first, every single time, and read the output before you let it touch anything. So that&amp;rsquo;s what I did. And the output was alarming in a way that turned out to be completely meaningless, and reassuring in a way that turned out to hide the one real hazard.&lt;/p&gt;
&lt;h2 id="the-wall-of-red-that-means-nothing"&gt;The wall of red that means nothing
&lt;/h2&gt;&lt;p&gt;A fresh account threw up screen after screen of &lt;code&gt;SubscriptionRequiredException&lt;/code&gt;. Hundreds of lines, all red, all looking like something had gone badly wrong.&lt;/p&gt;
&lt;p&gt;They hadn&amp;rsquo;t. aws-nuke works by asking every region &amp;ldquo;do you have any of &lt;em&gt;this&lt;/em&gt; kind of resource?&amp;rdquo;, for every kind of resource it knows about. On a brand-new account you&amp;rsquo;ve never enabled most services in most regions, so the API&amp;rsquo;s honest answer is &amp;ldquo;you&amp;rsquo;re not subscribed to that here&amp;rdquo;, which surfaces as an exception, which the tool dutifully logs in red. It isn&amp;rsquo;t a failure. It&amp;rsquo;s the sound of an empty account being asked four hundred questions and answering &amp;ldquo;nothing here&amp;rdquo; to almost all of them.&lt;/p&gt;
&lt;p&gt;The skill, and it is a skill, is learning to read a destructive tool&amp;rsquo;s dry-run and tell the noise from the signal. &lt;code&gt;SubscriptionRequiredException&lt;/code&gt; on a fresh account is noise. Once you know that, the wall of red stops being frightening and becomes scenery.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a related trap in the same neighbourhood, and the &lt;code&gt;nuke-config&lt;/code&gt; module&amp;rsquo;s own &lt;a class="link" href="https://gitlab.com/phpboyscout/terraform-aws-bootstrap/-/blob/v0.2.0/modules/nuke-config/variables.tf#L11" target="_blank" rel="noopener"
 &gt;&lt;code&gt;regions&lt;/code&gt; variable documents it&lt;/a&gt;. aws-nuke has a special &lt;code&gt;global&lt;/code&gt; pseudo-region for things that don&amp;rsquo;t live in one place (IAM, Route 53, CloudFront), and then the actual regions for everything else. It &lt;em&gt;also&lt;/em&gt; accepts &lt;code&gt;all&lt;/code&gt;, meaning every enabled region. Mixing &lt;code&gt;all&lt;/code&gt; with explicit region values scans some regions twice and muddies the output, so the module&amp;rsquo;s guidance is to pick one approach or the other. More scenery you have to learn to read before the genuinely important line will stand out.&lt;/p&gt;
&lt;h2 id="the-line-that-should-have-scared-me-and-didnt-look-like-it"&gt;The line that should have scared me, and didn&amp;rsquo;t look like it
&lt;/h2&gt;&lt;p&gt;Buried in that calm-looking eye of the storm, among the resources aws-nuke intended to delete, were the IAM resources granting the identity &lt;em&gt;running the nuke&lt;/em&gt; its administrative access.&lt;/p&gt;
&lt;p&gt;Sit with that for a second. aws-nuke runs as some principal with enough power to delete everything. To delete EVERYTHING, it has to delete IAM resources too. And if the plan deletes the very grant that gives the running identity its admin &lt;em&gt;before&lt;/em&gt; it&amp;rsquo;s finished, the tool strands itself partway through: no permissions left to complete the teardown, and now you&amp;rsquo;ve got a half-nuked account and a principal that can&amp;rsquo;t act on it. The cleanup tool sawing off the branch it&amp;rsquo;s standing on, calmly, without a single red line to warn you, because from the API&amp;rsquo;s point of view deleting that resource is a perfectly valid request.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the operation that actually mattered, and it was the quietest thing in the output.&lt;/p&gt;
&lt;h2 id="two-ways-to-keep-its-hands-attached"&gt;Two ways to keep its hands attached
&lt;/h2&gt;&lt;p&gt;The fix has two halves, one explicit and one structural.&lt;/p&gt;
&lt;p&gt;The explicit half is to &lt;em&gt;preserve the privilege path&lt;/em&gt;. The &lt;code&gt;nuke-config&lt;/code&gt; module passes a caller-supplied set of &lt;code&gt;filters&lt;/code&gt; straight through into the rendered config, so you tell aws-nuke &amp;ldquo;everything except these&amp;rdquo;. You exclude the identity running the nuke, and the policy and path that grant it admin, from deletion. The tool cleans the account and leaves its own hands alone, because you told it which resources are off-limits.&lt;/p&gt;
&lt;p&gt;The structural half is to not give it a tempting separate thing to delete in the first place. If an identity gets its admin through an IAM &lt;em&gt;group&lt;/em&gt; it belongs to, that group is its own deletable resource, one more thing in the plan, one more way to be stranded. The automation role in &lt;code&gt;terraform-aws-bootstrap&lt;/code&gt; instead takes its policies as &lt;a class="link" href="https://gitlab.com/phpboyscout/terraform-aws-bootstrap/-/blob/v0.2.0/modules/automation-iam/main.tf#L119" target="_blank" rel="noopener"
 &gt;direct attachments to the role itself&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;aws_iam_role_policy_attachment&amp;#34; &amp;#34;gitlab&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; for_each&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;is_gitlab&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;policy_arns&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; policy_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No intermediary group sitting there as a separate, deletable object. Flattening the privilege onto the role makes the dependency simpler to reason about and gives the cleanup tool one fewer foot-gun to find. Belt and braces: filter the path out explicitly, &lt;em&gt;and&lt;/em&gt; don&amp;rsquo;t build a structure that invites the problem.&lt;/p&gt;
&lt;h2 id="what-it-comes-down-to"&gt;What it comes down to
&lt;/h2&gt;&lt;p&gt;A destructive tool&amp;rsquo;s dry-run is the most valuable thing it produces, and reading it well is its own competence. On a fresh account, the screenfuls of red &lt;code&gt;SubscriptionRequiredException&lt;/code&gt; are noise, the sound of an empty account answering &amp;ldquo;nothing here&amp;rdquo;, and the &lt;code&gt;all&lt;/code&gt;-versus-&lt;code&gt;global&lt;/code&gt; region wrinkle is more of the same. Learn to see past all of it, because the operation that can actually hurt you is rarely the one shouting. Mine was the calm, unremarkable line proposing to delete the admin grant the nuke needed to finish its own job.&lt;/p&gt;
&lt;p&gt;Keep the cleanup tool&amp;rsquo;s hands attached: filter the privileged path out of the teardown so it&amp;rsquo;s never a candidate for deletion, and attach that privilege directly rather than through a group that&amp;rsquo;s just one more thing to delete. Then let it loose on everything else, which is, after all, what you brought it in to do.&lt;/p&gt;</description></item></channel></rss>