Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Perform comprehensive cloud asset inventory and relationship mapping using Cartography to build a Neo4j security graph of infrastructure assets, IAM permissions, and attack paths across AWS, GCP, and Azure.
.claude/skills/performing-cloud-asset-inventory-with-cartography/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | — | — |
| case-10 | ✗→✓ | ▲ Improved | — | — |
| case-03 | ✗→✓ | ▲ Improved | — | — |
| case-15 | ✗→✓ | ▲ Improved | — | — |
| case-04 | ✗→✓ | ▲ Improved | — | — |
Cartography is a CNCF sandbox project (originally created at Lyft) that consolidates infrastructure assets and their relationships into a Neo4j graph database. It queries cloud APIs to discover resources, maps relationships between them, and enables security teams to identify attack paths, generate asset reports, and find areas for security improvement. The graph model reveals hidden connections such as IAM permission chains, network paths, and cross-account trust relationships.
bash# Install Cartography pip install cartography # Verify installation cartography --help
bashdocker run -d \ --name neo4j \ -p 7474:7474 -p 7687:7687 \ -e NEO4J_AUTH=neo4j/changethispassword \ -e NEO4J_PLUGINS='["apoc"]' \ -v neo4j_data:/data \ neo4j:5-community
bash# Sync AWS account data to Neo4j cartography \ --neo4j-uri bolt://localhost:7687 \ --neo4j-user neo4j \ --neo4j-password-env-var NEO4J_PASSWORD
bashcartography \ --neo4j-uri bolt://localhost:7687 \ --neo4j-user neo4j \ --neo4j-password-env-var NEO4J_PASSWORD \ --aws-sync-all-profiles
bashcartography \ --neo4j-uri bolt://localhost:7687 \ --neo4j-user neo4j \ --neo4j-password-env-var NEO4J_PASSWORD \ --gcp-requested-syncs compute iam storage
cypherMATCH (b:S3Bucket) WHERE b.anonymous_access = true OR b.anonymous_actions IS NOT NULL RETURN b.name, b.anonymous_actions, b.region, b.arn ORDER BY b.name
cypherMATCH (user:AWSUser)-[:POLICY]->(policy:AWSPolicy) WHERE policy.name = 'AdministratorAccess' OR policy.arn CONTAINS 'AdministratorAccess' RETURN user.name, user.arn, policy.name, user.password_last_used
cypherMATCH (instance:EC2Instance)-[:MEMBER_OF_EC2_SECURITY_GROUP]->(sg:EC2SecurityGroup) -[:MEMBER_OF_EC2_SECURITY_GROUP_RULE]->(rule:IpRule) WHERE rule.fromport <= 22 AND rule.toport >= 22 AND rule.protocol IN ['tcp', '-1'] AND '0.0.0.0/0' IN rule.ipranges RETURN instance.instanceid, instance.publicipaddress, sg.groupid, sg.name
cypherMATCH (role:AWSRole)-[:TRUSTS_AWS_PRINCIPAL]->(principal:AWSPrincipal) WHERE principal.arn CONTAINS ':root' AND NOT principal.arn CONTAINS role.accountid RETURN role.arn, role.name, principal.arn AS trusted_account ORDER BY role.name
cypherMATCH path = (instance:EC2Instance)-[:STS_ASSUME_ROLE_ALLOWS|MEMBER_OF_EC2_SECURITY_GROUP| POLICY|INSTANCE_PROFILE*1..5]->(bucket:S3Bucket) WHERE instance.publicipaddress IS NOT NULL AND bucket.name CONTAINS 'sensitive' RETURN path LIMIT 25
cypherMATCH (role:AWSRole) WHERE role.last_used IS NULL OR role.last_used < datetime().epochMillis - (90 * 24 * 60 * 60 * 1000) RETURN role.name, role.arn, role.last_used ORDER BY role.last_used
cypherMATCH (func:AWSLambda)-[:STS_ASSUME_ROLE_ALLOWS]->(role:AWSRole)-[:POLICY]->(policy:AWSPolicy) WHERE policy.name = 'AdministratorAccess' RETURN func.name, func.arn, role.name, policy.name
cypherMATCH (vpc:AWSVpc)-[:RESOURCE]->(subnet:EC2Subnet)-[:MEMBER_OF_SUBNET]->(instance:EC2Instance) WHERE instance.publicipaddress IS NOT NULL RETURN vpc.id, subnet.subnetid, subnet.cidr_block, instance.instanceid, instance.publicipaddress, instance.state
bash# Add to crontab - sync every 6 hours 0 */6 * * * /usr/local/bin/cartography \ --neo4j-uri bolt://localhost:7687 \ --neo4j-user neo4j \ --neo4j-password-env-var NEO4J_PASSWORD \ >> /var/log/cartography/sync.log 2>&1
yamlversion: '3.8' services: neo4j: image: neo4j:5-community ports: - "7474:7474" - "7687:7687" environment: NEO4J_AUTH: neo4j/securepwd123 NEO4J_PLUGINS: '["apoc"]' NEO4J_dbms_memory_heap_max__size: 4G volumes: - neo4j_data:/data cartography: image: ghcr.io/cartography-cncf/cartography:latest depends_on: - neo4j environment: NEO4J_PASSWORD: securepwd123 AWS_DEFAULT_REGION: us-east-1 command: > --neo4j-uri bolt://neo4j:7687 --neo4j-user neo4j --neo4j-password-env-var NEO4J_PASSWORD volumes: neo4j_data:
AWSAccount, GCPProject, AzureSubscriptionEC2Instance, S3Bucket, RDSInstance, AWSLambdaAWSUser, AWSRole, AWSGroup, AWSPolicyEC2SecurityGroup, EC2Subnet, AWSVpcGCPInstance, GCSBucket, GCPRoleRESOURCE: Account owns resourcePOLICY: Principal has policy attachedSTS_ASSUME_ROLE_ALLOWS: Principal can assume roleMEMBER_OF_EC2_SECURITY_GROUP: Instance belongs to SGTRUSTS_AWS_PRINCIPAL: Cross-account trust| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +50 percentage points is the difference between those two pass rates over the 21 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.