Source code for dxaws_accounts.providers.aws
from __future__ import annotations
from dataclasses import asdict
from typing import Any, Iterable, List, Optional
from dxaws_core.providers.aws import AwsProviderBase
from dxaws_accounts.models import AwsOrgAccount
[docs]
class AwsProvider(AwsProviderBase):
"""AWS implementation for dxaws-accounts.
This provider is intentionally thin: it only knows how to talk to AWS
Organizations and convert AWS responses into our internal models.
"""
def __init__(self, *, aws) -> None:
# provider_ns is derived from the module name, e.g. "cloudfront", "acm", "dns.route53"
super().__init__(aws=aws, provider_ns="accounts.organizations")
# ---------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------
def _client(self, service_name: str):
"""Best-effort client accessor compatible with different aws wrappers."""
if hasattr(self.aws, "client") and callable(getattr(self.aws, "client")):
return self.aws.client(service_name)
# Common alternative: aws.session.client(...)
sess = getattr(self.aws, "session", None)
if sess is not None and hasattr(sess, "client") and callable(getattr(sess, "client")):
return sess.client(service_name)
raise TypeError(
"aws object must provide .client(service_name) or .session.client(service_name)"
)
@staticmethod
def _to_org_account(d: dict[str, Any]) -> AwsOrgAccount:
"""Convert an AWS Organizations Account dict into our AwsOrgAccount model."""
# AWS returns keys like: Id, Arn, Email, Name, Status, JoinedMethod, JoinedTimestamp
return AwsOrgAccount(
id=str(d.get("Id") or ""),
name=str(d.get("Name") or ""),
email=str(d.get("Email") or ""),
status=str(d.get("Status") or ""),
)
# ---------------------------------------------------------------------
# Public API used by the Accounts manager
# ---------------------------------------------------------------------
[docs]
def list_org_accounts(self) -> list[AwsOrgAccount]:
"""List all accounts visible via AWS Organizations."""
org = self._client("organizations")
accounts: list[AwsOrgAccount] = []
paginator = getattr(org, "get_paginator", None)
if callable(paginator):
for page in org.get_paginator("list_accounts").paginate():
for a in page.get("Accounts", []) or []:
accounts.append(self._to_org_account(a))
return accounts
# Fallback (non-paginated)
resp = org.list_accounts()
for a in resp.get("Accounts", []) or []:
accounts.append(self._to_org_account(a))
return accounts
[docs]
def describe_org_account(self, *, account_id: str) -> AwsOrgAccount:
"""Fetch a single account from AWS Organizations."""
if not account_id:
raise ValueError("account_id is required")
org = self._client("organizations")
resp = org.describe_account(AccountId=account_id)
acct = resp.get("Account") or {}
return self._to_org_account(acct)
# Convenience (sometimes handy for debugging / o11y)
[docs]
def list_org_accounts_dict(self) -> list[dict[str, Any]]:
"""Return org accounts as plain dicts (useful for logging/tests)."""
return [asdict(a) for a in self.list_org_accounts()]