<?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: ICHINO Kazuaki</title>
    <description>The latest articles on DEV Community by ICHINO Kazuaki (@kazzpapa3).</description>
    <link>https://dev.to/kazzpapa3</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%2F1370144%2Fbf3bb771-370a-49dc-af32-1ba808441ab4.jpg</url>
      <title>DEV Community: ICHINO Kazuaki</title>
      <link>https://dev.to/kazzpapa3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kazzpapa3"/>
    <language>en</language>
    <item>
      <title>Idea for Managing Policies Across Nearly 1,000 AWS Organizations — A Reseller's Perspective</title>
      <dc:creator>ICHINO Kazuaki</dc:creator>
      <pubDate>Wed, 29 Apr 2026 03:09:19 +0000</pubDate>
      <link>https://dev.to/aws-builders/idea-for-managing-policies-across-nearly-1000-aws-organizations-a-resellers-perspective-2o22</link>
      <guid>https://dev.to/aws-builders/idea-for-managing-policies-across-nearly-1000-aws-organizations-a-resellers-perspective-2o22</guid>
      <description>&lt;p&gt;"A Note from the Author"&lt;br&gt;&lt;br&gt;
I work in the Technical Support division of an AWS reseller operating under the AWS Solution Provider Program in Japan. This post is written from that perspective — managing hundreds of AWS Organizations on behalf of our customers and wrestling with the policy sprawl that comes with it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Japan's IT industry has traditionally relied heavily on outsourcing to external vendors for building and managing cloud infrastructure, rather than developing in-house capabilities. Decision-making can be slow when multiple organizations are involved, and the AWS reseller model itself is a product of this outsourcing culture. Many Japanese companies purchase AWS through resellers rather than directly, which means resellers like us end up provisioning and managing a large number of AWS accounts and Organizations on their behalf.&lt;/li&gt;
&lt;li&gt;As a reseller, we face a unique challenge: we need to enforce restrictions across all of these Organizations to protect both ourselves and our customers, but the exact restrictions vary depending on each customer's configuration. This leads to a proliferation of policy variants that are mostly the same but differ in small, critical ways — and keeping them all in sync is a real headache.&lt;/li&gt;
&lt;li&gt;This article is based on &lt;a href="https://speakerdeck.com/kazzpapa3/1000-nimojie-ku-aws-organizations-zu-zhi-noporisiyun-yong-wotiyantositai-toiuhua" rel="noopener noreferrer"&gt;a presentation&lt;/a&gt; I gave at &lt;a href="https://s-jaws.doorkeeper.jp/events/180401" rel="noopener noreferrer"&gt;Security-JAWS #40&lt;/a&gt;, a community event focused on AWS security in Japan, held on February 12, 2026.&lt;/li&gt;
&lt;li&gt;My English writing skills are limited, so I used GenAI (Kiro CLI) to help translate this article from the original Japanese. I hope it reads well — any awkwardness is on me, not the AI.&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;Hello, everyone. I'm Ichino (&lt;a href="https://x.com/kazzpapa3" rel="noopener noreferrer"&gt;@kazzpapa3&lt;/a&gt;), a Technical Support Engineer at an AWS partner company. My favorite AWS services are the AWS CLI and AWS CloudTrail. My least favorite (as a support engineer) is AWS Billing — the billing logic is just too convoluted.&lt;/p&gt;

&lt;p&gt;Today I want to talk about a challenge we're facing: &lt;strong&gt;how to properly manage IAM and SCP policies across nearly 1,000 AWS Organizations&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I introduced myself as a Technical Support Engineer, but I also work in the department that manages the specifications for our resold AWS accounts.&lt;/p&gt;

&lt;p&gt;Here's the situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have multiple account provisioning configurations, and there are many small differences between them.&lt;/li&gt;
&lt;li&gt;Most of the policy content is shared, but those small differences cause policies to proliferate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When the shared parts need updating, we have to make the same change in multiple places.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the problem I want to address.&lt;/p&gt;

&lt;p&gt;Oh, and personally — &lt;strong&gt;I think JSON came too early for humanity.&lt;/strong&gt; More on that later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the Differences Exist
&lt;/h2&gt;

&lt;p&gt;Many people assume that reseller-provided AWS accounts can't use AWS Organizations or AWS Control Tower. That's actually not the case with our company — we do allow customers to use both.&lt;/p&gt;

&lt;p&gt;However, as a reseller, there are certain operations we can't permit. We make the management account available to customers but restrict what they can do with it. The exact restrictions depend on the state of each Organization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Support case access&lt;/strong&gt;: Some Organizations deny all &lt;code&gt;support:*&lt;/code&gt; actions (customers must go through the reseller for support), while others allow customers to file support cases directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root user management&lt;/strong&gt;: In some Organizations, the reseller manages root user credentials for member accounts; in others, the customer manages them. This changes the IAM conditions in the policy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organization-level operations&lt;/strong&gt;: Leaving the Organization is always denied, but other operations may vary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These differences exist in both IAM policies and SCPs.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Scale of the Problem
&lt;/h2&gt;

&lt;p&gt;Nearly 1,000 management accounts doesn't mean 1,000 unique policies — but it does mean several distinct patterns have emerged. When a fundamental AWS update requires a policy change (think re:Invent season with its flood of announcements), we need to update the relevant section across every variant. That review process is painful.&lt;/p&gt;

&lt;p&gt;Visually, think of each policy as a series of blocks — one per &lt;code&gt;Sid&lt;/code&gt; or &lt;code&gt;Condition&lt;/code&gt;. Most blocks are shared (shown in blue), but a few differ (shown in other colors). The challenge is keeping the blue blocks in sync across all variants.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Building-Block Idea
&lt;/h2&gt;

&lt;p&gt;Since this is AWS, why not think in terms of building blocks? What if we could break policies into modular parts and compose them as needed — like LEGO bricks?&lt;/p&gt;

&lt;p&gt;Instead of maintaining each policy as a standalone document, we'd maintain individual components at the smallest reasonable granularity and assemble them at build time.&lt;/p&gt;

&lt;p&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%2F16wa0fq0et9mh1v1en3j.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%2F16wa0fq0et9mh1v1en3j.png" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

A conceptual view of the building block components.&lt;br&gt;Blue blocks represent shared elements, while red and yellow blocks represent the parts that differ between the two policies.



&lt;p&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%2Fm7resrrs68ku8ed1rymu.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%2Fm7resrrs68ku8ed1rymu.png" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

An illustration of how the smaller, modularized blocks are assembled to form the diagram above.&lt;br&gt;Some elements use different parts, and we also envision embedding different parts into shared components to build larger, coarser-grained modules.


&lt;h2&gt;
  
  
  Should We Use IaC?
&lt;/h2&gt;

&lt;p&gt;We considered AWS CDK. You could model &lt;code&gt;PolicyStatement&lt;/code&gt; objects as reusable classes or functions, manage shared parts in JSON, and add variant-specific pieces in code.&lt;/p&gt;

&lt;p&gt;But there were concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploying to ~1,000 accounts means creating ~1,000 stacks and running them all. That's a lot of CloudFormation overhead.&lt;/li&gt;
&lt;li&gt;The learning curve for CDK felt disproportionate to the problem we're solving.&lt;/li&gt;
&lt;li&gt;We wanted to explore whether a simpler, non-IaC approach could work first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be clear: &lt;strong&gt;we're not against IaC.&lt;/strong&gt; We're just exploring alternatives that might better fit our deployment model and team skills.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Approach: YAML Modules + &lt;code&gt;yq&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;With some help from generative AI for brainstorming, we arrived at a lightweight approach: write policy components in YAML and use the &lt;a href="https://github.com/mikefarah/yq" rel="noopener noreferrer"&gt;&lt;code&gt;yq&lt;/code&gt;&lt;/a&gt; command-line tool to assemble them.&lt;/p&gt;
&lt;h3&gt;
  
  
  Writing Policies in YAML
&lt;/h3&gt;

&lt;p&gt;Each component is a separate YAML file. For example, even the &lt;code&gt;Version&lt;/code&gt; declaration gets its own file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VersionDeclaration.yaml&lt;/strong&gt;&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;# Extracted just the IAM JSON policy Version element.&lt;/span&gt;
&lt;span class="c1"&gt;# Needs review if the Version specification is ever revised.&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html&lt;/span&gt;
&lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17"&lt;/span&gt;
&lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a module that denies &lt;code&gt;support:*&lt;/code&gt; (used when customers must go through the reseller for support):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BasicRestrictionAsResaler.yaml&lt;/strong&gt;&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BasicRestrictionAsResaler&lt;/span&gt;
    &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deny&lt;/span&gt;
    &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;support:*&lt;/span&gt;
      &lt;span class="c1"&gt;# Summary: Deny all AWS Support actions&lt;/span&gt;
      &lt;span class="c1"&gt;# Relaxable?: No — customers must not file support cases directly&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;supportplans:*&lt;/span&gt;
      &lt;span class="c1"&gt;# Summary: Deny all support plan modifications&lt;/span&gt;
      &lt;span class="c1"&gt;# Relaxable?: No — support plan must not be changed&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tax:*&lt;/span&gt;
      &lt;span class="c1"&gt;# Summary: Deny all tax setting modifications&lt;/span&gt;
      &lt;span class="c1"&gt;# Relaxable?: No — tax settings must remain configured for Japan&lt;/span&gt;

    &lt;span class="c1"&gt;# Deny on all resources&lt;/span&gt;
    &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the variant that &lt;em&gt;allows&lt;/em&gt; &lt;code&gt;support:*&lt;/code&gt; (for customers who are permitted to file cases directly):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BasicRestrictionAsResalerForResold.yaml&lt;/strong&gt;&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BasicRestrictionAsResalerForResold&lt;/span&gt;
    &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deny&lt;/span&gt;
    &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;supportplans:*&lt;/span&gt;
      &lt;span class="c1"&gt;# Summary: Deny all support plan modifications&lt;/span&gt;
      &lt;span class="c1"&gt;# Relaxable?: No — support plan must not be changed&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tax:*&lt;/span&gt;
      &lt;span class="c1"&gt;# Summary: Deny all tax setting modifications&lt;/span&gt;
      &lt;span class="c1"&gt;# Relaxable?: No — tax settings must remain configured for Japan&lt;/span&gt;

    &lt;span class="c1"&gt;# Deny on all resources&lt;/span&gt;
    &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the only difference: the first variant includes &lt;code&gt;support:*&lt;/code&gt; in the deny list; the second omits it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assembling with &lt;code&gt;yq&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To build a policy that denies support access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yq eval-all &lt;span class="s1"&gt;'
  select(fileIndex == 0) |
  .Statement = [
    (load("IAMPolicyRestrictionForCustomer.yaml") | .[0]),
    (load("BasicRestrictionAsResaler.yaml") | .[0]),        # This line differs
    (load("RestrictionToAWSOrganizationsAsResaler.yaml") | .[0])
  ]
'&lt;/span&gt; VersionDeclaration.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; foo.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To build a policy that allows support access, just swap one module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yq eval-all &lt;span class="s1"&gt;'
  select(fileIndex == 0) |
  .Statement = [
    (load("IAMPolicyRestrictionForCustomer.yaml") | .[0]),
    (load("BasicRestrictionAsResalerForResold.yaml") | .[0]),  # This line differs
    (load("RestrictionToAWSOrganizationsAsResaler.yaml") | .[0])
  ]
'&lt;/span&gt; VersionDeclaration.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bar.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what &lt;code&gt;yq&lt;/code&gt; is doing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;eval-all&lt;/code&gt; — operate across multiple files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;select(fileIndex == 0)&lt;/code&gt; — use the first file (&lt;code&gt;VersionDeclaration.yaml&lt;/code&gt;) as the base.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.Statement = [...]&lt;/code&gt; — populate the &lt;code&gt;Statement&lt;/code&gt; array by loading each module file and extracting its first element.&lt;/li&gt;
&lt;li&gt;Output the assembled YAML.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Converting to JSON
&lt;/h3&gt;

&lt;p&gt;Once assembled, convert to JSON for use as an actual IAM policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yq &lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json foo.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; foo.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a standard IAM policy document:&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BasicRestrictionAsResaler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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;"support:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"supportplans:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"tax:*"&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;"Resource"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Other statements omitted for brevity.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Validating the Output
&lt;/h3&gt;

&lt;p&gt;We can validate the generated policy using IAM Access Analyzer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws accessanalyzer validate-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-type&lt;/span&gt; IDENTITY_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://foo.json
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"findings"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;findings&lt;/code&gt; comes back empty, the policy document is syntactically valid. Note that this doesn't verify whether the policy &lt;em&gt;does what you intend&lt;/em&gt; — that's a separate review.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;p&gt;The modular approach feels promising. Here's what stood out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single source of truth for shared components.&lt;/strong&gt; When a shared module needs updating, you change it once. Every policy that includes it picks up the change at build time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YAML enables inline documentation.&lt;/strong&gt; This was an unexpected but significant benefit. JSON doesn't support comments, so our existing policy documents had no way to explain &lt;em&gt;why&lt;/em&gt; a particular action was denied. With YAML, we can annotate each action with its rationale — &lt;strong&gt;"Know Why" documentation becomes a natural part of the policy source.&lt;/strong&gt; (Further proof that JSON came too early for humanity.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low barrier to entry.&lt;/strong&gt; &lt;code&gt;yq&lt;/code&gt; is a single binary with a straightforward syntax. No SDK, no runtime, no framework to learn.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  JSONC as an alternative
&lt;/h3&gt;

&lt;p&gt;After presenting an earlier version of this talk at JAWS-UG DE&amp;amp;I, someone pointed out that &lt;a href="https://jsonc.org/" rel="noopener noreferrer"&gt;JSONC (JSON with Comments)&lt;/a&gt; exists as a specification. That's a fair point — but YAML still wins for us because of the modular composition workflow with &lt;code&gt;yq&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Remaining Challenge: Deployment
&lt;/h2&gt;

&lt;p&gt;The policy &lt;em&gt;composition&lt;/em&gt; problem feels solved, but &lt;strong&gt;deployment&lt;/strong&gt; is still an open question.&lt;/p&gt;

&lt;p&gt;Currently, we use bulk-update scripts that overwrite policies across all accounts. This works because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The accounts are under our management.&lt;/li&gt;
&lt;li&gt;Policy names and role names are stable and guaranteed to exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're considering whether CloudFormation StackSets might be a better deployment mechanism, but there's a migration challenge: the existing IAM resources weren't deployed via CloudFormation, so importing them into StackSets without conflicts requires careful planning.&lt;/p&gt;

&lt;p&gt;The policy assembly step (YAML modules + &lt;code&gt;yq&lt;/code&gt;) is independent of the deployment mechanism. Whether we stick with scripts, move to StackSets, or even use CDK for deployment, the modular source-of-truth approach works either way.&lt;/p&gt;

&lt;p&gt;A likely next step is integrating this into a &lt;strong&gt;GitHub Actions pipeline&lt;/strong&gt;: commit a module change → CI assembles all affected policies → validates them with Access Analyzer → produces deployment-ready JSON artifacts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;We chose not to use IaC for this particular problem, but that's not a rejection of IaC in general. Our reasoning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The resources are under our direct control, with stable naming conventions.&lt;/li&gt;
&lt;li&gt;A "nonexistent" state is essentially impossible in our environment.&lt;/li&gt;
&lt;li&gt;A simple bulk-update script is more predictable and faster than rolling out ~1,000 CloudFormation stacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, &lt;strong&gt;I'd love to hear if you have a better approach.&lt;/strong&gt; This is very much a work in progress, and I'm open to suggestions.&lt;/p&gt;

&lt;p&gt;Security is job zero.&lt;/p&gt;

&lt;p&gt;Thank you for reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mikefarah/yq" rel="noopener noreferrer"&gt;yq — GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mikefarah.gitbook.io/yq" rel="noopener noreferrer"&gt;yq — Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/accessanalyzer/validate-policy.html" rel="noopener noreferrer"&gt;validate-policy — AWS CLI Command Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-policy-validation.html" rel="noopener noreferrer"&gt;Validate policies with IAM Access Analyzer — AWS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.co.jp/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jsonc.org/" rel="noopener noreferrer"&gt;JSONC Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>automation</category>
      <category>aws</category>
      <category>cloud</category>
      <category>management</category>
    </item>
    <item>
      <title>Rethinking What You Need to Do When Your Access Keys Are Compromised</title>
      <dc:creator>ICHINO Kazuaki</dc:creator>
      <pubDate>Wed, 29 Apr 2026 01:15:39 +0000</pubDate>
      <link>https://dev.to/aws-builders/rethinking-what-you-need-to-do-when-your-access-keys-are-compromised-od7</link>
      <guid>https://dev.to/aws-builders/rethinking-what-you-need-to-do-when-your-access-keys-are-compromised-od7</guid>
      <description>&lt;p&gt;"A Note from the Author"&lt;br&gt;&lt;br&gt;
I work in the Technical Support division of an AWS reseller operating under the AWS Solution Provider Program in Japan. This post is written from that perspective — sitting between our customers and AWS, handling incidents day-to-day.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Japan's IT industry has traditionally relied heavily on outsourcing to external vendors for building and managing cloud infrastructure, rather than developing in-house capabilities. This means that when a security incident like an access key leak occurs, the response often involves coordination across multiple organizations, which can slow down decision-making at a critical moment. Some of the observations in this post reflect that reality.&lt;/li&gt;
&lt;li&gt;Also, my English writing skills are limited, so I used GenAI (Kiro CLI) to help translate this article from the original Japanese. I hope it reads well — any awkwardness is on me, not the AI.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Hello, everyone.&lt;/p&gt;

&lt;p&gt;I'm Ichino from the Technical Support team, and I'm a big fan of the AWS CLI.&lt;/p&gt;

&lt;p&gt;Unintended access key leaks have been around for a long time, and they show no signs of stopping — whether it's accidental commits to public repositories on GitHub or exposure through server-side vulnerabilities.&lt;/p&gt;

&lt;p&gt;Today, I want to walk through what you need to do when an access key leak happens and what kind of response is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Typical Notification
&lt;/h2&gt;

&lt;p&gt;When unauthorized use of an access key is suspected, AWS Support notifies the account holder by opening a support case with a message like the one below.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Note that AWS does not publicly disclose exactly what behaviors trigger detection (that's internal information), nor can we give a definitive answer on how quickly notifications are sent.&lt;/p&gt;

&lt;p&gt;That said, as a colleague of mine has written, my personal impression is that detection tends to be fast when &lt;code&gt;sts:GetCallerIdentity&lt;/code&gt; — commonly used as the first move in an attack — is called from an unusual location.&lt;br&gt;
(This is just my gut feeling and not backed by any official data.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.serverworks.co.jp/why-getcalleridentity-is-initial-access" rel="noopener noreferrer"&gt;Japanese article / 攻撃の初動としても利用される GetCallerIdentity とは何か&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Subject Line
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;[CASE 123456789012345] [Action Required] Unexpected Activity Detected on your AWS Account 123456789012&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Body
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;We detected potentially unexpected activity in your AWS account. This activity is related to your AWS access key(s) belonging to User(s) on the account. Please review the detailed list of access key(s) and User(s) in the existing Support Case within your AWS Console.&lt;/p&gt;

&lt;p&gt;Refer to the user guide [1] for detailed instructions.&lt;/p&gt;

&lt;p&gt;As a security best practice, we recommend that you enable multi-factor authentication (MFA) [2].&lt;/p&gt;

&lt;p&gt;Step 1: If your application uses the exposed access key, you need to replace the key. To replace the key, first create a second key (at that point, both keys will be active). Then, modify your application to use the new key.&lt;/p&gt;

&lt;p&gt;Next, disable (do not delete) the exposed key by clicking on the "Make inactive" option in the console. If there are any problems with your application, you can reactivate the exposed key. When your application is fully functional using the new key, please delete the exposed access key(s).&lt;/p&gt;

&lt;p&gt;To delete IAM user keys, go to [3].&lt;br&gt;
To delete Root user keys, go to [4].&lt;/p&gt;

&lt;p&gt;Please note, only rotating and deleting the exposed key may not be sufficient to protect your account, continue to Step 2.&lt;/p&gt;

&lt;p&gt;Step 2: Check your CloudTrail log for unwanted activity.&lt;/p&gt;

&lt;p&gt;Check your account for any unwanted activity, such as creation of unapproved IAM users and/or associated passwords (login profile), access keys, policies, roles or temporary security credentials by checking your CloudTrail log, and immediately delete them.&lt;/p&gt;

&lt;p&gt;To delete IAM users, go to [5].&lt;br&gt;
To delete policies, go to [6].&lt;br&gt;
To delete roles, go to [7].&lt;/p&gt;

&lt;p&gt;Please note, deleting IAM users may impact production workloads and should be done carefully.&lt;/p&gt;

&lt;p&gt;Step 3: Review your AWS account for any unwanted usage.&lt;br&gt;
Check your account for any unwanted usage, such as EC2 instances, Lambda functions, or EC2 Spot bids by logging into your AWS Management Console and reviewing each service page. You can also do this by checking the "Bills" page in the Billing console [8].&lt;/p&gt;

&lt;p&gt;Please note, unwanted usage can occur in any region and your console only displays one region at a time. To switch regions, use the drop-down menu in the top-right corner of the console.&lt;/p&gt;

&lt;p&gt;Step 4: Please work with your TAM/Account Manager and respond to the existing Support Case or create a new one [9] to confirm completion of steps 1-3 and apply for a billing adjustment, if applicable. We will work with you to evaluate billing adjustments after the account is secured.&lt;/p&gt;

&lt;p&gt;We will provide a list of potentially unexpected resources related to this event through the associated Support Case within the next 2 hour(s). Please review the resources listed for each of your services, and take action to stop, delete, or terminate any that are unwanted. The deeplinks in the attached file should take you to the exact resource page, or you can manually navigate to the resource to investigate. In addition to the resources detailed in the Support Case, it's important that you review all your services and credentials to identify and address any other unwanted usage, as described.&lt;/p&gt;

&lt;p&gt;If you need help securing your account, let us know through the Support Case where you can request a phone call or chat session for immediate assistance. Alternatively, if you believe that your account is secured and there is no unwanted access or usage, please contact us immediately via the Support Case to confirm this in writing.&lt;/p&gt;

&lt;p&gt;Thank you for your immediate attention to this matter.&lt;/p&gt;

&lt;p&gt;[1] &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/potential-account-compromise/" rel="noopener noreferrer"&gt;https://aws.amazon.com/premiumsupport/knowledge-center/potential-account-compromise/&lt;/a&gt;&lt;br&gt;
[2] &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable.html&lt;/a&gt;&lt;br&gt;
[3] &lt;a href="https://console.aws.amazon.com/iam/home#users" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/iam/home#users&lt;/a&gt;&lt;br&gt;
[4] &lt;a href="https://console.aws.amazon.com/iam/home#security_credential" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/iam/home#security_credential&lt;/a&gt;&lt;br&gt;
[5] &lt;a href="https://console.aws.amazon.com/iamv2/home#/users" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/iamv2/home#/users&lt;/a&gt;&lt;br&gt;
[6] &lt;a href="https://console.aws.amazon.com/iam/home#/policies" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/iam/home#/policies&lt;/a&gt;&lt;br&gt;
[7] &lt;a href="https://console.aws.amazon.com/iam/home#/roles" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/iam/home#/roles&lt;/a&gt;&lt;br&gt;
[8] &lt;a href="https://console.aws.amazon.com/billing/home#/bill" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/billing/home#/bill&lt;/a&gt;&lt;br&gt;
[9] &lt;a href="https://console.aws.amazon.com/support/home?#/" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/support/home?#/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Breaking Down the Required Actions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1
&lt;/h3&gt;

&lt;p&gt;The notification says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 1: If your application uses the exposed access key, you need to replace the key. To replace the key, first create a second key (at that point both keys will be active). Then, modify your application to use the new key.&lt;/p&gt;

&lt;p&gt;Next, disable (do not delete) the exposed key by clicking on the "Make inactive" option in the console. If there are any problems with your application, you can reactivate the exposed key. When your application is fully functional using the new key, please delete the exposed access key(s).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The wording can be a bit confusing about whether you should or shouldn't delete the key, but here's the gist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ultimately, the compromised access key must be deleted.&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;However, your organization may be legitimately using that key in applications or systems, so deleting it immediately could cause a business outage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create a new access key and update any applications or systems that were using the compromised one.

&lt;ul&gt;
&lt;li&gt;Make sure your systems are configured to use the new key.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Once that's done, &lt;strong&gt;deactivate&lt;/strong&gt; (not delete) the compromised key.

&lt;ul&gt;
&lt;li&gt;By deactivating instead of deleting, you leave yourself a rollback path in case something breaks.&lt;/li&gt;
&lt;li&gt;Verify that your applications and systems work correctly with the new key.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;After confirming there's no business impact, delete the compromised access key.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key was originally issued for a purpose — it's being used in some application or system. The point is: &lt;strong&gt;keep your legitimate workloads running while discarding the key that fell into the wrong hands.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2
&lt;/h3&gt;

&lt;p&gt;The notification says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 2: Check your CloudTrail log for unwanted activity.&lt;/p&gt;

&lt;p&gt;Check your account for any unwanted activity, such as creation of unapproved IAM users and/or associated passwords (login profile), access keys, policies, roles or temporary security credentials by checking your CloudTrail log, and immediately delete them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This step asks you to investigate whether any unauthorized changes or new resources were created, primarily focusing on IAM.&lt;/p&gt;

&lt;p&gt;Here, you'll need to use AWS CloudTrail to investigate, keeping these important points in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, CloudTrail records 90 days of &lt;a href="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-management-events-with-cloudtrail.html" rel="noopener noreferrer"&gt;management events&lt;/a&gt; as "Event history."&lt;/li&gt;
&lt;li&gt;Event history is recorded per AWS Region, so you need to explicitly select the region you want to examine.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;AWS has a concept called "global service events," and IAM falls into this category.&lt;/li&gt;
&lt;li&gt;Most global service events are logged as having occurred in the US East (N. Virginia) region.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, &lt;strong&gt;to review CloudTrail event history for IAM resources, you need to check the US East (N. Virginia) region.&lt;/strong&gt; &lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The compromised access key details are typically provided in a follow-up message within the support case opened by AWS, in a format like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Access Key: AKIAIOSFODNN7EXAMPLE&lt;br&gt;
IAMUser: foouser&lt;br&gt;
Event Name: GetCallerIdentity&lt;br&gt;
Event Time: January 1, 2026 00:00:00 (UTC+00:00)&lt;br&gt;
IP: 203.0.113.39&lt;br&gt;
IP Country/Region: US  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use the timestamp, IAM user name, and access key ID from this information as the primary filters when reviewing CloudTrail event history in the US East (N. Virginia) region.&lt;/p&gt;

&lt;p&gt;In recent incidents, attackers commonly delete the login profile (i.e., the console password) of existing IAM users to delay discovery. Rather than continuing to use the leaked key directly, they often create new IAM users with fresh access keys, building out additional IAM resources to carry out their activities. Modifications to existing IAM roles and policies are also frequently observed.&lt;/p&gt;

&lt;p&gt;You need to verify all of these actions and confirm that no IAM resources were created, modified, or deleted without the account owner's intent.&lt;/p&gt;

&lt;p&gt;In short, review all actions performed using the compromised access key across IAM resources. If new IAM resources were created, follow the chain — examine the activity performed by each newly created resource, then check what those resources created in turn. Repeat this process to uncover every unauthorized IAM resource.&lt;/p&gt;

&lt;p&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%2F0jv03gthi9hvriixmfv7.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%2F0jv03gthi9hvriixmfv7.png" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Investigation perspective for IAM resources&lt;br&gt;You need to follow the chain: actions taken with the leaked key AND actions taken with any newly created keys.&lt;/p&gt;



&lt;p&gt;For a complete list of IAM actions, refer to the official documentation below. Focus your review on non-Read operations — particularly Create and Update actions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/service-authorization/latest/reference/list_awsidentityandaccessmanagementiam.html" rel="noopener noreferrer"&gt;Actions, resources, and condition keys for AWS Identity and Access Management (IAM)&lt;/a&gt;&lt;/p&gt;

&lt;p&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%2F55jg1kahcyrbdbjqeyno.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%2F55jg1kahcyrbdbjqeyno.png" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS CloudTrail Event history page in the Management Console&lt;br&gt;IAM event history must be checked in the US East (N. Virginia) region.&lt;/p&gt;



&lt;p&gt;For detailed instructions on using CloudTrail, see the official documentation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/view-cloudtrail-events-console.html" rel="noopener noreferrer"&gt;Viewing recent management events with the console&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3
&lt;/h3&gt;

&lt;p&gt;The notification says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 3: Review your AWS account for any unwanted usage. Check your account for any unwanted usage, such as EC2 instances, Lambda functions, or EC2 Spot bids by logging into your AWS Management Console and reviewing each service page. You can also do this by checking the "Bills" page in the Billing console.&lt;/p&gt;

&lt;p&gt;Please note, unwanted usage can occur in any region and your console only displays one region at a time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unlike Step 2, this step requires you to check a broad range of AWS services — not just IAM — for any unauthorized resource creation, and to delete them as needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  What Should You Look For?
&lt;/h4&gt;

&lt;p&gt;This is a genuinely difficult question. In a nutshell, it depends on the IAM policies attached to the compromised access key's IAM user.&lt;/p&gt;

&lt;p&gt;For example, Amazon SES SMTP users are internally issued as IAM users with access keys (though the notification subject and handling differ when these are abused). If created following the recommended procedure, the key is issued with only the &lt;code&gt;ses:SendRawEmail&lt;/code&gt; permission via the AmazonSesSendingAccess inline policy at the time of writing. &lt;sup id="fnref4"&gt;4&lt;/sup&gt;  In that case, the attacker can only send emails via Amazon SES. While mass spam distribution is still a serious problem — it degrades your SES sender reputation — it wouldn't lead to a proliferation of resources across many AWS services.&lt;/p&gt;

&lt;p&gt;On the other hand, if the compromised key had &lt;code&gt;*FullAccess&lt;/code&gt;, &lt;code&gt;PowerUserAccess&lt;/code&gt;, or &lt;code&gt;AdministratorAccess&lt;/code&gt; attached, the scope of what an attacker can do expands dramatically.&lt;/p&gt;

&lt;p&gt;Therefore, you need to &lt;strong&gt;consider what the compromised and any newly created access keys are authorized to do.&lt;/strong&gt; Based on that, you must check &lt;strong&gt;all enabled regions&lt;/strong&gt; for unauthorized resource creation by the malicious actor.&lt;/p&gt;

&lt;p&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%2F6odta03f8illvlvof0vc.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%2F6odta03f8illvlvof0vc.png" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Investigation perspective for Step 3&lt;br&gt;Here too, you need to check actions from both the leaked key and any newly created keys, across all enabled regions.&lt;/p&gt;



&lt;p&gt;Manually reviewing event history across all regions with various filter conditions through the console is extremely tedious.&lt;/p&gt;

&lt;p&gt;If you have a CloudTrail trail configured with events stored in an S3 bucket, querying with Athena is very useful. CloudTrail Lake (though new onboarding has been announced as ending) is also helpful. However, if these services weren't enabled when the leak occurred, you'll only have the default 90 days of management event history.&lt;/p&gt;

&lt;p&gt;Reviewing default management events through the Management Console is very difficult, so I recommend using the AWS CLI to extract the data and convert it to CSV or another format for easier analysis.&lt;/p&gt;

&lt;p&gt;I've put together a sample shell script that makes it easier to extract event history. It works in any environment with &lt;code&gt;aws cli&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt; installed — feel free to use it as a reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kazzpapa3/research-via-cloudtrail" rel="noopener noreferrer"&gt;GitHub Repository : research-via-cloudtrail&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 4: Please work with your TAM/Account Manager and respond to the existing Support Case or create a new one to confirm completion of steps 1-3 and apply for a billing adjustment, if applicable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After completing the verification and remediation described in Steps 1–3, you can apply for a billing adjustment if needed.&lt;/p&gt;

&lt;p&gt;If you're using our billing agency service (as is common with AWS resellers in Japan), you cannot reply to or create support cases directly. You'll need to go through us — but you can request a review for billing adjustments related to AWS charges incurred by unauthorized resource creation.&lt;/p&gt;

&lt;p&gt;However, neither AWS nor we can guarantee that charges will be reduced. Every case is individually reviewed by the relevant AWS team, taking into account the full circumstances — what happened, the background, and what remediation was performed. The review criteria and process are not disclosed; you simply receive an approval or denial. That said, if the same type of incident has occurred before and a credit was previously granted, a second occurrence is almost always denied.&lt;/p&gt;

&lt;p&gt;For high-value unauthorized charges, the review may take considerable time. Even in cases where a credit is ultimately approved, you may need to pay the charges upfront first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not assume that AWS will bail you out if something goes wrong.&lt;/strong&gt; Think of it as: there's a possibility of relief, but it's not guaranteed. The priority should always be preventing these incidents from happening in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Working in the Technical Support division — positioned between our customers and AWS — we see a lot of access key compromise incidents.&lt;/p&gt;

&lt;p&gt;When an access key is compromised, the notification generally follows the flow we've walked through here. By working through each step methodically, you can address the situation.&lt;/p&gt;

&lt;p&gt;However, attacker techniques have become increasingly sophisticated and automated in recent years. If a compromised access key has broad permissions for resource creation, a wide variety of resources can be spun up automatically, racking up charges in the hundreds of thousands of yen (thousands of dollars) in no time.&lt;/p&gt;

&lt;p&gt;Leak vectors include accidental commits to public repositories like GitHub, exposed &lt;code&gt;.env&lt;/code&gt; files, and other forms of unintended disclosure. Proper key management is critical.&lt;/p&gt;

&lt;p&gt;For use cases that traditionally required access keys — such as calling AWS APIs from outside AWS — alternatives like the &lt;code&gt;aws login&lt;/code&gt; command and IAM Roles Anywhere are now available. With these options on the table, it's worth revisiting whether your current use of access keys is still necessary.&lt;/p&gt;

&lt;p&gt;That said, there are still scenarios where access keys are the only option for external access. In those cases, access keys aren't inherently evil. But following best practices — regular rotation, avoiding overly broad permissions — remains essential.&lt;/p&gt;

&lt;p&gt;I usually close these posts with "I hope this helps someone," but this time: I hope you never need this post.&lt;/p&gt;

&lt;p&gt;Until next time.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Cases opened by AWS are sometimes referred to as "outbound cases." ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;CloudTrail Event history provides a viewable, searchable, downloadable, and immutable record of the past 90 days of CloudTrail management events in an AWS Region. (Quoted from the &lt;a href="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html#cloudtrail-concepts-event-history" rel="noopener noreferrer"&gt;AWS CloudTrail documentation&lt;/a&gt;) ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;For most services, events are recorded in the Region where the action occurred. For global services such as AWS Identity and Access Management (IAM), AWS STS, and Amazon CloudFront, events are delivered to trails that include global services. Most global service events are logged as occurring in the US East (N. Virginia) Region, though some are logged in other regions such as US East (Ohio) or US West (Oregon). (Quoted from the &lt;a href="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html#cloudtrail-concepts-global-service-events" rel="noopener noreferrer"&gt;AWS CloudTrail documentation&lt;/a&gt;) ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>credentials</category>
      <category>abuse</category>
    </item>
  </channel>
</rss>
