cartography 0.102.0rc1__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 +327 -0
  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 -44
  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 +97 -0
  208. cartography/models/aws/ec2/route_tables.py +128 -0
  209. cartography/models/aws/ec2/routes.py +85 -0
  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.0rc1.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.0rc1.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
  294. cartography-0.102.0rc1.dist-info/RECORD +0 -377
  295. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
  296. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
  297. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,93 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import boto3
7
+ import neo4j
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.intel.aws.ec2.util import get_botocore_config
12
+ from cartography.models.aws.efs.mount_target import EfsMountTargetSchema
13
+ from cartography.util import aws_handle_regions
14
+ from cartography.util import timeit
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ @timeit
20
+ @aws_handle_regions
21
+ def get_efs_mount_targets(
22
+ boto3_session: boto3.Session, region: str
23
+ ) -> List[Dict[str, Any]]:
24
+ client = boto3_session.client(
25
+ "efs", region_name=region, config=get_botocore_config()
26
+ )
27
+ paginator = client.get_paginator("describe_mount_targets")
28
+ mountTargets = []
29
+ for page in paginator.paginate():
30
+ mountTargets.extend(page["MountTargets"])
31
+ return mountTargets
32
+
33
+
34
+ @timeit
35
+ def load_efs_mount_targets(
36
+ neo4j_session: neo4j.Session,
37
+ data: List[Dict[str, Any]],
38
+ region: str,
39
+ current_aws_account_id: str,
40
+ aws_update_tag: int,
41
+ ) -> None:
42
+ logger.info(
43
+ f"Loading Efs {len(data)} mount targets for region '{region}' into graph.",
44
+ )
45
+ load(
46
+ neo4j_session,
47
+ EfsMountTargetSchema(),
48
+ data,
49
+ lastupdated=aws_update_tag,
50
+ Region=region,
51
+ AWS_ID=current_aws_account_id,
52
+ )
53
+
54
+
55
+ @timeit
56
+ def cleanup(
57
+ neo4j_session: neo4j.Session,
58
+ common_job_parameters: Dict[str, Any],
59
+ ) -> None:
60
+ logger.debug("Running Efs cleanup job.")
61
+ cleanup_job = GraphJob.from_node_schema(
62
+ EfsMountTargetSchema(), common_job_parameters
63
+ )
64
+ cleanup_job.run(neo4j_session)
65
+
66
+
67
+ @timeit
68
+ def sync(
69
+ neo4j_session: neo4j.Session,
70
+ boto3_session: boto3.session.Session,
71
+ regions: List[str],
72
+ current_aws_account_id: str,
73
+ update_tag: int,
74
+ common_job_parameters: Dict[str, Any],
75
+ ) -> None:
76
+ for region in regions:
77
+ logger.info(
78
+ f"Syncing Efs for region '{region}' in account '{current_aws_account_id}'.",
79
+ )
80
+ mountTargets = get_efs_mount_targets(boto3_session, region)
81
+ mount_target_data: List[Dict[str, Any]] = []
82
+ for mountTarget in mountTargets:
83
+ mount_target_data.append(mountTarget)
84
+
85
+ load_efs_mount_targets(
86
+ neo4j_session,
87
+ mount_target_data,
88
+ region,
89
+ current_aws_account_id,
90
+ update_tag,
91
+ )
92
+
93
+ cleanup(neo4j_session, common_job_parameters)
@@ -18,28 +18,32 @@ logger = logging.getLogger(__name__)
18
18
  @timeit
19
19
  @aws_handle_regions
20
20
  def get_eks_clusters(boto3_session: boto3.session.Session, region: str) -> List[str]:
21
- client = boto3_session.client('eks', region_name=region)
21
+ client = boto3_session.client("eks", region_name=region)
22
22
  clusters: List[str] = []
23
- paginator = client.get_paginator('list_clusters')
23
+ paginator = client.get_paginator("list_clusters")
24
24
  for page in paginator.paginate():
25
- clusters.extend(page['clusters'])
25
+ clusters.extend(page["clusters"])
26
26
  return clusters
27
27
 
28
28
 
29
29
  @timeit
30
- def get_eks_describe_cluster(boto3_session: boto3.session.Session, region: str, cluster_name: str) -> Dict:
31
- client = boto3_session.client('eks', region_name=region)
30
+ def get_eks_describe_cluster(
31
+ boto3_session: boto3.session.Session,
32
+ region: str,
33
+ cluster_name: str,
34
+ ) -> Dict:
35
+ client = boto3_session.client("eks", region_name=region)
32
36
  response = client.describe_cluster(name=cluster_name)
33
- return response['cluster']
37
+ return response["cluster"]
34
38
 
35
39
 
36
40
  @timeit
37
41
  def load_eks_clusters(
38
- neo4j_session: neo4j.Session,
39
- cluster_data: List[Dict[str, Any]],
40
- region: str,
41
- current_aws_account_id: str,
42
- aws_update_tag: int,
42
+ neo4j_session: neo4j.Session,
43
+ cluster_data: List[Dict[str, Any]],
44
+ region: str,
45
+ current_aws_account_id: str,
46
+ aws_update_tag: int,
43
47
  ) -> None:
44
48
  load(
45
49
  neo4j_session,
@@ -57,50 +61,72 @@ def _process_logging(cluster: Dict) -> bool:
57
61
  at least one entry has audit logging set to Enabled.
58
62
  """
59
63
  logging: bool = False
60
- cluster_logging: Any = cluster.get('logging', {}).get('clusterLogging')
64
+ cluster_logging: Any = cluster.get("logging", {}).get("clusterLogging")
61
65
  if cluster_logging:
62
- logging = any(filter(lambda x: 'audit' in x['types'] and x['enabled'], cluster_logging)) # type: ignore
66
+ logging = any(filter(lambda x: "audit" in x["types"] and x["enabled"], cluster_logging)) # type: ignore
63
67
  return logging
64
68
 
65
69
 
66
70
  @timeit
67
- def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
71
+ def cleanup(
72
+ neo4j_session: neo4j.Session,
73
+ common_job_parameters: Dict[str, Any],
74
+ ) -> None:
68
75
  logger.info("Running EKS cluster cleanup")
69
- GraphJob.from_node_schema(EKSClusterSchema(), common_job_parameters).run(neo4j_session)
76
+ GraphJob.from_node_schema(EKSClusterSchema(), common_job_parameters).run(
77
+ neo4j_session,
78
+ )
70
79
 
71
80
 
72
81
  def transform(cluster_data: Dict[str, Any]) -> List[Dict[str, Any]]:
73
82
  transformed_list = []
74
83
  for cluster_name, cluster_dict in cluster_data.items():
75
84
  transformed_dict = cluster_dict.copy()
76
- transformed_dict['ClusterLogging'] = _process_logging(transformed_dict)
77
- transformed_dict['ClusterEndpointPublic'] = transformed_dict.get('resourcesVpcConfig', {}).get(
78
- 'endpointPublicAccess',
85
+ transformed_dict["ClusterLogging"] = _process_logging(transformed_dict)
86
+ transformed_dict["ClusterEndpointPublic"] = transformed_dict.get(
87
+ "resourcesVpcConfig",
88
+ {},
89
+ ).get(
90
+ "endpointPublicAccess",
79
91
  )
80
- if 'createdAt' in transformed_dict:
81
- transformed_dict['created_at'] = str(transformed_dict['createdAt'])
92
+ if "createdAt" in transformed_dict:
93
+ transformed_dict["created_at"] = str(transformed_dict["createdAt"])
82
94
  transformed_list.append(transformed_dict)
83
95
  return transformed_list
84
96
 
85
97
 
86
98
  @timeit
87
99
  def sync(
88
- neo4j_session: neo4j.Session,
89
- boto3_session: boto3.session.Session,
90
- regions: List[str],
91
- current_aws_account_id: str,
92
- update_tag: int,
93
- common_job_parameters: Dict[str, Any],
100
+ neo4j_session: neo4j.Session,
101
+ boto3_session: boto3.session.Session,
102
+ regions: List[str],
103
+ current_aws_account_id: str,
104
+ update_tag: int,
105
+ common_job_parameters: Dict[str, Any],
94
106
  ) -> None:
95
107
  for region in regions:
96
- logger.info("Syncing EKS for region '%s' in account '%s'.", region, current_aws_account_id)
108
+ logger.info(
109
+ "Syncing EKS for region '%s' in account '%s'.",
110
+ region,
111
+ current_aws_account_id,
112
+ )
97
113
 
98
114
  clusters: List[str] = get_eks_clusters(boto3_session, region)
99
115
  cluster_data = {}
100
116
  for cluster_name in clusters:
101
- cluster_data[cluster_name] = get_eks_describe_cluster(boto3_session, region, cluster_name)
117
+ cluster_data[cluster_name] = get_eks_describe_cluster(
118
+ boto3_session,
119
+ region,
120
+ cluster_name,
121
+ )
102
122
  transformed_list = transform(cluster_data)
103
123
 
104
- load_eks_clusters(neo4j_session, transformed_list, region, current_aws_account_id, update_tag)
124
+ load_eks_clusters(
125
+ neo4j_session,
126
+ transformed_list,
127
+ region,
128
+ current_aws_account_id,
129
+ update_tag,
130
+ )
105
131
 
106
132
  cleanup(neo4j_session, common_job_parameters)
@@ -17,7 +17,7 @@ stat_handler = get_stats_client(__name__)
17
17
 
18
18
 
19
19
  def _get_topic(cluster: Dict) -> Dict:
20
- return cluster['NotificationConfiguration']
20
+ return cluster["NotificationConfiguration"]
21
21
 
22
22
 
23
23
  def transform_elasticache_topics(cluster_data: List[Dict]) -> List[Dict]:
@@ -28,7 +28,7 @@ def transform_elasticache_topics(cluster_data: List[Dict]) -> List[Dict]:
28
28
  topics: List[Dict] = []
29
29
  for cluster in cluster_data:
30
30
  topic = _get_topic(cluster)
31
- topic_arn = topic['TopicArn']
31
+ topic_arn = topic["TopicArn"]
32
32
  if topic_arn not in seen:
33
33
  seen.add(topic_arn)
34
34
  topics.append(topic)
@@ -37,20 +37,26 @@ def transform_elasticache_topics(cluster_data: List[Dict]) -> List[Dict]:
37
37
 
38
38
  @timeit
39
39
  @aws_handle_regions
40
- def get_elasticache_clusters(boto3_session: boto3.session.Session, region: str) -> List[Dict]:
40
+ def get_elasticache_clusters(
41
+ boto3_session: boto3.session.Session,
42
+ region: str,
43
+ ) -> List[Dict]:
41
44
  logger.debug(f"Getting ElastiCache Clusters in region '{region}'.")
42
- client = boto3_session.client('elasticache', region_name=region)
43
- paginator = client.get_paginator('describe_cache_clusters')
45
+ client = boto3_session.client("elasticache", region_name=region)
46
+ paginator = client.get_paginator("describe_cache_clusters")
44
47
  clusters: List[Dict] = []
45
48
  for page in paginator.paginate():
46
- clusters.extend(page['CacheClusters'])
49
+ clusters.extend(page["CacheClusters"])
47
50
  return clusters
48
51
 
49
52
 
50
53
  @timeit
51
54
  def load_elasticache_clusters(
52
- neo4j_session: neo4j.Session, clusters: List[Dict], region: str,
53
- aws_account_id: str, update_tag: int,
55
+ neo4j_session: neo4j.Session,
56
+ clusters: List[Dict],
57
+ region: str,
58
+ aws_account_id: str,
59
+ update_tag: int,
54
60
  ) -> None:
55
61
  query = """
56
62
  UNWIND $clusters as elasticache_cluster
@@ -85,7 +91,9 @@ def load_elasticache_clusters(
85
91
  ON CREATE SET r2.firstseen = timestamp()
86
92
  SET r2.lastupdated = $aws_update_tag
87
93
  """
88
- logger.info(f"Loading f{len(clusters)} ElastiCache clusters for region '{region}' into graph.")
94
+ logger.info(
95
+ f"Loading f{len(clusters)} ElastiCache clusters for region '{region}' into graph.",
96
+ )
89
97
  neo4j_session.run(
90
98
  query,
91
99
  clusters=clusters,
@@ -96,29 +104,45 @@ def load_elasticache_clusters(
96
104
 
97
105
 
98
106
  @timeit
99
- def cleanup(neo4j_session: neo4j.Session, current_aws_account_id: str, update_tag: int) -> None:
107
+ def cleanup(
108
+ neo4j_session: neo4j.Session,
109
+ current_aws_account_id: str,
110
+ update_tag: int,
111
+ ) -> None:
100
112
  run_cleanup_job(
101
- 'aws_import_elasticache_cleanup.json',
113
+ "aws_import_elasticache_cleanup.json",
102
114
  neo4j_session,
103
- {'UPDATE_TAG': update_tag, 'AWS_ID': current_aws_account_id},
115
+ {"UPDATE_TAG": update_tag, "AWS_ID": current_aws_account_id},
104
116
  )
105
117
 
106
118
 
107
119
  @timeit
108
120
  def sync(
109
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
110
- update_tag: int, common_job_parameters: Dict,
121
+ neo4j_session: neo4j.Session,
122
+ boto3_session: boto3.session.Session,
123
+ regions: List[str],
124
+ current_aws_account_id: str,
125
+ update_tag: int,
126
+ common_job_parameters: Dict,
111
127
  ) -> None:
112
128
  for region in regions:
113
- logger.info(f"Syncing ElastiCache clusters for region '{region}' in account {current_aws_account_id}")
129
+ logger.info(
130
+ f"Syncing ElastiCache clusters for region '{region}' in account {current_aws_account_id}",
131
+ )
114
132
  clusters = get_elasticache_clusters(boto3_session, region)
115
- load_elasticache_clusters(neo4j_session, clusters, region, current_aws_account_id, update_tag)
133
+ load_elasticache_clusters(
134
+ neo4j_session,
135
+ clusters,
136
+ region,
137
+ current_aws_account_id,
138
+ update_tag,
139
+ )
116
140
  cleanup(neo4j_session, current_aws_account_id, update_tag)
117
141
  merge_module_sync_metadata(
118
142
  neo4j_session,
119
- group_type='AWSAccount',
143
+ group_type="AWSAccount",
120
144
  group_id=current_aws_account_id,
121
- synced_type='ElasticacheCluster',
145
+ synced_type="ElasticacheCluster",
122
146
  update_tag=update_tag,
123
147
  stat_handler=stat_handler,
124
148
  )
@@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
20
20
  def _get_botocore_config() -> botocore.config.Config:
21
21
  return botocore.config.Config(
22
22
  retries={
23
- 'max_attempts': 8,
23
+ "max_attempts": 8,
24
24
  },
25
25
  )
26
26
 
@@ -35,19 +35,26 @@ def _get_es_domains(client: botocore.client.BaseClient) -> List[Dict]:
35
35
  :return: list of ES domains
36
36
  """
37
37
  data = client.list_domain_names()
38
- domain_names = [d['DomainName'] for d in data.get('DomainNames', [])]
38
+ domain_names = [d["DomainName"] for d in data.get("DomainNames", [])]
39
39
  # NOTE describe_elasticsearch_domains takes at most 5 domain names
40
- domain_name_chunks = [domain_names[i:i + 5] for i in range(0, len(domain_names), 5)]
40
+ domain_name_chunks = [
41
+ domain_names[i : i + 5] for i in range(0, len(domain_names), 5)
42
+ ]
41
43
  domains: List[Dict] = []
42
44
  for domain_name_chunk in domain_name_chunks:
43
- chunk_data = client.describe_elasticsearch_domains(DomainNames=domain_name_chunk)
44
- domains.extend(chunk_data['DomainStatusList'])
45
+ chunk_data = client.describe_elasticsearch_domains(
46
+ DomainNames=domain_name_chunk,
47
+ )
48
+ domains.extend(chunk_data["DomainStatusList"])
45
49
  return domains
46
50
 
47
51
 
48
52
  @timeit
49
53
  def _load_es_domains(
50
- neo4j_session: neo4j.Session, domain_list: List[Dict], aws_account_id: str, aws_update_tag: int,
54
+ neo4j_session: neo4j.Session,
55
+ domain_list: List[Dict],
56
+ aws_account_id: str,
57
+ aws_update_tag: int,
51
58
  ) -> None:
52
59
  """
53
60
  Ingest Elastic Search domains
@@ -86,7 +93,7 @@ def _load_es_domains(
86
93
  # TODO this is a hacky workaround -- neo4j doesn't accept datetime objects and this section of the object
87
94
  # TODO contains one. we really shouldn't be sending the entire object to neo4j
88
95
  for d in domain_list:
89
- del d['ServiceSoftwareOptions']
96
+ del d["ServiceSoftwareOptions"]
90
97
 
91
98
  neo4j_session.run(
92
99
  ingest_records,
@@ -104,7 +111,10 @@ def _load_es_domains(
104
111
 
105
112
  @timeit
106
113
  def _link_es_domains_to_dns(
107
- neo4j_session: neo4j.Session, domain_id: str, domain_data: Dict, aws_update_tag: int,
114
+ neo4j_session: neo4j.Session,
115
+ domain_id: str,
116
+ domain_data: Dict,
117
+ aws_update_tag: int,
108
118
  ) -> None:
109
119
  """
110
120
  Link the ES domain to its DNS FQDN endpoint and create associated nodes in the graph
@@ -117,15 +127,24 @@ def _link_es_domains_to_dns(
117
127
  # TODO add support for endpoints to this method
118
128
  if domain_data.get("Endpoint"):
119
129
  ingest_dns_record_by_fqdn(
120
- neo4j_session, aws_update_tag, domain_data["Endpoint"], domain_id,
121
- record_label="ESDomain", dns_node_additional_label="AWSDNSRecord",
130
+ neo4j_session,
131
+ aws_update_tag,
132
+ domain_data["Endpoint"],
133
+ domain_id,
134
+ record_label="ESDomain",
135
+ dns_node_additional_label="AWSDNSRecord",
122
136
  )
123
137
  else:
124
138
  logger.debug(f"No es endpoint data for domain id {domain_id}")
125
139
 
126
140
 
127
141
  @timeit
128
- def _link_es_domain_vpc(neo4j_session: neo4j.Session, domain_id: str, domain_data: Dict, aws_update_tag: int) -> None:
142
+ def _link_es_domain_vpc(
143
+ neo4j_session: neo4j.Session,
144
+ domain_id: str,
145
+ domain_data: Dict,
146
+ aws_update_tag: int,
147
+ ) -> None:
129
148
  """
130
149
  Link the ES domain to its DNS FQDN endpoint and create associated nodes in the graph
131
150
  if needed
@@ -177,7 +196,11 @@ def _link_es_domain_vpc(neo4j_session: neo4j.Session, domain_id: str, domain_dat
177
196
 
178
197
 
179
198
  @timeit
180
- def _process_access_policy(neo4j_session: neo4j.Session, domain_id: str, domain_data: Dict) -> None:
199
+ def _process_access_policy(
200
+ neo4j_session: neo4j.Session,
201
+ domain_id: str,
202
+ domain_data: Dict,
203
+ ) -> None:
181
204
  """
182
205
  Link the ES domain to its DNS FQDN endpoint and create associated nodes in the graph
183
206
  if needed
@@ -186,12 +209,14 @@ def _process_access_policy(neo4j_session: neo4j.Session, domain_id: str, domain_
186
209
  :param domain_id: ES domain id
187
210
  :param domain_data: domain data
188
211
  """
189
- tag_es = "MATCH (es:ESDomain{id: $DomainId}) SET es.exposed_internet = $InternetExposed"
212
+ tag_es = (
213
+ "MATCH (es:ESDomain{id: $DomainId}) SET es.exposed_internet = $InternetExposed"
214
+ )
190
215
 
191
216
  exposed_internet = False
192
217
 
193
218
  if domain_data.get("Endpoint") and domain_data.get("AccessPolicies"):
194
- policy = Policy(json.loads(domain_data['AccessPolicies']))
219
+ policy = Policy(json.loads(domain_data["AccessPolicies"]))
195
220
  if policy.is_internet_accessible():
196
221
  exposed_internet = True
197
222
 
@@ -201,20 +226,32 @@ def _process_access_policy(neo4j_session: neo4j.Session, domain_id: str, domain_
201
226
  @timeit
202
227
  def cleanup(neo4j_session: neo4j.Session, update_tag: int, aws_account_id: int) -> None:
203
228
  run_cleanup_job(
204
- 'aws_import_es_cleanup.json',
229
+ "aws_import_es_cleanup.json",
205
230
  neo4j_session,
206
- {'UPDATE_TAG': update_tag, 'AWS_ID': aws_account_id},
231
+ {"UPDATE_TAG": update_tag, "AWS_ID": aws_account_id},
207
232
  )
208
233
 
209
234
 
210
235
  @timeit
211
236
  def sync(
212
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
213
- update_tag: int, common_job_parameters: Dict,
237
+ neo4j_session: neo4j.Session,
238
+ boto3_session: boto3.session.Session,
239
+ regions: List[str],
240
+ current_aws_account_id: str,
241
+ update_tag: int,
242
+ common_job_parameters: Dict,
214
243
  ) -> None:
215
244
  for region in regions:
216
- logger.info("Syncing Elasticsearch Service for region '%s' in account '%s'.", region, current_aws_account_id)
217
- client = boto3_session.client('es', region_name=region, config=_get_botocore_config())
245
+ logger.info(
246
+ "Syncing Elasticsearch Service for region '%s' in account '%s'.",
247
+ region,
248
+ current_aws_account_id,
249
+ )
250
+ client = boto3_session.client(
251
+ "es",
252
+ region_name=region,
253
+ config=_get_botocore_config(),
254
+ )
218
255
  data = _get_es_domains(client)
219
256
  _load_es_domains(neo4j_session, data, current_aws_account_id, update_tag)
220
257
 
@@ -24,40 +24,59 @@ DESCRIBE_SLEEP = 1
24
24
 
25
25
  @timeit
26
26
  @aws_handle_regions
27
- def get_emr_clusters(boto3_session: boto3.session.Session, region: str) -> List[Dict[str, Any]]:
28
- client = boto3_session.client('emr', region_name=region, config=get_botocore_config())
27
+ def get_emr_clusters(
28
+ boto3_session: boto3.session.Session,
29
+ region: str,
30
+ ) -> List[Dict[str, Any]]:
31
+ client = boto3_session.client(
32
+ "emr",
33
+ region_name=region,
34
+ config=get_botocore_config(),
35
+ )
29
36
  clusters: List[Dict[str, Any]] = []
30
- paginator = client.get_paginator('list_clusters')
37
+ paginator = client.get_paginator("list_clusters")
31
38
  for page in paginator.paginate():
32
- cluster = page['Clusters']
39
+ cluster = page["Clusters"]
33
40
  clusters.extend(cluster)
34
41
  time.sleep(LIST_SLEEP)
35
42
  return clusters
36
43
 
37
44
 
38
45
  @timeit
39
- def get_emr_describe_cluster(boto3_session: boto3.session.Session, region: str, cluster_id: str) -> Dict[str, Any]:
40
- client = boto3_session.client('emr', region_name=region, config=get_botocore_config())
46
+ def get_emr_describe_cluster(
47
+ boto3_session: boto3.session.Session,
48
+ region: str,
49
+ cluster_id: str,
50
+ ) -> Dict[str, Any]:
51
+ client = boto3_session.client(
52
+ "emr",
53
+ region_name=region,
54
+ config=get_botocore_config(),
55
+ )
41
56
  cluster_details: Dict[str, Any] = {}
42
57
  try:
43
58
  response = client.describe_cluster(ClusterId=cluster_id)
44
- cluster_details = response['Cluster']
59
+ cluster_details = response["Cluster"]
45
60
  except botocore.exceptions.ClientError as e:
46
- code = e.response['Error']['Code']
47
- msg = e.response['Error']['Message']
48
- logger.warning(f"Could not run EMR describe_cluster due to boto3 error {code}: {msg}. Skipping.")
61
+ code = e.response["Error"]["Code"]
62
+ msg = e.response["Error"]["Message"]
63
+ logger.warning(
64
+ f"Could not run EMR describe_cluster due to boto3 error {code}: {msg}. Skipping.",
65
+ )
49
66
  return cluster_details
50
67
 
51
68
 
52
69
  @timeit
53
70
  def load_emr_clusters(
54
- neo4j_session: neo4j.Session,
55
- cluster_data: List[Dict[str, Any]],
56
- region: str,
57
- current_aws_account_id: str,
58
- aws_update_tag: int,
71
+ neo4j_session: neo4j.Session,
72
+ cluster_data: List[Dict[str, Any]],
73
+ region: str,
74
+ current_aws_account_id: str,
75
+ aws_update_tag: int,
59
76
  ) -> None:
60
- logger.info(f"Loading EMR {len(cluster_data)} clusters for region '{region}' into graph.")
77
+ logger.info(
78
+ f"Loading EMR {len(cluster_data)} clusters for region '{region}' into graph.",
79
+ )
61
80
  load(
62
81
  neo4j_session,
63
82
  EMRClusterSchema(),
@@ -69,7 +88,10 @@ def load_emr_clusters(
69
88
 
70
89
 
71
90
  @timeit
72
- def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
91
+ def cleanup(
92
+ neo4j_session: neo4j.Session,
93
+ common_job_parameters: Dict[str, Any],
94
+ ) -> None:
73
95
  logger.debug("Running EMR cleanup job.")
74
96
  cleanup_job = GraphJob.from_node_schema(EMRClusterSchema(), common_job_parameters)
75
97
  cleanup_job.run(neo4j_session)
@@ -77,22 +99,38 @@ def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any])
77
99
 
78
100
  @timeit
79
101
  def sync(
80
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
81
- update_tag: int, common_job_parameters: Dict[str, Any],
102
+ neo4j_session: neo4j.Session,
103
+ boto3_session: boto3.session.Session,
104
+ regions: List[str],
105
+ current_aws_account_id: str,
106
+ update_tag: int,
107
+ common_job_parameters: Dict[str, Any],
82
108
  ) -> None:
83
109
  for region in regions:
84
- logger.info(f"Syncing EMR for region '{region}' in account '{current_aws_account_id}'.")
110
+ logger.info(
111
+ f"Syncing EMR for region '{region}' in account '{current_aws_account_id}'.",
112
+ )
85
113
 
86
114
  clusters = get_emr_clusters(boto3_session, region)
87
115
 
88
116
  cluster_data: List[Dict[str, Any]] = []
89
117
  for cluster in clusters:
90
- cluster_id = cluster['Id']
91
- cluster_details = get_emr_describe_cluster(boto3_session, region, cluster_id)
118
+ cluster_id = cluster["Id"]
119
+ cluster_details = get_emr_describe_cluster(
120
+ boto3_session,
121
+ region,
122
+ cluster_id,
123
+ )
92
124
  if cluster_details:
93
125
  cluster_data.append(cluster_details)
94
126
  time.sleep(DESCRIBE_SLEEP)
95
127
 
96
- load_emr_clusters(neo4j_session, cluster_data, region, current_aws_account_id, update_tag)
128
+ load_emr_clusters(
129
+ neo4j_session,
130
+ cluster_data,
131
+ region,
132
+ current_aws_account_id,
133
+ update_tag,
134
+ )
97
135
 
98
136
  cleanup(neo4j_session, common_job_parameters)