Skip to main content

Python helper library that provides a high-level, cached view of AWS Organizations

Project description

aws-org-view

A small Python helper library that provides a high-level, cached view of AWS Organizations.

It is designed to be aggressively cache-heavy and lazy by default:

  • Results from AWS Organizations APIs are stored in TTL caches to minimise repeated API calls
  • Queries only perform the minimum number of API calls required to resolve the specific question being asked (for example, stopping parent traversal as soon as a match is found)

It wraps the low-level AWS Organizations API calls (via boto3) to make common “org structure” queries easier, including:

  • Checking whether an account belongs under a given OU/root (or matches any ID in a “haystack”)
  • Building a nested OU → accounts hierarchy tree, optionally only one level deep
  • Reducing repeated API calls with TTL caches (helpful when walking parent chains or listing lots of OUs/accounts)

Install

pip install aws-org-view

Quickstart

from aws_org_view import AwsOrgView

org = AwsOrgView()  # uses boto3.client("organizations") by default

# Example: check if account "123456789012" is within ou-abcd-xyz987 or ou-abcd-wft745 - either directly or in one of their descendant OUs.
found = org.account_in_haystack(
    account_id="123456789012",
    haystack=["ou-abcd-xyz987", "ou-abcd-wft745"],  # OUs, roots, and/or account IDs
)
print(found)

# Example: build a full OU hierarchy (root -> all descendants)
tree = org.get_ou_hierarchy()
print(tree)

What AwsOrgView does

Caching

AwsOrgView maintains several cachetools.TTLCache instances to avoid repeated AWS calls within a time window:

  • Parent lookups (list_parents)
  • Organization root ID (list_roots)
  • OU descriptions (describe_organizational_unit)
  • Accounts under a parent (list_accounts_for_parent)
  • Child OUs under a parent (list_organizational_units_for_parent)

You can control cache size and TTL:

org = AwsOrgView(cache_ttl=900, cache_maxsize=1024)  # 15 min TTL

Using account_in_haystack()

Purpose

account_in_haystack() answers:

“Does this account appear in (or sit underneath) any ID in this set of IDs?”

It walks upwards from the account:

account -> parent OU -> ... -> root

At each step it checks if the current ID is present in your haystack.

Signature

def account_in_haystack(
    self,
    account_id: str,
    haystack: set[str] | list[str],
    require_direct_descendant: bool = False,
) -> bool:

Examples

1) Account is directly listed in the haystack

org.account_in_haystack(
    account_id="123456789012",
    haystack=["123456789012"],
)
# -> True

2) Account is under an OU in the haystack

org.account_in_haystack(
    account_id="123456789012",
    haystack=["ou-abcd-xyz987"],
)
# -> True if that OU is in the account’s parent chain

3) Account is somewhere under the org root in the haystack

org.account_in_haystack(
    account_id="123456789012",
    haystack=["r-a1b2"],
)

4) Only count direct children of a target OU/root

If require_direct_descendant=True, the function only checks:

  • the account itself, and
  • its immediate parent

This is useful if you only consider an account “in scope” when it is directly attached to a given OU/root (not nested multiple OUs deep).

org.account_in_haystack(
    account_id="123456789012",
    haystack=["ou-abcd-xyz987"],
    require_direct_descendant=True,
)

Notes / behavior

  • haystack can be a list or a set; lists are converted to a set.
  • The code limits traversal depth to 6 checks (AWS supports up to 5 OU levels plus the root).
  • If AWS returns anything other than exactly one parent for an entity, a ParentResolutionError is raised.

Using get_ou_hierarchy()

Purpose

get_ou_hierarchy() builds a nested dictionary representing:

  • an OU/root node’s name
  • its direct accounts
  • its child OUs
  • recursively, the children of those OUs (unless you request only one level)

Signature

def get_ou_hierarchy(
    self,
    parent_id: str | None = None,
    direct_descendants_only: bool = False
) -> OUMembershipRetrieverResult:

Examples

1) Full org tree from the root

org = AwsOrgView()
tree = org.get_ou_hierarchy()  # parent_id=None => use org root

2) Tree rooted at a specific OU

tree = org.get_ou_hierarchy(parent_id="ou-abcd-xyz987")

3) Only include immediate child OUs (no recursion)

tree = org.get_ou_hierarchy(direct_descendants_only=True)

With direct_descendants_only=True, the output includes:

  • accounts at the parent node
  • immediate org_units entries with only their name

It will not recursively populate grandchildren OUs.

Returned structure

The return value is an OUMembershipRetrieverResult, which is a dict wrapper. The top-level dict has a single key: the root ID you requested (OU ID or root ID). That key maps to a node shaped like:

{
  "<parent_id>": {
    "name": "<friendly name>",
    "accounts": [ ...accounts directly under this parent... ],
    "org_units": {
      "<child_ou_id>": {
        "name": "<child ou name>",
        "accounts": [...],
        "org_units": {...}
      },
      ...
    }
  }
}

Getting a flat list of all accounts

OUMembershipRetrieverResult provides:

accounts = tree.get_accounts()

That recursively walks the tree and returns a single flat list of account objects.


Using a custom client

There are two supported customisation styles:

  1. Pass a pre-built boto3 Organizations client
  2. Pass a client provider (recommended when you need refresh/assume-role logic)

1) Pass a boto3 Organizations client

import boto3
from aws_org_view import AwsOrgView

client = boto3.client("organizations")
org = AwsOrgView(client=client)

2) Pass a custom OrganizationsClientProvider

The library defines a runtime-checkable protocol:

class OrganizationsClientProvider(Protocol):
    def get_client(self) -> OrganizationsClient: ...

Your provider can do anything internally (assume role, refresh credentials, add custom config), as long as it returns a valid Organizations client from get_client().

Example: provider using a named AWS profile

import boto3
from aws_org_view import AwsOrgView

class ProfileOrganizationsClientProvider:
    def __init__(self, profile_name: str):
        self._session = boto3.Session(profile_name=profile_name)

    def get_client(self):
        return self._session.client("organizations")

org = AwsOrgView(client=ProfileOrganizationsClientProvider("prod-admin"))

Example: provider that assumes a role

import boto3
from aws_org_view import AwsOrgView

class AssumeRoleOrganizationsClientProvider:
    def __init__(self, role_arn: str, session_name: str = "aws-org-view"):
        self._sts = boto3.client("sts")
        self._role_arn = role_arn
        self._session_name = session_name

    def get_client(self):
        creds = self._sts.assume_role(
            RoleArn=self._role_arn,
            RoleSessionName=self._session_name,
        )["Credentials"]

        session = boto3.Session(
            aws_access_key_id=creds["AccessKeyId"],
            aws_secret_access_key=creds["SecretAccessKey"],
            aws_session_token=creds["SessionToken"],
        )
        return session.client("organizations")

org = AwsOrgView(
    client=AssumeRoleOrganizationsClientProvider(
        "arn:aws:iam::123456789012:role/OrgReadRole"
    )
)

Development

Create (and remove if needed) the Hatch dev environment.

hatch env remove dev
hatch env create dev

Run tests: hatch run dev:fmt

Run code linting: hatch run dev:pytest

Run code type checking: hatch run dev:typing

License

aws-org-view is distributed under the terms of the MIT license.


Python written by humans. English written by AI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aws_org_view-0.0.3.tar.gz (7.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aws_org_view-0.0.3-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

File details

Details for the file aws_org_view-0.0.3.tar.gz.

File metadata

  • Download URL: aws_org_view-0.0.3.tar.gz
  • Upload date:
  • Size: 7.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.2 cpython/3.14.2 HTTPX/0.28.1

File hashes

Hashes for aws_org_view-0.0.3.tar.gz
Algorithm Hash digest
SHA256 11c5be284b24855ca3bb0bc0986095939372a38c0f5ec6eab0d7844cb5896b45
MD5 e34e6cf9b2402eb8ea01a167671199f5
BLAKE2b-256 17e770e00d715fe6d12ad610c784ebfc515c5af991f94c90d8c5c0a2f28c71bc

See more details on using hashes here.

File details

Details for the file aws_org_view-0.0.3-py3-none-any.whl.

File metadata

  • Download URL: aws_org_view-0.0.3-py3-none-any.whl
  • Upload date:
  • Size: 9.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.2 cpython/3.14.2 HTTPX/0.28.1

File hashes

Hashes for aws_org_view-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 0af919c6f3122cf23bc79ca473468ab3ce0e4204911476dd41942ccc1c8081c8
MD5 bb16c8c7a3b383a05cd360b34e996d96
BLAKE2b-256 e4cca2c2cf852771686f3b24ce86cd1e5954b64967b6602d98375b3295de540f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page