cartography 0.102.0rc2__py3-none-any.whl → 0.103.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

Files changed (297) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +2 -2
  3. cartography/cli.py +376 -249
  4. cartography/client/core/tx.py +39 -18
  5. cartography/config.py +28 -0
  6. cartography/driftdetect/__main__.py +1 -2
  7. cartography/driftdetect/add_shortcut.py +10 -2
  8. cartography/driftdetect/cli.py +71 -75
  9. cartography/driftdetect/detect_deviations.py +7 -3
  10. cartography/driftdetect/get_states.py +20 -8
  11. cartography/driftdetect/model.py +5 -5
  12. cartography/driftdetect/serializers.py +8 -6
  13. cartography/driftdetect/storage.py +2 -2
  14. cartography/graph/cleanupbuilder.py +35 -15
  15. cartography/graph/job.py +46 -17
  16. cartography/graph/querybuilder.py +165 -80
  17. cartography/graph/statement.py +35 -26
  18. cartography/intel/analysis.py +4 -1
  19. cartography/intel/aws/__init__.py +114 -55
  20. cartography/intel/aws/apigateway.py +134 -63
  21. cartography/intel/aws/cloudtrail.py +127 -0
  22. cartography/intel/aws/cloudwatch.py +93 -0
  23. cartography/intel/aws/config.py +56 -20
  24. cartography/intel/aws/dynamodb.py +108 -40
  25. cartography/intel/aws/ec2/__init__.py +2 -2
  26. cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
  27. cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
  28. cartography/intel/aws/ec2/images.py +49 -20
  29. cartography/intel/aws/ec2/instances.py +234 -136
  30. cartography/intel/aws/ec2/internet_gateways.py +40 -11
  31. cartography/intel/aws/ec2/key_pairs.py +44 -20
  32. cartography/intel/aws/ec2/launch_templates.py +101 -59
  33. cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
  34. cartography/intel/aws/ec2/load_balancers.py +82 -42
  35. cartography/intel/aws/ec2/network_acls.py +89 -65
  36. cartography/intel/aws/ec2/network_interfaces.py +146 -87
  37. cartography/intel/aws/ec2/reserved_instances.py +45 -16
  38. cartography/intel/aws/ec2/route_tables.py +138 -98
  39. cartography/intel/aws/ec2/security_groups.py +71 -21
  40. cartography/intel/aws/ec2/snapshots.py +61 -22
  41. cartography/intel/aws/ec2/subnets.py +54 -18
  42. cartography/intel/aws/ec2/tgw.py +100 -34
  43. cartography/intel/aws/ec2/util.py +1 -1
  44. cartography/intel/aws/ec2/volumes.py +69 -41
  45. cartography/intel/aws/ec2/vpc.py +37 -12
  46. cartography/intel/aws/ec2/vpc_peerings.py +83 -24
  47. cartography/intel/aws/ecr.py +88 -32
  48. cartography/intel/aws/ecs.py +83 -47
  49. cartography/intel/aws/efs.py +93 -0
  50. cartography/intel/aws/eks.py +55 -29
  51. cartography/intel/aws/elasticache.py +42 -18
  52. cartography/intel/aws/elasticsearch.py +57 -20
  53. cartography/intel/aws/emr.py +61 -23
  54. cartography/intel/aws/iam.py +401 -145
  55. cartography/intel/aws/iam_instance_profiles.py +22 -22
  56. cartography/intel/aws/identitycenter.py +71 -37
  57. cartography/intel/aws/inspector.py +159 -89
  58. cartography/intel/aws/kms.py +92 -38
  59. cartography/intel/aws/lambda_function.py +103 -34
  60. cartography/intel/aws/organizations.py +30 -10
  61. cartography/intel/aws/permission_relationships.py +133 -51
  62. cartography/intel/aws/rds.py +249 -85
  63. cartography/intel/aws/redshift.py +107 -46
  64. cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
  65. cartography/intel/aws/resources.py +57 -46
  66. cartography/intel/aws/route53.py +108 -61
  67. cartography/intel/aws/s3.py +168 -83
  68. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  69. cartography/intel/aws/secretsmanager.py +24 -12
  70. cartography/intel/aws/securityhub.py +20 -9
  71. cartography/intel/aws/sns.py +166 -0
  72. cartography/intel/aws/sqs.py +60 -28
  73. cartography/intel/aws/ssm.py +70 -30
  74. cartography/intel/aws/util/arns.py +7 -7
  75. cartography/intel/aws/util/common.py +31 -4
  76. cartography/intel/azure/__init__.py +78 -19
  77. cartography/intel/azure/compute.py +101 -27
  78. cartography/intel/azure/cosmosdb.py +496 -170
  79. cartography/intel/azure/sql.py +296 -105
  80. cartography/intel/azure/storage.py +322 -113
  81. cartography/intel/azure/subscription.py +39 -23
  82. cartography/intel/azure/tenant.py +13 -4
  83. cartography/intel/azure/util/credentials.py +95 -55
  84. cartography/intel/bigfix/__init__.py +2 -2
  85. cartography/intel/bigfix/computers.py +93 -65
  86. cartography/intel/cloudflare/__init__.py +74 -0
  87. cartography/intel/cloudflare/accounts.py +57 -0
  88. cartography/intel/cloudflare/dnsrecords.py +64 -0
  89. cartography/intel/cloudflare/members.py +75 -0
  90. cartography/intel/cloudflare/roles.py +65 -0
  91. cartography/intel/cloudflare/zones.py +64 -0
  92. cartography/intel/create_indexes.py +3 -2
  93. cartography/intel/crowdstrike/__init__.py +11 -9
  94. cartography/intel/crowdstrike/endpoints.py +5 -1
  95. cartography/intel/crowdstrike/spotlight.py +8 -3
  96. cartography/intel/cve/__init__.py +46 -13
  97. cartography/intel/cve/feed.py +48 -12
  98. cartography/intel/digitalocean/__init__.py +22 -13
  99. cartography/intel/digitalocean/compute.py +75 -108
  100. cartography/intel/digitalocean/management.py +44 -80
  101. cartography/intel/digitalocean/platform.py +48 -43
  102. cartography/intel/dns.py +36 -10
  103. cartography/intel/duo/__init__.py +21 -16
  104. cartography/intel/duo/api_host.py +14 -9
  105. cartography/intel/duo/endpoints.py +50 -45
  106. cartography/intel/duo/groups.py +18 -14
  107. cartography/intel/duo/phones.py +37 -34
  108. cartography/intel/duo/tokens.py +26 -23
  109. cartography/intel/duo/users.py +54 -50
  110. cartography/intel/duo/web_authn_credentials.py +30 -25
  111. cartography/intel/entra/__init__.py +25 -7
  112. cartography/intel/entra/ou.py +112 -0
  113. cartography/intel/entra/users.py +69 -63
  114. cartography/intel/gcp/__init__.py +185 -49
  115. cartography/intel/gcp/compute.py +418 -231
  116. cartography/intel/gcp/crm.py +96 -43
  117. cartography/intel/gcp/dns.py +60 -19
  118. cartography/intel/gcp/gke.py +72 -38
  119. cartography/intel/gcp/iam.py +61 -41
  120. cartography/intel/gcp/storage.py +84 -55
  121. cartography/intel/github/__init__.py +13 -11
  122. cartography/intel/github/repos.py +270 -137
  123. cartography/intel/github/teams.py +170 -88
  124. cartography/intel/github/users.py +70 -39
  125. cartography/intel/github/util.py +36 -34
  126. cartography/intel/gsuite/__init__.py +47 -26
  127. cartography/intel/gsuite/api.py +73 -30
  128. cartography/intel/jamf/__init__.py +19 -1
  129. cartography/intel/jamf/computers.py +30 -7
  130. cartography/intel/jamf/util.py +7 -2
  131. cartography/intel/kandji/__init__.py +6 -3
  132. cartography/intel/kandji/devices.py +14 -8
  133. cartography/intel/kubernetes/namespaces.py +7 -4
  134. cartography/intel/kubernetes/pods.py +7 -4
  135. cartography/intel/kubernetes/services.py +8 -4
  136. cartography/intel/lastpass/__init__.py +2 -2
  137. cartography/intel/lastpass/users.py +23 -12
  138. cartography/intel/oci/__init__.py +44 -11
  139. cartography/intel/oci/iam.py +134 -38
  140. cartography/intel/oci/organizations.py +13 -6
  141. cartography/intel/oci/utils.py +43 -20
  142. cartography/intel/okta/__init__.py +66 -15
  143. cartography/intel/okta/applications.py +42 -20
  144. cartography/intel/okta/awssaml.py +93 -33
  145. cartography/intel/okta/factors.py +16 -4
  146. cartography/intel/okta/groups.py +56 -29
  147. cartography/intel/okta/organization.py +5 -1
  148. cartography/intel/okta/origins.py +6 -2
  149. cartography/intel/okta/roles.py +15 -5
  150. cartography/intel/okta/users.py +20 -8
  151. cartography/intel/okta/utils.py +6 -4
  152. cartography/intel/openai/__init__.py +86 -0
  153. cartography/intel/openai/adminapikeys.py +90 -0
  154. cartography/intel/openai/apikeys.py +96 -0
  155. cartography/intel/openai/projects.py +94 -0
  156. cartography/intel/openai/serviceaccounts.py +82 -0
  157. cartography/intel/openai/users.py +78 -0
  158. cartography/intel/openai/util.py +29 -0
  159. cartography/intel/pagerduty/__init__.py +8 -7
  160. cartography/intel/pagerduty/escalation_policies.py +18 -6
  161. cartography/intel/pagerduty/schedules.py +12 -4
  162. cartography/intel/pagerduty/services.py +11 -4
  163. cartography/intel/pagerduty/teams.py +8 -3
  164. cartography/intel/pagerduty/users.py +3 -1
  165. cartography/intel/pagerduty/vendors.py +3 -1
  166. cartography/intel/semgrep/__init__.py +24 -6
  167. cartography/intel/semgrep/dependencies.py +50 -28
  168. cartography/intel/semgrep/deployment.py +3 -1
  169. cartography/intel/semgrep/findings.py +42 -18
  170. cartography/intel/snipeit/__init__.py +17 -3
  171. cartography/intel/snipeit/asset.py +12 -6
  172. cartography/intel/snipeit/user.py +8 -5
  173. cartography/intel/snipeit/util.py +9 -4
  174. cartography/intel/tailscale/__init__.py +77 -0
  175. cartography/intel/tailscale/acls.py +146 -0
  176. cartography/intel/tailscale/devices.py +127 -0
  177. cartography/intel/tailscale/postureintegrations.py +81 -0
  178. cartography/intel/tailscale/tailnets.py +76 -0
  179. cartography/intel/tailscale/users.py +80 -0
  180. cartography/intel/tailscale/utils.py +132 -0
  181. cartography/models/aws/apigateway.py +21 -17
  182. cartography/models/aws/apigatewaycertificate.py +28 -22
  183. cartography/models/aws/apigatewayresource.py +28 -20
  184. cartography/models/aws/apigatewaystage.py +33 -25
  185. cartography/models/aws/cloudtrail/__init__.py +0 -0
  186. cartography/models/aws/cloudtrail/trail.py +61 -0
  187. cartography/models/aws/cloudwatch/__init__.py +0 -0
  188. cartography/models/aws/cloudwatch/loggroup.py +52 -0
  189. cartography/models/aws/dynamodb/gsi.py +30 -22
  190. cartography/models/aws/dynamodb/tables.py +25 -17
  191. cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
  192. cartography/models/aws/ec2/images.py +36 -34
  193. cartography/models/aws/ec2/instances.py +51 -45
  194. cartography/models/aws/ec2/keypair.py +21 -16
  195. cartography/models/aws/ec2/keypair_instance.py +28 -21
  196. cartography/models/aws/ec2/launch_configurations.py +30 -26
  197. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  198. cartography/models/aws/ec2/launch_templates.py +21 -17
  199. cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
  200. cartography/models/aws/ec2/load_balancers.py +47 -37
  201. cartography/models/aws/ec2/network_acl_rules.py +38 -30
  202. cartography/models/aws/ec2/network_acls.py +38 -29
  203. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  204. cartography/models/aws/ec2/networkinterfaces.py +53 -37
  205. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  206. cartography/models/aws/ec2/reservations.py +18 -14
  207. cartography/models/aws/ec2/route_table_associations.py +44 -34
  208. cartography/models/aws/ec2/route_tables.py +50 -43
  209. cartography/models/aws/ec2/routes.py +45 -37
  210. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  211. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  212. cartography/models/aws/ec2/subnet_instance.py +24 -19
  213. cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
  214. cartography/models/aws/ec2/volumes.py +47 -40
  215. cartography/models/aws/efs/__init__.py +0 -0
  216. cartography/models/aws/efs/mount_target.py +52 -0
  217. cartography/models/aws/eks/clusters.py +23 -21
  218. cartography/models/aws/emr.py +32 -30
  219. cartography/models/aws/iam/instanceprofile.py +33 -24
  220. cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
  221. cartography/models/aws/identitycenter/awspermissionset.py +37 -29
  222. cartography/models/aws/identitycenter/awsssouser.py +23 -21
  223. cartography/models/aws/inspector/findings.py +77 -65
  224. cartography/models/aws/inspector/packages.py +35 -29
  225. cartography/models/aws/s3/__init__.py +0 -0
  226. cartography/models/aws/s3/account_public_access_block.py +51 -0
  227. cartography/models/aws/sns/__init__.py +0 -0
  228. cartography/models/aws/sns/topic.py +50 -0
  229. cartography/models/aws/ssm/instance_information.py +51 -39
  230. cartography/models/aws/ssm/instance_patch.py +32 -26
  231. cartography/models/bigfix/bigfix_computer.py +42 -38
  232. cartography/models/bigfix/bigfix_root.py +3 -3
  233. cartography/models/cloudflare/__init__.py +0 -0
  234. cartography/models/cloudflare/account.py +25 -0
  235. cartography/models/cloudflare/dnsrecord.py +55 -0
  236. cartography/models/cloudflare/member.py +82 -0
  237. cartography/models/cloudflare/role.py +44 -0
  238. cartography/models/cloudflare/zone.py +59 -0
  239. cartography/models/core/common.py +12 -10
  240. cartography/models/core/nodes.py +5 -2
  241. cartography/models/core/relationships.py +14 -6
  242. cartography/models/crowdstrike/hosts.py +37 -35
  243. cartography/models/cve/cve.py +34 -32
  244. cartography/models/cve/cve_feed.py +6 -6
  245. cartography/models/digitalocean/__init__.py +0 -0
  246. cartography/models/digitalocean/account.py +21 -0
  247. cartography/models/digitalocean/droplet.py +56 -0
  248. cartography/models/digitalocean/project.py +48 -0
  249. cartography/models/duo/api_host.py +3 -3
  250. cartography/models/duo/endpoint.py +43 -41
  251. cartography/models/duo/group.py +14 -14
  252. cartography/models/duo/phone.py +27 -27
  253. cartography/models/duo/token.py +16 -16
  254. cartography/models/duo/user.py +46 -44
  255. cartography/models/duo/web_authn_credential.py +27 -19
  256. cartography/models/entra/ou.py +48 -0
  257. cartography/models/entra/tenant.py +24 -18
  258. cartography/models/entra/user.py +64 -48
  259. cartography/models/gcp/iam.py +23 -23
  260. cartography/models/github/orgs.py +5 -4
  261. cartography/models/github/teams.py +37 -31
  262. cartography/models/github/users.py +34 -23
  263. cartography/models/kandji/device.py +22 -16
  264. cartography/models/kandji/tenant.py +6 -4
  265. cartography/models/lastpass/tenant.py +3 -3
  266. cartography/models/lastpass/user.py +32 -28
  267. cartography/models/openai/__init__.py +0 -0
  268. cartography/models/openai/adminapikey.py +90 -0
  269. cartography/models/openai/apikey.py +84 -0
  270. cartography/models/openai/organization.py +17 -0
  271. cartography/models/openai/project.py +70 -0
  272. cartography/models/openai/serviceaccount.py +50 -0
  273. cartography/models/openai/user.py +49 -0
  274. cartography/models/semgrep/dependencies.py +36 -24
  275. cartography/models/semgrep/deployment.py +5 -5
  276. cartography/models/semgrep/findings.py +58 -42
  277. cartography/models/semgrep/locations.py +27 -21
  278. cartography/models/snipeit/asset.py +30 -21
  279. cartography/models/snipeit/tenant.py +6 -4
  280. cartography/models/snipeit/user.py +19 -12
  281. cartography/models/tailscale/__init__.py +0 -0
  282. cartography/models/tailscale/device.py +95 -0
  283. cartography/models/tailscale/group.py +86 -0
  284. cartography/models/tailscale/postureintegration.py +58 -0
  285. cartography/models/tailscale/tag.py +102 -0
  286. cartography/models/tailscale/tailnet.py +29 -0
  287. cartography/models/tailscale/user.py +52 -0
  288. cartography/stats.py +3 -3
  289. cartography/sync.py +113 -31
  290. cartography/util.py +84 -62
  291. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/METADATA +8 -15
  292. cartography-0.103.0.dist-info/RECORD +442 -0
  293. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
  294. cartography-0.102.0rc2.dist-info/RECORD +0 -381
  295. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
  296. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
  297. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,9 @@ import neo4j
7
7
  from cartography.client.core.tx import load
8
8
  from cartography.graph.job import GraphJob
9
9
  from cartography.intel.aws.ec2.util import get_botocore_config
10
- from cartography.models.aws.ec2.route_table_associations import RouteTableAssociationSchema
10
+ from cartography.models.aws.ec2.route_table_associations import (
11
+ RouteTableAssociationSchema,
12
+ )
11
13
  from cartography.models.aws.ec2.route_tables import RouteTableSchema
12
14
  from cartography.models.aws.ec2.routes import RouteSchema
13
15
  from cartography.util import aws_handle_regions
@@ -16,7 +18,9 @@ from cartography.util import timeit
16
18
  logger = logging.getLogger(__name__)
17
19
 
18
20
 
19
- def _get_route_id_and_target(route_table_id: str, route: dict[str, Any]) -> tuple[str, str | None]:
21
+ def _get_route_id_and_target(
22
+ route_table_id: str, route: dict[str, Any]
23
+ ) -> tuple[str, str | None]:
20
24
  """
21
25
  Generate a unique identifier for an AWS EC2 route and return the target of the route
22
26
  regardless of its type.
@@ -29,18 +33,18 @@ def _get_route_id_and_target(route_table_id: str, route: dict[str, Any]) -> tupl
29
33
  A tuple containing the unique identifier for the route and the target of the route
30
34
  """
31
35
  route_target_keys = [
32
- 'DestinationCidrBlock',
33
- 'DestinationIpv6CidrBlock',
34
- 'GatewayId',
35
- 'InstanceId',
36
- 'NatGatewayId',
37
- 'TransitGatewayId',
38
- 'LocalGatewayId',
39
- 'CarrierGatewayId',
40
- 'NetworkInterfaceId',
41
- 'VpcPeeringConnectionId',
42
- 'EgressOnlyInternetGatewayId',
43
- 'CoreNetworkArn',
36
+ "DestinationCidrBlock",
37
+ "DestinationIpv6CidrBlock",
38
+ "GatewayId",
39
+ "InstanceId",
40
+ "NatGatewayId",
41
+ "TransitGatewayId",
42
+ "LocalGatewayId",
43
+ "CarrierGatewayId",
44
+ "NetworkInterfaceId",
45
+ "VpcPeeringConnectionId",
46
+ "EgressOnlyInternetGatewayId",
47
+ "CoreNetworkArn",
44
48
  ]
45
49
 
46
50
  # Start with the route table ID
@@ -63,23 +67,27 @@ def _get_route_id_and_target(route_table_id: str, route: dict[str, Any]) -> tupl
63
67
  "so that we can update the available keys.",
64
68
  )
65
69
 
66
- return '|'.join(parts), target
70
+ return "|".join(parts), target
67
71
 
68
72
 
69
73
  @timeit
70
74
  @aws_handle_regions
71
- def get_route_tables(boto3_session: boto3.session.Session, region: str) -> list[dict[str, Any]]:
72
- client = boto3_session.client('ec2', region_name=region, config=get_botocore_config())
73
- paginator = client.get_paginator('describe_route_tables')
75
+ def get_route_tables(
76
+ boto3_session: boto3.session.Session, region: str
77
+ ) -> list[dict[str, Any]]:
78
+ client = boto3_session.client(
79
+ "ec2", region_name=region, config=get_botocore_config()
80
+ )
81
+ paginator = client.get_paginator("describe_route_tables")
74
82
  route_tables: list[dict[str, Any]] = []
75
83
  for page in paginator.paginate():
76
- route_tables.extend(page['RouteTables'])
84
+ route_tables.extend(page["RouteTables"])
77
85
  return route_tables
78
86
 
79
87
 
80
88
  def _transform_route_table_associations(
81
- route_table_id: str,
82
- associations: list[dict[str, Any]],
89
+ route_table_id: str,
90
+ associations: list[dict[str, Any]],
83
91
  ) -> tuple[list[dict[str, Any]], bool]:
84
92
  """
85
93
  Transform route table association data into a format suitable for cartography ingestion.
@@ -96,29 +104,33 @@ def _transform_route_table_associations(
96
104
  transformed = []
97
105
  is_main = False
98
106
  for association in associations:
99
- if association.get('SubnetId'):
100
- target = association['SubnetId']
101
- elif association.get('GatewayId'):
102
- target = association['GatewayId']
107
+ if association.get("SubnetId"):
108
+ target = association["SubnetId"]
109
+ elif association.get("GatewayId"):
110
+ target = association["GatewayId"]
103
111
  else:
104
112
  is_main = True
105
- target = 'main'
113
+ target = "main"
106
114
 
107
115
  transformed_association = {
108
- 'id': association['RouteTableAssociationId'],
109
- 'route_table_id': route_table_id,
110
- 'subnet_id': association.get('SubnetId'),
111
- 'gateway_id': association.get('GatewayId'),
112
- 'main': association.get('Main', False),
113
- 'association_state': association.get('AssociationState', {}).get('State'),
114
- 'association_state_message': association.get('AssociationState', {}).get('Message'),
115
- '_target': target,
116
+ "id": association["RouteTableAssociationId"],
117
+ "route_table_id": route_table_id,
118
+ "subnet_id": association.get("SubnetId"),
119
+ "gateway_id": association.get("GatewayId"),
120
+ "main": association.get("Main", False),
121
+ "association_state": association.get("AssociationState", {}).get("State"),
122
+ "association_state_message": association.get("AssociationState", {}).get(
123
+ "Message"
124
+ ),
125
+ "_target": target,
116
126
  }
117
127
  transformed.append(transformed_association)
118
128
  return transformed, is_main
119
129
 
120
130
 
121
- def _transform_route_table_routes(route_table_id: str, routes: list[dict[str, Any]]) -> list[dict[str, Any]]:
131
+ def _transform_route_table_routes(
132
+ route_table_id: str, routes: list[dict[str, Any]]
133
+ ) -> list[dict[str, Any]]:
122
134
  """
123
135
  Transform route table route data into a format suitable for cartography ingestion.
124
136
 
@@ -134,32 +146,32 @@ def _transform_route_table_routes(route_table_id: str, routes: list[dict[str, An
134
146
  route_id, target = _get_route_id_and_target(route_table_id, route)
135
147
 
136
148
  transformed_route = {
137
- 'id': route_id,
138
- 'route_table_id': route_table_id,
139
- 'destination_cidr_block': route.get('DestinationCidrBlock'),
140
- 'destination_ipv6_cidr_block': route.get('DestinationIpv6CidrBlock'),
141
- 'gateway_id': route.get('GatewayId'),
142
- 'instance_id': route.get('InstanceId'),
143
- 'instance_owner_id': route.get('InstanceOwnerId'),
144
- 'nat_gateway_id': route.get('NatGatewayId'),
145
- 'transit_gateway_id': route.get('TransitGatewayId'),
146
- 'local_gateway_id': route.get('LocalGatewayId'),
147
- 'carrier_gateway_id': route.get('CarrierGatewayId'),
148
- 'network_interface_id': route.get('NetworkInterfaceId'),
149
- 'vpc_peering_connection_id': route.get('VpcPeeringConnectionId'),
150
- 'state': route.get('State'),
151
- 'origin': route.get('Origin'),
152
- 'core_network_arn': route.get('CoreNetworkArn'),
153
- 'destination_prefix_list_id': route.get('DestinationPrefixListId'),
154
- 'egress_only_internet_gateway_id': route.get('EgressOnlyInternetGatewayId'),
155
- '_target': target,
149
+ "id": route_id,
150
+ "route_table_id": route_table_id,
151
+ "destination_cidr_block": route.get("DestinationCidrBlock"),
152
+ "destination_ipv6_cidr_block": route.get("DestinationIpv6CidrBlock"),
153
+ "gateway_id": route.get("GatewayId"),
154
+ "instance_id": route.get("InstanceId"),
155
+ "instance_owner_id": route.get("InstanceOwnerId"),
156
+ "nat_gateway_id": route.get("NatGatewayId"),
157
+ "transit_gateway_id": route.get("TransitGatewayId"),
158
+ "local_gateway_id": route.get("LocalGatewayId"),
159
+ "carrier_gateway_id": route.get("CarrierGatewayId"),
160
+ "network_interface_id": route.get("NetworkInterfaceId"),
161
+ "vpc_peering_connection_id": route.get("VpcPeeringConnectionId"),
162
+ "state": route.get("State"),
163
+ "origin": route.get("Origin"),
164
+ "core_network_arn": route.get("CoreNetworkArn"),
165
+ "destination_prefix_list_id": route.get("DestinationPrefixListId"),
166
+ "egress_only_internet_gateway_id": route.get("EgressOnlyInternetGatewayId"),
167
+ "_target": target,
156
168
  }
157
169
  transformed.append(transformed_route)
158
170
  return transformed
159
171
 
160
172
 
161
173
  def transform_route_table_data(
162
- route_tables: list[dict[str, Any]],
174
+ route_tables: list[dict[str, Any]],
163
175
  ) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]:
164
176
  """
165
177
  Transform route table data into a format suitable for cartography ingestion.
@@ -175,31 +187,37 @@ def transform_route_table_data(
175
187
  route_data = []
176
188
 
177
189
  for rt in route_tables:
178
- route_table_id = rt['RouteTableId']
190
+ route_table_id = rt["RouteTableId"]
179
191
 
180
192
  # Transform routes
181
193
  current_routes = []
182
- if rt.get('Routes'):
183
- current_routes = _transform_route_table_routes(route_table_id, rt['Routes'])
194
+ if rt.get("Routes"):
195
+ current_routes = _transform_route_table_routes(route_table_id, rt["Routes"])
184
196
  route_data.extend(current_routes)
185
197
 
186
198
  # If the rt has a association marked with main=True, then it is the main route table for the VPC.
187
199
  is_main = False
188
200
  # Transform associations
189
- if rt.get('Associations'):
190
- associations, is_main = _transform_route_table_associations(route_table_id, rt['Associations'])
201
+ if rt.get("Associations"):
202
+ associations, is_main = _transform_route_table_associations(
203
+ route_table_id, rt["Associations"]
204
+ )
191
205
  association_data.extend(associations)
192
206
 
193
207
  transformed_rt = {
194
- 'id': route_table_id,
195
- 'route_table_id': route_table_id,
196
- 'owner_id': rt.get('OwnerId'),
197
- 'vpc_id': rt.get('VpcId'),
198
- 'VpnGatewayIds': [vgw['GatewayId'] for vgw in rt.get('PropagatingVgws', [])],
199
- 'RouteTableAssociationIds': [assoc['RouteTableAssociationId'] for assoc in rt.get('Associations', [])],
200
- 'RouteIds': [route['id'] for route in current_routes],
201
- 'tags': rt.get('Tags', []),
202
- 'main': is_main,
208
+ "id": route_table_id,
209
+ "route_table_id": route_table_id,
210
+ "owner_id": rt.get("OwnerId"),
211
+ "vpc_id": rt.get("VpcId"),
212
+ "VpnGatewayIds": [
213
+ vgw["GatewayId"] for vgw in rt.get("PropagatingVgws", [])
214
+ ],
215
+ "RouteTableAssociationIds": [
216
+ assoc["RouteTableAssociationId"] for assoc in rt.get("Associations", [])
217
+ ],
218
+ "RouteIds": [route["id"] for route in current_routes],
219
+ "tags": rt.get("Tags", []),
220
+ "main": is_main,
203
221
  }
204
222
  transformed_tables.append(transformed_rt)
205
223
 
@@ -208,11 +226,11 @@ def transform_route_table_data(
208
226
 
209
227
  @timeit
210
228
  def load_route_tables(
211
- neo4j_session: neo4j.Session,
212
- data: list[dict[str, Any]],
213
- region: str,
214
- current_aws_account_id: str,
215
- update_tag: int,
229
+ neo4j_session: neo4j.Session,
230
+ data: list[dict[str, Any]],
231
+ region: str,
232
+ current_aws_account_id: str,
233
+ update_tag: int,
216
234
  ) -> None:
217
235
  load(
218
236
  neo4j_session,
@@ -226,11 +244,11 @@ def load_route_tables(
226
244
 
227
245
  @timeit
228
246
  def load_route_table_associations(
229
- neo4j_session: neo4j.Session,
230
- data: list[dict[str, Any]],
231
- region: str,
232
- current_aws_account_id: str,
233
- update_tag: int,
247
+ neo4j_session: neo4j.Session,
248
+ data: list[dict[str, Any]],
249
+ region: str,
250
+ current_aws_account_id: str,
251
+ update_tag: int,
234
252
  ) -> None:
235
253
  load(
236
254
  neo4j_session,
@@ -244,11 +262,11 @@ def load_route_table_associations(
244
262
 
245
263
  @timeit
246
264
  def load_routes(
247
- neo4j_session: neo4j.Session,
248
- data: list[dict[str, Any]],
249
- region: str,
250
- current_aws_account_id: str,
251
- update_tag: int,
265
+ neo4j_session: neo4j.Session,
266
+ data: list[dict[str, Any]],
267
+ region: str,
268
+ current_aws_account_id: str,
269
+ update_tag: int,
252
270
  ) -> None:
253
271
  load(
254
272
  neo4j_session,
@@ -261,27 +279,49 @@ def load_routes(
261
279
 
262
280
 
263
281
  @timeit
264
- def cleanup(neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]) -> None:
282
+ def cleanup(
283
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
284
+ ) -> None:
265
285
  logger.debug("Running EC2 route tables cleanup")
266
- GraphJob.from_node_schema(RouteTableSchema(), common_job_parameters).run(neo4j_session)
286
+ GraphJob.from_node_schema(RouteTableSchema(), common_job_parameters).run(
287
+ neo4j_session
288
+ )
267
289
  GraphJob.from_node_schema(RouteSchema(), common_job_parameters).run(neo4j_session)
268
- GraphJob.from_node_schema(RouteTableAssociationSchema(), common_job_parameters).run(neo4j_session)
290
+ GraphJob.from_node_schema(RouteTableAssociationSchema(), common_job_parameters).run(
291
+ neo4j_session
292
+ )
269
293
 
270
294
 
271
295
  @timeit
272
296
  def sync_route_tables(
273
- neo4j_session: neo4j.Session,
274
- boto3_session: boto3.session.Session,
275
- regions: list[str],
276
- current_aws_account_id: str,
277
- update_tag: int,
278
- common_job_parameters: dict[str, Any],
297
+ neo4j_session: neo4j.Session,
298
+ boto3_session: boto3.session.Session,
299
+ regions: list[str],
300
+ current_aws_account_id: str,
301
+ update_tag: int,
302
+ common_job_parameters: dict[str, Any],
279
303
  ) -> None:
280
304
  for region in regions:
281
- logger.info("Syncing EC2 route tables for region '%s' in account '%s'.", region, current_aws_account_id)
305
+ logger.info(
306
+ "Syncing EC2 route tables for region '%s' in account '%s'.",
307
+ region,
308
+ current_aws_account_id,
309
+ )
282
310
  route_tables = get_route_tables(boto3_session, region)
283
- transformed_tables, association_data, route_data = transform_route_table_data(route_tables)
284
- load_routes(neo4j_session, route_data, region, current_aws_account_id, update_tag)
285
- load_route_table_associations(neo4j_session, association_data, region, current_aws_account_id, update_tag)
286
- load_route_tables(neo4j_session, transformed_tables, region, current_aws_account_id, update_tag)
311
+ transformed_tables, association_data, route_data = transform_route_table_data(
312
+ route_tables
313
+ )
314
+ load_routes(
315
+ neo4j_session, route_data, region, current_aws_account_id, update_tag
316
+ )
317
+ load_route_table_associations(
318
+ neo4j_session, association_data, region, current_aws_account_id, update_tag
319
+ )
320
+ load_route_tables(
321
+ neo4j_session,
322
+ transformed_tables,
323
+ region,
324
+ current_aws_account_id,
325
+ update_tag,
326
+ )
287
327
  cleanup(neo4j_session, common_job_parameters)
@@ -6,30 +6,46 @@ from typing import List
6
6
  import boto3
7
7
  import neo4j
8
8
 
9
- from .util import get_botocore_config
10
9
  from cartography.graph.job import GraphJob
11
- from cartography.models.aws.ec2.securitygroup_instance import EC2SecurityGroupInstanceSchema
10
+ from cartography.models.aws.ec2.securitygroup_instance import (
11
+ EC2SecurityGroupInstanceSchema,
12
+ )
12
13
  from cartography.util import aws_handle_regions
13
14
  from cartography.util import run_cleanup_job
14
15
  from cartography.util import timeit
15
16
 
17
+ from .util import get_botocore_config
18
+
16
19
  logger = logging.getLogger(__name__)
17
20
 
18
21
 
19
22
  @timeit
20
23
  @aws_handle_regions
21
- def get_ec2_security_group_data(boto3_session: boto3.session.Session, region: str) -> List[Dict]:
22
- client = boto3_session.client('ec2', region_name=region, config=get_botocore_config())
23
- paginator = client.get_paginator('describe_security_groups')
24
+ def get_ec2_security_group_data(
25
+ boto3_session: boto3.session.Session,
26
+ region: str,
27
+ ) -> List[Dict]:
28
+ client = boto3_session.client(
29
+ "ec2",
30
+ region_name=region,
31
+ config=get_botocore_config(),
32
+ )
33
+ paginator = client.get_paginator("describe_security_groups")
24
34
  security_groups: List[Dict] = []
25
35
  for page in paginator.paginate():
26
- security_groups.extend(page['SecurityGroups'])
36
+ security_groups.extend(page["SecurityGroups"])
27
37
  return security_groups
28
38
 
29
39
 
30
40
  @timeit
31
- def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule_type: str, update_tag: int) -> None:
32
- INGEST_RULE_TEMPLATE = Template("""
41
+ def load_ec2_security_group_rule(
42
+ neo4j_session: neo4j.Session,
43
+ group: Dict,
44
+ rule_type: str,
45
+ update_tag: int,
46
+ ) -> None:
47
+ INGEST_RULE_TEMPLATE = Template(
48
+ """
33
49
  MERGE (rule:$rule_label{ruleid: $RuleId})
34
50
  ON CREATE SET rule :IpRule, rule.firstseen = timestamp(), rule.fromport = $FromPort, rule.toport = $ToPort,
35
51
  rule.protocol = $Protocol
@@ -39,7 +55,8 @@ def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule
39
55
  MERGE (group)<-[r:MEMBER_OF_EC2_SECURITY_GROUP]-(rule)
40
56
  ON CREATE SET r.firstseen = timestamp()
41
57
  SET r.lastupdated = $update_tag;
42
- """)
58
+ """,
59
+ )
43
60
 
44
61
  ingest_rule_group_pair = """
45
62
  MERGE (group:EC2SecurityGroup{id: $GroupId})
@@ -64,7 +81,10 @@ def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule
64
81
  """
65
82
 
66
83
  group_id = group["GroupId"]
67
- rule_type_map = {"IpPermissions": "IpPermissionInbound", "IpPermissionsEgress": "IpPermissionEgress"}
84
+ rule_type_map = {
85
+ "IpPermissions": "IpPermissionInbound",
86
+ "IpPermissionsEgress": "IpPermissionEgress",
87
+ }
68
88
 
69
89
  if group.get(rule_type):
70
90
  for rule in group[rule_type]:
@@ -76,7 +96,9 @@ def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule
76
96
  # NOTE Cypher query syntax is incompatible with Python string formatting, so we have to do this awkward
77
97
  # NOTE manual formatting instead.
78
98
  neo4j_session.run(
79
- INGEST_RULE_TEMPLATE.safe_substitute(rule_label=rule_type_map[rule_type]),
99
+ INGEST_RULE_TEMPLATE.safe_substitute(
100
+ rule_label=rule_type_map[rule_type],
101
+ ),
80
102
  RuleId=ruleid,
81
103
  FromPort=from_port,
82
104
  ToPort=to_port,
@@ -104,8 +126,11 @@ def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule
104
126
 
105
127
  @timeit
106
128
  def load_ec2_security_groupinfo(
107
- neo4j_session: neo4j.Session, data: List[Dict], region: str,
108
- current_aws_account_id: str, update_tag: int,
129
+ neo4j_session: neo4j.Session,
130
+ data: List[Dict],
131
+ region: str,
132
+ current_aws_account_id: str,
133
+ update_tag: int,
109
134
  ) -> None:
110
135
  ingest_security_group = """
111
136
  MERGE (group:EC2SecurityGroup{id: $GroupId})
@@ -138,26 +163,51 @@ def load_ec2_security_groupinfo(
138
163
  )
139
164
 
140
165
  load_ec2_security_group_rule(neo4j_session, group, "IpPermissions", update_tag)
141
- load_ec2_security_group_rule(neo4j_session, group, "IpPermissionsEgress", update_tag)
166
+ load_ec2_security_group_rule(
167
+ neo4j_session,
168
+ group,
169
+ "IpPermissionsEgress",
170
+ update_tag,
171
+ )
142
172
 
143
173
 
144
174
  @timeit
145
- def cleanup_ec2_security_groupinfo(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
175
+ def cleanup_ec2_security_groupinfo(
176
+ neo4j_session: neo4j.Session,
177
+ common_job_parameters: Dict,
178
+ ) -> None:
146
179
  run_cleanup_job(
147
- 'aws_import_ec2_security_groupinfo_cleanup.json',
180
+ "aws_import_ec2_security_groupinfo_cleanup.json",
148
181
  neo4j_session,
149
182
  common_job_parameters,
150
183
  )
151
- GraphJob.from_node_schema(EC2SecurityGroupInstanceSchema(), common_job_parameters).run(neo4j_session)
184
+ GraphJob.from_node_schema(
185
+ EC2SecurityGroupInstanceSchema(),
186
+ common_job_parameters,
187
+ ).run(neo4j_session)
152
188
 
153
189
 
154
190
  @timeit
155
191
  def sync_ec2_security_groupinfo(
156
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
157
- update_tag: int, common_job_parameters: Dict,
192
+ neo4j_session: neo4j.Session,
193
+ boto3_session: boto3.session.Session,
194
+ regions: List[str],
195
+ current_aws_account_id: str,
196
+ update_tag: int,
197
+ common_job_parameters: Dict,
158
198
  ) -> None:
159
199
  for region in regions:
160
- logger.info("Syncing EC2 security groups for region '%s' in account '%s'.", region, current_aws_account_id)
200
+ logger.info(
201
+ "Syncing EC2 security groups for region '%s' in account '%s'.",
202
+ region,
203
+ current_aws_account_id,
204
+ )
161
205
  data = get_ec2_security_group_data(boto3_session, region)
162
- load_ec2_security_groupinfo(neo4j_session, data, region, current_aws_account_id, update_tag)
206
+ load_ec2_security_groupinfo(
207
+ neo4j_session,
208
+ data,
209
+ region,
210
+ current_aws_account_id,
211
+ update_tag,
212
+ )
163
213
  cleanup_ec2_security_groupinfo(neo4j_session, common_job_parameters)
@@ -14,34 +14,46 @@ logger = logging.getLogger(__name__)
14
14
 
15
15
 
16
16
  @timeit
17
- def get_snapshots_in_use(neo4j_session: neo4j.Session, region: str, current_aws_account_id: str) -> List[str]:
17
+ def get_snapshots_in_use(
18
+ neo4j_session: neo4j.Session,
19
+ region: str,
20
+ current_aws_account_id: str,
21
+ ) -> List[str]:
18
22
  query = """
19
23
  MATCH (:AWSAccount{id: $AWS_ACCOUNT_ID})-[:RESOURCE]->(v:EBSVolume)
20
24
  WHERE v.region = $Region
21
25
  RETURN v.snapshotid as snapshot
22
26
  """
23
- results = neo4j_session.run(query, AWS_ACCOUNT_ID=current_aws_account_id, Region=region)
24
- return [r['snapshot'] for r in results if r['snapshot']]
27
+ results = neo4j_session.run(
28
+ query,
29
+ AWS_ACCOUNT_ID=current_aws_account_id,
30
+ Region=region,
31
+ )
32
+ return [r["snapshot"] for r in results if r["snapshot"]]
25
33
 
26
34
 
27
35
  @timeit
28
36
  @aws_handle_regions
29
- def get_snapshots(boto3_session: boto3.session.Session, region: str, in_use_snapshot_ids: List[str]) -> List[Dict]:
30
- client = boto3_session.client('ec2', region_name=region)
31
- paginator = client.get_paginator('describe_snapshots')
37
+ def get_snapshots(
38
+ boto3_session: boto3.session.Session,
39
+ region: str,
40
+ in_use_snapshot_ids: List[str],
41
+ ) -> List[Dict]:
42
+ client = boto3_session.client("ec2", region_name=region)
43
+ paginator = client.get_paginator("describe_snapshots")
32
44
  snapshots: List[Dict] = []
33
- for page in paginator.paginate(OwnerIds=['self']):
34
- snapshots.extend(page['Snapshots'])
45
+ for page in paginator.paginate(OwnerIds=["self"]):
46
+ snapshots.extend(page["Snapshots"])
35
47
 
36
48
  # fetch in-use snapshots not in self_owned snapshots
37
- self_owned_snapshot_ids = {s['SnapshotId'] for s in snapshots}
49
+ self_owned_snapshot_ids = {s["SnapshotId"] for s in snapshots}
38
50
  other_snapshot_ids = set(in_use_snapshot_ids) - self_owned_snapshot_ids
39
51
  if other_snapshot_ids:
40
52
  try:
41
53
  for page in paginator.paginate(SnapshotIds=list(other_snapshot_ids)):
42
- snapshots.extend(page['Snapshots'])
54
+ snapshots.extend(page["Snapshots"])
43
55
  except ClientError as e:
44
- if e.response['Error']['Code'] == 'InvalidSnapshot.NotFound':
56
+ if e.response["Error"]["Code"] == "InvalidSnapshot.NotFound":
45
57
  logger.warning(
46
58
  f"Failed to retrieve page of in-use, \
47
59
  not owned snapshots. Continuing anyway. Error - {e}",
@@ -54,7 +66,11 @@ def get_snapshots(boto3_session: boto3.session.Session, region: str, in_use_snap
54
66
 
55
67
  @timeit
56
68
  def load_snapshots(
57
- neo4j_session: neo4j.Session, data: List[Dict], region: str, current_aws_account_id: str, update_tag: int,
69
+ neo4j_session: neo4j.Session,
70
+ data: List[Dict],
71
+ region: str,
72
+ current_aws_account_id: str,
73
+ update_tag: int,
58
74
  ) -> None:
59
75
  ingest_snapshots = """
60
76
  UNWIND $snapshots_list as snapshot
@@ -73,7 +89,7 @@ def load_snapshots(
73
89
  """
74
90
 
75
91
  for snapshot in data:
76
- snapshot['StartTime'] = str(snapshot['StartTime'])
92
+ snapshot["StartTime"] = str(snapshot["StartTime"])
77
93
 
78
94
  neo4j_session.run(
79
95
  ingest_snapshots,
@@ -88,7 +104,7 @@ def load_snapshots(
88
104
  def get_snapshot_volumes(snapshots: List[Dict]) -> List[Dict]:
89
105
  snapshot_volumes: List[Dict] = []
90
106
  for snapshot in snapshots:
91
- if snapshot.get('VolumeId'):
107
+ if snapshot.get("VolumeId"):
92
108
  snapshot_volumes.append(snapshot)
93
109
 
94
110
  return snapshot_volumes
@@ -96,7 +112,10 @@ def get_snapshot_volumes(snapshots: List[Dict]) -> List[Dict]:
96
112
 
97
113
  @timeit
98
114
  def load_snapshot_volume_relations(
99
- neo4j_session: neo4j.Session, data: List[Dict], current_aws_account_id: str, update_tag: int,
115
+ neo4j_session: neo4j.Session,
116
+ data: List[Dict],
117
+ current_aws_account_id: str,
118
+ update_tag: int,
100
119
  ) -> None:
101
120
  ingest_volumes = """
102
121
  UNWIND $snapshot_volumes_list as volume
@@ -124,9 +143,12 @@ def load_snapshot_volume_relations(
124
143
 
125
144
 
126
145
  @timeit
127
- def cleanup_snapshots(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
146
+ def cleanup_snapshots(
147
+ neo4j_session: neo4j.Session,
148
+ common_job_parameters: Dict,
149
+ ) -> None:
128
150
  run_cleanup_job(
129
- 'aws_import_snapshots_cleanup.json',
151
+ "aws_import_snapshots_cleanup.json",
130
152
  neo4j_session,
131
153
  common_job_parameters,
132
154
  )
@@ -134,14 +156,31 @@ def cleanup_snapshots(neo4j_session: neo4j.Session, common_job_parameters: Dict)
134
156
 
135
157
  @timeit
136
158
  def sync_ebs_snapshots(
137
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str],
138
- current_aws_account_id: str, update_tag: int, common_job_parameters: Dict,
159
+ neo4j_session: neo4j.Session,
160
+ boto3_session: boto3.session.Session,
161
+ regions: List[str],
162
+ current_aws_account_id: str,
163
+ update_tag: int,
164
+ common_job_parameters: Dict,
139
165
  ) -> None:
140
166
  for region in regions:
141
- logger.debug("Syncing snapshots for region '%s' in account '%s'.", region, current_aws_account_id)
142
- snapshots_in_use = get_snapshots_in_use(neo4j_session, region, current_aws_account_id)
167
+ logger.debug(
168
+ "Syncing snapshots for region '%s' in account '%s'.",
169
+ region,
170
+ current_aws_account_id,
171
+ )
172
+ snapshots_in_use = get_snapshots_in_use(
173
+ neo4j_session,
174
+ region,
175
+ current_aws_account_id,
176
+ )
143
177
  data = get_snapshots(boto3_session, region, snapshots_in_use)
144
178
  load_snapshots(neo4j_session, data, region, current_aws_account_id, update_tag)
145
179
  snapshot_volumes = get_snapshot_volumes(data)
146
- load_snapshot_volume_relations(neo4j_session, snapshot_volumes, current_aws_account_id, update_tag)
180
+ load_snapshot_volume_relations(
181
+ neo4j_session,
182
+ snapshot_volumes,
183
+ current_aws_account_id,
184
+ update_tag,
185
+ )
147
186
  cleanup_snapshots(neo4j_session, common_job_parameters)