ACP Logo
Beyond 'one company, one organisation': the consultancy's AWS inventory problem — many orgs, mixed accounts, multiple partitions, solved with GitOps
Tamás Kiss
Tamás Kiss

Beyond 'one company, one organisation': the consultancy's AWS inventory problem — many orgs, mixed accounts, multiple partitions, solved with GitOps

A techie journal about why a spreadsheet stopped working, why we did not run to the nearest enterprise CMDB either, and how GitHub ended up being a surprisingly good fit for cloud account inventory.

Intro

Let's get the clickbait out of the way: this is not a magical zero-touch CMDB, it's a Git repo. Now the why and how and the rest of the story.

As a company we look after roughly 140+ AWS accounts. Across multiple organisations, even across multiple AWS partitions (yes, that's a thing — more on that later). Some of them are organisation-level accounts, some are standalone accounts, and the mix grows every quarter. Managing that many accounts, and actually understanding what they are and how they are set up, is one of those boring problems that every company runs into before they reach the maturity level where they have enterprise-grade asset management and all manner of fancy solutions to run it. And quite frankly, even those fancy solutions are not particularly good at managing cloud assets — at least not at the level we needed.

The nutshell version, before you commit to reading the long, detailed, mildly sidetracked whole story:

  • Spreadsheet stopped scaling, enterprise CMDB is overkill.
  • Put the inventory in Git — version control, access control and history come for free.
  • Use GitHub Actions with OIDC to fetch the data via a read-only IAM role — no long-lived credentials.
  • Once you have that, treat each commit as an event to fire downstream automation.

Let's dive in.

What we actually need to track

The interesting question isn't which accounts do we have — that's just one aws organizations list-accounts away. The interesting question is everything around the account.

For our use case we specifically wanted to know:

  • the SSO login URL, because the first thing anyone asks is "how do I get in?"
  • which AWS Landing Zone Accelerator (LZA) version is deployed, because that drives the features and guardrails in place
  • the home region, and the wider regional footprint
  • the billing arrangement, which became much more relevant since the recent release that allows billing transfers — and we obviously make use of that as a reseller
  • account lineage — where it came from, where it moved, when it was closed

A flat list of account IDs helps with none of this. The metadata is the whole point.

Why Git?

We were already going down the GitOps road for everything else, so the question wasn't really "should this be in Git", it was "why wouldn't it be?". Once I sat with that for a bit, the honest answer turned out to be: no reason at all. Git solves a surprising number of inventory problems for free.

Access control. GitHub's team and permission model is already part of our daily workflow. No need to invent a new RBAC just for the inventory. And because the data isn't particularly sensitive, the group with read access can be intentionally wide. That's a feature, not a workaround. Developers and operators should be able to look up an SSO URL without raising a ticket.

History. Git history is the audit log. Closed accounts don't vanish — they sit in the commit history. "Who used to own this account? When did we close it? When did it move organisation?" — all answerable with git log, not by trawling through some ticketing system.

Single source of truth. The repo is canonical. Anything else — dashboards, notifications, reports — is derived from it.

How it fills itself in

Git as a source of truth only works if the source of truth stays current. Manual updates rot. Always. So the inventory updates itself.

The mechanism is straightforward:

  1. A read-only IAM role is deployed into each organisation's management account. Permissions are narrowly scoped to only the things that are relevant: the Organisations APIs, basic account attributes, and the bits of LZA configuration we want to surface. Nothing else.
  2. That role trusts GitHub's OIDC provider, scoped to our specific repo and workflows. No long-lived credentials anywhere — no secret to rotate, no secret to leak.
  3. GitHub Actions assumes the role on a schedule (and on demand), queries each organisation and each standalone account, and writes the results back into the repo as structured files.
  4. The commit lands. History updates. The diff is the change log.

The OIDC bit matters more than it sounds. It means the trust boundary is "a workflow in this repo", not "whoever happens to hold the access key". Once you have set this up once for one repo, you will never willingly go back to stored credentials.

The events side, or: GitOps cuts both ways

Here is where it gets nicer than a plain inventory. Because the repo lives on GitHub, every change is also an event. A new account appearing in a diff is not just an update to the data, but also a trigger.

We hang additional automation off these events:

  • reseller notifications when accounts are created, closed, or moved between organisations
  • for billing transfer cases we have additional reporting duties towards AWS, and these can now be fired as events. No need to wire them up to whatever else happens to be creating the account
  • anything else we want to add later, without touching the systems that create the accounts

That last point is the one that sold me on this shape. We have automated account vending machines through the LZA product, but the vending machine isn't necessarily wired into every downstream consumer — and frankly, we don't want it to be. That kind of coupling gets ugly fast. By making the inventory itself the event source, every consumer hooks into one place.

Sidetrack: how this was actually built

Worth a small sidetrack here, because the how is part of the point. This was built with AI assistance — like more or less everything else these days. But the order of operations matters far more than the fact that an LLM was in the loop.

The first artefact wasn't code. It was a PRD, a product requirements document, written by hand, by a human (me) who already had a rough idea of what the architecture had to look like. Building blocks named, constraints stated, non-goals called out. Then that document was handed to the model as the prompt for the actual implementation.

Day one produced something good enough — a long afternoon, end to end, from PRD to working solution. Not "production-grade" good enough, but "the shape is right and we can iterate" good enough. Everything since then has been edge cases that you only find by running the thing against real data.

Which neatly brings us to a gotcha worth its own section.

Sidetrack of the sidetrack: the ARN partition gotcha

We are an AWS Direct Connect partner via our partnership with Interxion, which means we offer Direct Connect not just in the standard global (commercial) partition but also in the new European Sovereign Cloud partition of AWS. And, of course, we have AWS accounts in both. So the inventory has to cover both as well.

If you have never had reason to look closely at an ARN, this is the moment to look closely at an ARN:

arn:aws:iam::123456789012:role/MyRole
arn:aws-eusc:iam::123456789012:role/MyRole

That second segment is the partition. It is part of the ARN itself; it is not a region, and it is not a tag. Each isolated cloud — the commercial one most people know, the European Sovereign Cloud, the AWS China regions, AWS GovCloud (US), and a handful of intelligence-community clouds — has its own partition value. Any code that assumes every ARN starts with arn:aws: will silently break the moment you point it at a non-commercial partition. Ask me how I know.

And here is the genuinely interesting nuance: when you are writing an IAM trust policy or referencing a managed policy, the intuitive instinct is to make the partition match the partition you are deploying to. Don't. For AWS-managed policies referenced by ARN, leave the partition as aws and let AWS translate it for you when the policy is evaluated in the other partition. Hardcoding aws-eusc into a managed policy ARN is precisely the kind of "fix" that looks correct, deploys cleanly, and then fails in a way that takes embarrassingly long to debug.

For your own resources, on the other hand, the partition must match the partition the resource actually lives in. The rule of thumb that survived contact with reality:

Tipp: AWS-managed → leave it as aws. Customer-managed → use the actual partition.

This is exactly the sort of thing a PRD doesn't capture, the model doesn't know to warn you about, and you only find by deploying into a second partition and watching something go sideways. Which is, more or less, an honest description of every cloud project ever.

Closing words

For a developer or an operator, the inventory repo became the boring, reliable place you go when you need to know something about an account. SSO login URL? It's there. Account ID for that one workload in that one region? It's there. Which LZA version is running in the prod organisation? There.

For the platform team, it's a read-only mirror that updates itself, ships events to anything that needs them, and audits itself by virtue of being Git.

Nothing about this is technically exotic — IAM roles, OIDC, GitHub Actions, and a folder of Markdown (with a small amount of YAML on the side for the things that need to be machine-readable — our Odoo invoice generator, for example, reads the inventory's YAML snapshot to figure out who to bill for what). The whole thing is "obvious in retrospect". But it sits exactly in the gap where most growing cloud organisations get stuck: too many accounts for a spreadsheet, not enough budget or appetite for an enterprise platform, and a real need to know what's out there.

GitOps turns out to fit that gap rather well.

Nous contacter

Parlez-nous de votre projet – nous vous répondons sous 24 heures.

  • Décrivez-nous votre défi
  • Recevez une proposition d'architecture sur mesure
  • Démarrez avec un accompagnement expert