Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com>
AWS Inventory Connectivity Graph
A community-contributed tool that generates interactive connectivity graphs from Prowler AWS scans, visualizing relationships between AWS resources with zero additional API calls.
Overview
This tool extends Prowler by producing two artifacts after a scan completes:
<output>.inventory.json– Machine-readable graph (nodes + edges)<output>.inventory.html– Interactive D3.js force-directed visualization
Why?
Prowler's existing outputs (CSV, ASFF, OCSF, HTML) report individual check findings but provide no cross-service topology view. Security engineers need to understand how resources are connected—which Lambda functions sit inside which VPC, which IAM roles can be assumed by which services, which event sources trigger which functions—before they can reason about attack paths, blast-radius, or lateral-movement risk.
This tool fills that gap by building a connectivity graph from the service clients that are already loaded during a Prowler scan.
Features
Supported AWS Services
The tool currently extracts connectivity information from:
- Lambda – Functions, VPC/subnet/SG edges, event source mappings, layers, DLQ, KMS
- EC2 – Instances, security groups, subnet/VPC edges
- VPC – VPCs, subnets, peering connections
- RDS – DB instances, VPC/SG/cluster/KMS edges
- ELBv2 – ALB/NLB load balancers, SG and VPC edges
- S3 – Buckets, replication targets, logging buckets, KMS keys
- IAM – Roles, trust-relationship edges (who can assume what)
Edge Semantic Types
Edges are typed for downstream filtering and attack-path analysis:
network– Resources share a network path (VPC/subnet/SG)iam– IAM trust or permission relationshiptriggers– One resource can invoke another (event source → Lambda)data_flow– Data is written/read (Lambda → SQS dead-letter queue)depends_on– Soft dependency (Lambda layer, subnet belongs to VPC)routes_to– Traffic routing (LB → target)replicates_to– S3 replicationencrypts– KMS key encrypts the resourcelogs_to– Logging relationship
Interactive HTML Graph Features
- Force-directed layout with drag-and-drop node pinning
- Zoom / pan (mouse wheel + click-drag on background)
- Per-service color-coded nodes with a legend
- Hover tooltips showing ARN + all metadata properties
- Service filter dropdown (show only Lambda, EC2, RDS, etc.)
- Adjustable link-distance and charge-strength physics sliders
- Edge labels on every arrow
Installation
Prerequisites
- Python 3.9.1 or higher
- Prowler installed and configured (see Prowler documentation)
Setup
- Clone or download this directory to your local machine
- Ensure Prowler is installed and working
- No additional dependencies required beyond Prowler's existing requirements
Usage
Basic Usage
Run Prowler with your desired checks, then use the inventory graph script:
# Run Prowler scan (example)
prowler aws --output-formats csv
# Generate inventory graph from the scan
python contrib/inventory-graph/inventory_graph.py --output-directory ./output
Command-Line Options
python contrib/inventory-graph/inventory_graph.py [OPTIONS]
Options:
--output-directory DIR Directory to save output files (default: ./output)
--output-filename NAME Base filename without extension (default: prowler-inventory-<timestamp>)
--help Show this help message and exit
Example Workflow
# 1. Run a Prowler scan on your AWS account
prowler aws --profile my-aws-profile --output-formats csv html
# 2. Generate the inventory graph
python contrib/inventory-graph/inventory_graph.py \
--output-directory ./output \
--output-filename my-aws-inventory
# 3. Open the HTML file in your browser
open output/my-aws-inventory.inventory.html
Integration with Prowler Scans
The tool reads from already-loaded AWS service clients in memory (via sys.modules). This means:
- Zero extra AWS API calls – Uses data already collected during the Prowler scan
- Graceful degradation – Services not scanned are silently skipped
- Flexible – Works with any subset of Prowler checks
Output Files
JSON Output (*.inventory.json)
Machine-readable graph structure:
{
"generated_at": "2026-03-19T12:34:56Z",
"nodes": [
{
"id": "arn:aws:lambda:us-east-1:123456789012:function:my-function",
"type": "lambda_function",
"name": "my-function",
"service": "lambda",
"region": "us-east-1",
"account_id": "123456789012",
"properties": {
"runtime": "python3.9",
"vpc_id": "vpc-abc123"
}
}
],
"edges": [
{
"source_id": "arn:aws:lambda:...",
"target_id": "arn:aws:ec2:...:vpc/vpc-abc123",
"edge_type": "network",
"label": "in-vpc"
}
],
"stats": {
"node_count": 42,
"edge_count": 87
}
}
HTML Output (*.inventory.html)
Self-contained interactive visualization that opens in any modern browser. No server or build step required.
Architecture
Design Decisions
| Decision | Rationale |
|---|---|
| Read from sys.modules | Zero extra AWS API calls; services not scanned are silently skipped |
| Self-contained HTML | D3.js v7 via CDN; no server, no build step; opens in any browser |
| One extractor per service | Each extractor is independently testable; adding a new service = one new file + one line in the registry |
| Typed edges | Semantic types allow downstream consumers (attack-path tools, Neo4j import) to filter by relationship class |
Project Structure
contrib/inventory-graph/
├── README.md # This file
├── inventory_graph.py # Main entry point script
├── lib/
│ ├── __init__.py
│ ├── models.py # ResourceNode, ResourceEdge, ConnectivityGraph dataclasses
│ ├── graph_builder.py # Reads loaded service clients from sys.modules
│ ├── inventory_output.py # write_json(), write_html()
│ └── extractors/
│ ├── __init__.py
│ ├── lambda_extractor.py # Lambda functions → VPC/subnet/SG/event-sources/layers/DLQ/KMS
│ ├── ec2_extractor.py # EC2 instances + security groups → subnet/VPC
│ ├── vpc_extractor.py # VPCs, subnets, peering connections
│ ├── rds_extractor.py # RDS instances → VPC/SG/cluster/KMS
│ ├── elbv2_extractor.py # ALB/NLB load balancers → SG/VPC
│ ├── s3_extractor.py # S3 buckets → replication targets/logging buckets/KMS keys
│ └── iam_extractor.py # IAM roles + trust-relationship edges
└── examples/
└── sample_output.html # Example output (optional)
Testing
Smoke Test (No AWS Credentials Needed)
import sys
from unittest.mock import MagicMock
# Wire a fake Lambda client
mock_module = MagicMock()
mock_fn = MagicMock()
mock_fn.arn = "arn:aws:lambda:us-east-1:123:function:test"
mock_fn.name = "test"
mock_fn.region = "us-east-1"
mock_fn.vpc_id = "vpc-abc"
mock_fn.security_groups = ["sg-111"]
mock_fn.subnet_ids = {"subnet-aaa"}
mock_fn.environment = None
mock_fn.kms_key_arn = None
mock_fn.layers = []
mock_fn.dead_letter_config = None
mock_fn.event_source_mappings = []
mock_module.awslambda_client.functions = {mock_fn.arn: mock_fn}
mock_module.awslambda_client.audited_account = "123"
sys.modules["prowler.providers.aws.services.awslambda.awslambda_client"] = mock_module
from contrib.inventory_graph.lib.graph_builder import build_graph
from contrib.inventory_graph.lib.inventory_output import write_json, write_html
graph = build_graph()
write_json(graph, "/tmp/test.inventory.json")
write_html(graph, "/tmp/test.inventory.html")
# Open /tmp/test.inventory.html in a browser
Extending
Adding a New Service
- Create a new extractor file in
lib/extractors/(e.g.,dynamodb_extractor.py) - Implement the
extract(client)function that returns(nodes, edges) - Register it in
lib/graph_builder.pyin the_SERVICE_REGISTRYtuple
Example extractor template:
from typing import List, Tuple
from prowler.lib.outputs.inventory.models import ResourceNode, ResourceEdge
def extract(client) -> Tuple[List[ResourceNode], List[ResourceEdge]]:
"""Extract DynamoDB tables and their relationships."""
nodes = []
edges = []
for table in client.tables:
nodes.append(
ResourceNode(
id=table.arn,
type="dynamodb_table",
name=table.name,
service="dynamodb",
region=table.region,
account_id=client.audited_account,
properties={"billing_mode": table.billing_mode}
)
)
# Add edges for KMS encryption, streams, etc.
if table.kms_key_arn:
edges.append(
ResourceEdge(
source_id=table.kms_key_arn,
target_id=table.arn,
edge_type="encrypts",
label="encrypts"
)
)
return nodes, edges
Troubleshooting
No nodes discovered
Problem: The tool reports "no nodes discovered" after running.
Solution: Ensure you've run a Prowler scan first. The tool reads from in-memory service clients loaded during the scan. If no services were scanned, no nodes will be discovered.
Missing services in the graph
Problem: Some AWS services are not appearing in the graph.
Solution: The tool only includes services that have been scanned by Prowler. Run Prowler with the services you want to include, or run without service filters to scan all available services.
HTML file doesn't display properly
Problem: The HTML visualization doesn't load or shows errors.
Solution:
- Ensure you're opening the file in a modern browser (Chrome, Firefox, Safari, Edge)
- Check your browser's console for JavaScript errors
- Verify the file was generated completely (check file size > 0)
- The HTML requires internet access to load D3.js from CDN
Roadmap
Potential future enhancements:
- Support for additional AWS services (DynamoDB, SQS, SNS, etc.)
- Export to Neo4j / Cartography format
- Attack path analysis integration
- Multi-account/multi-region aggregation
- Custom edge type filtering in HTML UI
- Graph diff between two scans
Contributing
This is a community contribution. If you'd like to enhance it:
- Fork the Prowler repository
- Make your changes in
contrib/inventory-graph/ - Test thoroughly
- Submit a pull request with a clear description
License
This tool is part of the Prowler project and is licensed under the Apache License 2.0.
Credits
- Author: @sandiyochristan
- Related PR: #10382
- Prowler Project: prowler-cloud/prowler
Support
For issues or questions:
- Open an issue in the Prowler repository
- Join the Prowler Community Slack
- Tag your issue with
contrib:inventory-graph