惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
N
Netflix TechBlog - Medium
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
DataBreaches.Net
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
I
InfoQ
F
Fortinet All Blogs
J
Java Code Geeks
Last Week in AI
Last Week in AI
美团技术团队
大猫的无限游戏
大猫的无限游戏
有赞技术团队
有赞技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园_首页
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Idea for Managing Policies Across Nearly 1,000 AWS Organi...
ICHINO Kazua · 2026-04-29 · via DEV Community

"A Note from the Author"

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.

  • 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.
  • 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.
  • This article is based on a presentation I gave at Security-JAWS #40, a community event focused on AWS security in Japan, held on February 12, 2026.
  • 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.

Hello, everyone. I'm Ichino (@kazzpapa3), 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.

Today I want to talk about a challenge we're facing: how to properly manage IAM and SCP policies across nearly 1,000 AWS Organizations.

Background

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

Here's the situation:

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

This is the problem I want to address.

Oh, and personally — I think JSON came too early for humanity. More on that later.

Why the Differences Exist

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.

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:

  • Support case access: Some Organizations deny all support:* actions (customers must go through the reseller for support), while others allow customers to file support cases directly.
  • Root user management: 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.
  • Organization-level operations: Leaving the Organization is always denied, but other operations may vary.

These differences exist in both IAM policies and SCPs.

The Scale of the Problem

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.

Visually, think of each policy as a series of blocks — one per Sid or Condition. 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.

The Building-Block Idea

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?

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.


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


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

Should We Use IaC?

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

But there were concerns:

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

To be clear: we're not against IaC. We're just exploring alternatives that might better fit our deployment model and team skills.

The Approach: YAML Modules + yq

With some help from generative AI for brainstorming, we arrived at a lightweight approach: write policy components in YAML and use the yq command-line tool to assemble them.

Writing Policies in YAML

Each component is a separate YAML file. For example, even the Version declaration gets its own file:

VersionDeclaration.yaml

# Extracted just the IAM JSON policy Version element.
# Needs review if the Version specification is ever revised.
# https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html
Version: "2012-10-17"
Statement:

Enter fullscreen mode Exit fullscreen mode

Here's a module that denies support:* (used when customers must go through the reseller for support):

BasicRestrictionAsResaler.yaml

  - Sid: BasicRestrictionAsResaler
    Effect: Deny
    Action:
      - support:*
      # Summary: Deny all AWS Support actions
      # Relaxable?: No — customers must not file support cases directly

      - supportplans:*
      # Summary: Deny all support plan modifications
      # Relaxable?: No — support plan must not be changed

      - tax:*
      # Summary: Deny all tax setting modifications
      # Relaxable?: No — tax settings must remain configured for Japan

    # Deny on all resources
    Resource: '*'

Enter fullscreen mode Exit fullscreen mode

And here's the variant that allows support:* (for customers who are permitted to file cases directly):

BasicRestrictionAsResalerForResold.yaml

  - Sid: BasicRestrictionAsResalerForResold
    Effect: Deny
    Action:
      - supportplans:*
      # Summary: Deny all support plan modifications
      # Relaxable?: No — support plan must not be changed

      - tax:*
      # Summary: Deny all tax setting modifications
      # Relaxable?: No — tax settings must remain configured for Japan

    # Deny on all resources
    Resource: '*'

Enter fullscreen mode Exit fullscreen mode

Notice the only difference: the first variant includes support:* in the deny list; the second omits it.

Assembling with yq

To build a policy that denies support access:

yq eval-all '
  select(fileIndex == 0) |
  .Statement = [
    (load("IAMPolicyRestrictionForCustomer.yaml") | .[0]),
    (load("BasicRestrictionAsResaler.yaml") | .[0]),        # This line differs
    (load("RestrictionToAWSOrganizationsAsResaler.yaml") | .[0])
  ]
' VersionDeclaration.yaml > foo.yaml

Enter fullscreen mode Exit fullscreen mode

To build a policy that allows support access, just swap one module:

yq eval-all '
  select(fileIndex == 0) |
  .Statement = [
    (load("IAMPolicyRestrictionForCustomer.yaml") | .[0]),
    (load("BasicRestrictionAsResalerForResold.yaml") | .[0]),  # This line differs
    (load("RestrictionToAWSOrganizationsAsResaler.yaml") | .[0])
  ]
' VersionDeclaration.yaml > bar.yaml

Enter fullscreen mode Exit fullscreen mode

Here's what yq is doing:

  1. eval-all — operate across multiple files.
  2. select(fileIndex == 0) — use the first file (VersionDeclaration.yaml) as the base.
  3. .Statement = [...] — populate the Statement array by loading each module file and extracting its first element.
  4. Output the assembled YAML.

Converting to JSON

Once assembled, convert to JSON for use as an actual IAM policy:

yq -o=json foo.yaml > foo.json

Enter fullscreen mode Exit fullscreen mode

The result is a standard IAM policy document:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BasicRestrictionAsResaler",
      "Effect": "Deny",
      "Action": [
        "support:*",
        "supportplans:*",
        "tax:*"
      ],
      "Resource": "*"
    }
  ]
}

Enter fullscreen mode Exit fullscreen mode

(Other statements omitted for brevity.)

Validating the Output

We can validate the generated policy using IAM Access Analyzer:

aws accessanalyzer validate-policy \
  --policy-type IDENTITY_POLICY \
  --policy-document file://foo.json
{
    "findings": []
}

Enter fullscreen mode Exit fullscreen mode

If findings comes back empty, the policy document is syntactically valid. Note that this doesn't verify whether the policy does what you intend — that's a separate review.

What We Learned

The modular approach feels promising. Here's what stood out:

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

JSONC as an alternative

After presenting an earlier version of this talk at JAWS-UG DE&I, someone pointed out that JSONC (JSON with Comments) exists as a specification. That's a fair point — but YAML still wins for us because of the modular composition workflow with yq.

The Remaining Challenge: Deployment

The policy composition problem feels solved, but deployment is still an open question.

Currently, we use bulk-update scripts that overwrite policies across all accounts. This works because:

  • The accounts are under our management.
  • Policy names and role names are stable and guaranteed to exist.

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.

The policy assembly step (YAML modules + yq) 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.

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

Closing Thoughts

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

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

That said, I'd love to hear if you have a better approach. This is very much a work in progress, and I'm open to suggestions.

Security is job zero.

Thank you for reading.

References