I work in cloud security, and the question I keep coming back to on any account is how things are wired together. Scanners like Prowler and Checkov are good at telling you what's misconfigured. What they hand back is a flat list, though, and a flat list won't tell you that the bucket with a public policy triggers a Lambda whose role can assume a role in another account.
cloudg (short for cloud graphing) is the tool I built for that. It collects assets from AWS, Azure and GCP into a NetworkX graph with typed edges between them. It can also run Prowler, ScoutSuite, Checkov and Trivy over the same inventory, so each finding sits on the asset it's about.
It's a Python package under the MIT license, and the current release on PyPI is 0.6.1.
Two commands, two questions
cloudg map answers "what exists here, and how is it connected?" It runs no scanners, and they don't need to be installed. On AWS it has 133 dedicated collectors, plus a Cloud Control API sweep that lists every resource type that supports listing (over 800 of them), tagged or not. Azure is read through Azure Resource Graph across subscriptions and management groups, and GCP through Cloud Asset Inventory across the organization.
With --org, it starts from the AWS Organizations management account and maps every member account, along with the OU tree, the SCPs and the Control Tower landing zone.
The part I care about most is that the edges say what they mean. An S3 bucket INVOKES a Lambda, a task definition USES_IMAGE an ECR repository, a function ASSUMES_ROLE a role, and an SCP GOVERNS an OU. Trust to accounts you didn't map shows up as cross-account edges. That is what lets cloudg deps tell you what a resource depends on and what breaks if it goes away.
cloudg run is the full security pipeline. It collects, builds the graph, fans out to the scanners, then merges and deduplicates their findings and maps them to 28 compliance frameworks. Those include CIS for all three clouds, NIST 800-53 and CSF 2.0, PCI DSS 4.0, SOC 2, HIPAA, ISO 27001:2022 and MITRE ATT&CK.
If you already ran the scanners yourself, cloudg ingest takes their output files directly. That path needs no cloud credentials and no scanner binaries.
Trying it
You need Python 3.11 or newer. The cloud SDKs are extras, so install only the providers you use ([aws], [azure], [gcp], or [all]):
pip install "cloudg[aws]"
# map every region in one account
cloudg map -p aws --regions all
# what does this role depend on, and what depends on it?
cloudg deps arn:aws:iam::123456789012:role/app-role
# the full pipeline with scanners, one region to start
cloudg run -p aws --regions us-east-1
Credentials come from the usual places: env vars, a CLI profile with --profile, SSO, OIDC web identity in CI, or the instance role on cloud compute. For real accounts I'd give it a dedicated read-only role. The docs suggest one with the SecurityAudit and ViewOnlyAccess managed policies, rolled out with a StackSet when you're mapping a whole org.
The scanners are separate programs, not Python dependencies. install.sh (or install.bat on Windows) sets them up. The Docker image bundles Prowler, Checkov and Trivy; ScoutSuite isn't in it, so run that one separately and feed its report to cloudg ingest.
What you get back
Everything lands in ./reports. After cloudg run, open report.html first: it has a D3 topology view next to the findings table, and it works offline because Chart.js and D3 are embedded in the page.
| File | What it is |
|---|---|
report.html |
interactive report, topology plus findings |
findings.json |
all findings, assets, edges and compliance results |
topology.svg, topology.graphml
|
the graph as a picture and as GraphML |
ontology.ttl, ontology.jsonld
|
an RDF ontology you can query with SPARQL |
rag_chunks.jsonl |
retrieval-ready chunks for an LLM pipeline |
terraform/*.tf.json |
Terraform that recreates the live infrastructure, with an import_commands.sh to adopt it into state (off by default, --terraform) |
inventory-map.json |
the scanner-free map from cloudg map
|
inventory-dependencies.json |
shared dependencies, blast radius, cross-account edges and security coverage gaps |
The map and the scan don't have to happen together. You can map today, run the scanners next week, and merge them with cloudg map --findings ./reports/raw-findings.json. That gives you asset-map.json (each asset with its findings) and compliance-map.json (each framework with the assets it affects).
How it works
- Collection runs every provider at once with asyncio, looping over accounts and regions.
--regions alldiscovers the enabled regions for you. - Assets and network edges go into a directed NetworkX graph. A BFS from an internet node over network-flow edges finds what is actually exposed, and a blast radius score estimates what an attacker could reach from each node.
- Scanner findings are deduplicated in two passes. Within one scanner, it's by check ID and resource. Across scanners, two findings merge only when the normalised title and the check semantics (from a check-equivalence ruleset) both match.
- Merged findings are rescored against CVSS and mapped to compliance controls.
- The same inventory feeds the exports: the RDF ontology with 64 typed relations, the RAG chunks (split per asset, per Louvain community and per relation domain), and Terraform for 25+ asset types.
One design choice worth calling out: a control is reported as FAIL when a finding maps to it, and as PASS only when Prowler's passing checks map to it. Controls nothing assessed are left out of the report. I didn't want a compliance view that looks green because nobody checked.
Mapping a whole AWS Organization means thousands of API calls per account, so every call goes through a shared rate limiter. Limits are set per service at or below what each provider publishes, Retry-After is honoured, and an API that stays throttled gets skipped by a circuit breaker for a while. The map doesn't fail over it; the coverage output marks that service as throttled.
Using it from an AI agent
Since 0.6.0, cloudg can run as an MCP server, so Claude Desktop, Claude Code, Cursor or VS Code can query a map you've already made. It exposes 74 tools (find_assets, attack_paths, blast_radius, compliance_gaps and so on), plus resources and prompts such as security_posture_review.
pip install "cloudg[mcp]"
cloudg mcp serve --dataset prod=./reports/inventory-map.json
# print the config block for your client
cloudg mcp config --client claude-desktop --dataset prod=./reports/inventory-map.json
Handing cloud inventory to an LLM has its own risks, so every request goes through a policy. Resource names and tags are attacker-controllable text, which makes them a prompt injection path. The default standard profile redacts secrets and fences that text off. The strict profile also pseudonymises account IDs, ARNs, IPs and names, and hides the tools that call the cloud.
Where it's at
It's beta. AWS has the deepest coverage; Azure and GCP lean more on Resource Graph and Cloud Asset Inventory. The Terraform export covers 25+ asset types, not everything. If you're on 0.6.0, upgrade: 0.6.1 fixed report.html so markup in scanner output can no longer run in the page.
If you point it at a real account, I'd like to hear where the map is wrong or missing something. Issues and PRs are open, and custom collectors and scanners can be added as plugins through entry points without touching the core.
Top comments (0)