Skip to content

Lower-level return values

Section 21 · From docs/INVENTORY_REFERENCE.md on GitHub

21.1 RelationshipLinker

linker = RelationshipLinker(assets, materialize_external=True)
linker.seed_existing(edges)  # -> None
new_edges = linker.link(include_generic=True)  # -> list[NetworkEdge]
linker.external_assets  # list[CloudAsset]
linker.unresolved  # list[dict], max 2000
linker.resolve(identifier, context=None)  # -> str | None (asset id)
  • link() returns only the edges it created. They are deduplicated on (source_id, target_id, edge_type) against each other and against seeded edges; the generic pass also skips a pair already linked in either direction.
  • external_assets are the CLOUD_ACCOUNT placeholders it created; add them to your asset list, the new edges point at them.
  • unresolved entries are section 11 records.
  • resolve() returns an asset id or None; context (the referencing asset) scopes ambiguous names.
  • With materialize_external=False references into unmapped accounts are reported as unresolved instead.

21.2 Collectors

All inventory collectors implement the BaseCollector interface:

Method Returns
await collect() list[CloudAsset]
await collect_edges() list[NetworkEdge], the collector network edges of 5.3 (AWS security group rules and VPC → subnet, Azure NSG rules and VNet → subnet, GCP internet ingress)
await run() tuple[list[CloudAsset], list[NetworkEdge]]
coverage attribute CollectionCoverage with one ServiceCoverage per task (AWS) or step

The assets carry declared relations but no relationship edges: run them through RelationshipLinker (or InventoryMapper._link) to get the typed edges. AWSDeepInventoryCollector.collect() also drops breadth-sweep hits that duplicate a deep-collected asset (by arn and aliases), so its output has at most one asset per resource.

AzureDeepInventoryCollector.collection_mode is "resource-graph" or "sdk" after collect().

AzureDeepInventoryCollector(credential, subscription_id, graph_client_factory=None, use_resource_graph=True, *, arm_client_factory=None, dataplane_client_factory=None, options=None, **options) takes the AzureDeepOptions fields as keyword options (services, exclude_services, child_resources, max_child_parents, diagnostic_settings, max_diagnostic_settings, data_plane, kubernetes, kubernetes_timeout, rest_concurrency); an unknown option raises TypeError. Its coverage attribute is a CollectionCoverage with one record per task. The factories are credential -> client (ARM REST: list(path, api_version, **params), get(...), post(...)) and (credential, base_url, scope) -> client (data planes); tests inject fakes there.

discover_entra(credential, client_factory=None, *, coverage=None, max_objects=10000, max_expansions=1000) returns the Entra assets (ORGANIZATION, IDENTITY_USER, IDENTITY_GROUP, APP_REGISTRATION, SERVICE_PRINCIPAL, IAM_POLICY) and records one coverage entry per Graph listing on coverage.

21.3 Kubernetes

Function Returns
eks_token(session, cluster_name, region) str, a k8s-aws-v1. bearer token (presigned STS GetCallerIdentity)
KubernetesReader(endpoint, ca_data_b64, token, timeout=10).get(path, params=None) dict, the decoded JSON response
KubernetesReader.list(path) iterator of dict items, following continue tokens, 500 per page, at most 5000 objects
map_cluster_objects(reader, cluster_arn, region, account_id) list[CloudAsset]: K8S_NAMESPACE, K8S_SERVICE_ACCOUNT, K8S_WORKLOAD, K8S_SERVICE, K8S_INGRESS
collect_eks_workloads(session, cluster, region, account_id, timeout=10) list[CloudAsset], the above for one DescribeCluster result

Kubernetes object assets carry cluster_arn, namespace, kind and, per kind:

Kind Metadata Relations
Namespace phase cluster CONTAINS / CLUSTER_CONTAINS_SERVICE → namespace
ServiceAccount irsa_role_arn namespace contains it; ASSUMES_ROLE / RUNS_ON → IRSA role
Deployment, StatefulSet, DaemonSet, CronJob replicas, ready_replicas, schedule, images, service_account, host_network, privileged_containers, node_selector namespace contains it; USES_IMAGE / RUNS_ON → each image's repository; REFERENCES / RUNS_ON → its service account
Service service_type, ports, load_balancer_hostnames, internal namespace contains it; LOAD_BALANCER_TARGET / SERVES_TRAFFIC_TO → selected workloads; load balancer ROUTE / LOAD_BALANCED_BY → service (reverse); exposed when LoadBalancer with a hostname
Ingress ingress_class, hosts, backends, load_balancer_hostnames, tls namespace contains it; ROUTE / SERVES_TRAFFIC_TO → backend services; load balancer ROUTE / LOAD_BALANCED_BY → ingress; exposed when it has a hostname

Pod Identity associations are collected by the EKS collector itself, not through the Kubernetes API.

AKS goes through the same mapper:

Function Returns
map_cluster_objects(reader, cluster_arn, region, account_id, provider=CloudProvider.AWS) as above; with provider=CloudProvider.AZURE the assets are Azure assets and the AKS rules below apply
azure_kubernetes.cluster_endpoint(arm, cluster_id) (server, ca_b64) from the clusters section of the listClusterUserCredential kubeconfig
azure_kubernetes.aks_mappable(cluster_row) None, or why the cluster is skipped (no Entra integration, stopped)
azure_kubernetes.collect_aks_workloads(credential, arm, cluster_row, *, timeout=10, reader_factory=None) list[CloudAsset] for one AKS cluster, read with an Entra token for 6dae42f8-4368-4678-94ff-3960e28e3630/.default

On AKS, a ServiceAccount carries workload_identity_client_id and workload_identity_tenant_id instead of irsa_role_arn, and ASSUMES_ROLE / RUNS_ON → azure-client-id:<client id>, the user-assigned identity. Service and Ingress load_balancer_hostnames also hold load balancer IPs, which resolve to public IP assets, and service.beta.kubernetes.io/azure-load-balancer-internal: "true" sets internal. EKS output is unchanged.

21.4 Catalogs

Function Module Returns
load_catalog(name) cloudg.inventory.catalogs dict, the packaged <name>.yaml merged with $CLOUDG_CATALOG_DIR/<name>.yaml; cached per process
asset_type_map(mapping, catalog="") cloudg.inventory.catalogs dict[str, AssetType]; raises ValueError naming the catalog, key and value for an unknown type
flatten(groups) cloudg.inventory.catalogs list[str] from a list or a mapping of group → list
asset_type_from_arn(arn) cloudg.inventory.aws_deep_tasks (re-exported by aws_deep) AssetType from the aws_arn_types catalog, OTHER when unknown
select_tasks(names, include=None, exclude=None) cloudg.inventory.aws_deep_tasks (re-exported by aws_deep) list[str] of task names kept

21.5 Mapper helpers

Function Returns
deduplicate(assets, edges) tuple[list[CloudAsset], list[NetworkEdge]]: one asset per arn (the deep-collected copy wins over sweep or placeholder copies; relations and aliases are merged; exposure is OR-ed); edges re-pointed to the kept copy, self-loops dropped, and exact repeats dropped: two edges are repeats when their endpoints, edge_type, port_range, ports, protocol, cidr and direction all match, so parallel security group, NACL and internet-exposure rules survive
add_account_hierarchy(assets, edges) tuple[list[CloudAsset], list[NetworkEdge]]: an account node per (provider, account_id) (reusing organization / subscription / project nodes), and a hierarchy CONTAINS edge to every asset nothing else contains