cartography 0.102.0rc2__py3-none-any.whl → 0.103.0rc1__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 (251) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +2 -2
  3. cartography/cli.py +302 -253
  4. cartography/client/core/tx.py +39 -18
  5. cartography/config.py +4 -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/config.py +56 -20
  23. cartography/intel/aws/dynamodb.py +108 -40
  24. cartography/intel/aws/ec2/__init__.py +2 -2
  25. cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
  26. cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
  27. cartography/intel/aws/ec2/images.py +49 -20
  28. cartography/intel/aws/ec2/instances.py +234 -136
  29. cartography/intel/aws/ec2/internet_gateways.py +40 -11
  30. cartography/intel/aws/ec2/key_pairs.py +44 -20
  31. cartography/intel/aws/ec2/launch_templates.py +101 -59
  32. cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
  33. cartography/intel/aws/ec2/load_balancers.py +82 -42
  34. cartography/intel/aws/ec2/network_acls.py +89 -65
  35. cartography/intel/aws/ec2/network_interfaces.py +146 -87
  36. cartography/intel/aws/ec2/reserved_instances.py +45 -16
  37. cartography/intel/aws/ec2/route_tables.py +138 -98
  38. cartography/intel/aws/ec2/security_groups.py +71 -21
  39. cartography/intel/aws/ec2/snapshots.py +61 -22
  40. cartography/intel/aws/ec2/subnets.py +54 -18
  41. cartography/intel/aws/ec2/tgw.py +100 -34
  42. cartography/intel/aws/ec2/util.py +1 -1
  43. cartography/intel/aws/ec2/volumes.py +69 -41
  44. cartography/intel/aws/ec2/vpc.py +37 -12
  45. cartography/intel/aws/ec2/vpc_peerings.py +83 -24
  46. cartography/intel/aws/ecr.py +88 -32
  47. cartography/intel/aws/ecs.py +83 -47
  48. cartography/intel/aws/eks.py +55 -29
  49. cartography/intel/aws/elasticache.py +42 -18
  50. cartography/intel/aws/elasticsearch.py +57 -20
  51. cartography/intel/aws/emr.py +61 -23
  52. cartography/intel/aws/iam.py +401 -145
  53. cartography/intel/aws/iam_instance_profiles.py +22 -22
  54. cartography/intel/aws/identitycenter.py +71 -37
  55. cartography/intel/aws/inspector.py +159 -89
  56. cartography/intel/aws/kms.py +92 -38
  57. cartography/intel/aws/lambda_function.py +103 -34
  58. cartography/intel/aws/organizations.py +30 -10
  59. cartography/intel/aws/permission_relationships.py +133 -51
  60. cartography/intel/aws/rds.py +249 -85
  61. cartography/intel/aws/redshift.py +107 -46
  62. cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
  63. cartography/intel/aws/resources.py +53 -46
  64. cartography/intel/aws/route53.py +108 -61
  65. cartography/intel/aws/s3.py +168 -83
  66. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  67. cartography/intel/aws/secretsmanager.py +24 -12
  68. cartography/intel/aws/securityhub.py +20 -9
  69. cartography/intel/aws/sns.py +166 -0
  70. cartography/intel/aws/sqs.py +60 -28
  71. cartography/intel/aws/ssm.py +70 -30
  72. cartography/intel/aws/util/arns.py +7 -7
  73. cartography/intel/aws/util/common.py +31 -4
  74. cartography/intel/azure/__init__.py +78 -19
  75. cartography/intel/azure/compute.py +101 -27
  76. cartography/intel/azure/cosmosdb.py +496 -170
  77. cartography/intel/azure/sql.py +296 -105
  78. cartography/intel/azure/storage.py +322 -113
  79. cartography/intel/azure/subscription.py +39 -23
  80. cartography/intel/azure/tenant.py +13 -4
  81. cartography/intel/azure/util/credentials.py +95 -55
  82. cartography/intel/bigfix/__init__.py +2 -2
  83. cartography/intel/bigfix/computers.py +93 -65
  84. cartography/intel/create_indexes.py +3 -2
  85. cartography/intel/crowdstrike/__init__.py +11 -9
  86. cartography/intel/crowdstrike/endpoints.py +5 -1
  87. cartography/intel/crowdstrike/spotlight.py +8 -3
  88. cartography/intel/cve/__init__.py +46 -13
  89. cartography/intel/cve/feed.py +48 -12
  90. cartography/intel/digitalocean/__init__.py +22 -13
  91. cartography/intel/digitalocean/compute.py +75 -108
  92. cartography/intel/digitalocean/management.py +44 -80
  93. cartography/intel/digitalocean/platform.py +48 -43
  94. cartography/intel/dns.py +36 -10
  95. cartography/intel/duo/__init__.py +21 -16
  96. cartography/intel/duo/api_host.py +14 -9
  97. cartography/intel/duo/endpoints.py +50 -45
  98. cartography/intel/duo/groups.py +18 -14
  99. cartography/intel/duo/phones.py +37 -34
  100. cartography/intel/duo/tokens.py +26 -23
  101. cartography/intel/duo/users.py +54 -50
  102. cartography/intel/duo/web_authn_credentials.py +30 -25
  103. cartography/intel/entra/__init__.py +25 -7
  104. cartography/intel/entra/ou.py +112 -0
  105. cartography/intel/entra/users.py +69 -63
  106. cartography/intel/gcp/__init__.py +185 -49
  107. cartography/intel/gcp/compute.py +418 -231
  108. cartography/intel/gcp/crm.py +96 -43
  109. cartography/intel/gcp/dns.py +60 -19
  110. cartography/intel/gcp/gke.py +72 -38
  111. cartography/intel/gcp/iam.py +61 -41
  112. cartography/intel/gcp/storage.py +84 -55
  113. cartography/intel/github/__init__.py +13 -11
  114. cartography/intel/github/repos.py +270 -137
  115. cartography/intel/github/teams.py +170 -88
  116. cartography/intel/github/users.py +70 -39
  117. cartography/intel/github/util.py +36 -34
  118. cartography/intel/gsuite/__init__.py +47 -26
  119. cartography/intel/gsuite/api.py +73 -30
  120. cartography/intel/jamf/__init__.py +19 -1
  121. cartography/intel/jamf/computers.py +30 -7
  122. cartography/intel/jamf/util.py +7 -2
  123. cartography/intel/kandji/__init__.py +6 -3
  124. cartography/intel/kandji/devices.py +14 -8
  125. cartography/intel/kubernetes/namespaces.py +7 -4
  126. cartography/intel/kubernetes/pods.py +7 -4
  127. cartography/intel/kubernetes/services.py +8 -4
  128. cartography/intel/lastpass/__init__.py +2 -2
  129. cartography/intel/lastpass/users.py +23 -12
  130. cartography/intel/oci/__init__.py +44 -11
  131. cartography/intel/oci/iam.py +134 -38
  132. cartography/intel/oci/organizations.py +13 -6
  133. cartography/intel/oci/utils.py +43 -20
  134. cartography/intel/okta/__init__.py +66 -15
  135. cartography/intel/okta/applications.py +42 -20
  136. cartography/intel/okta/awssaml.py +93 -33
  137. cartography/intel/okta/factors.py +16 -4
  138. cartography/intel/okta/groups.py +56 -29
  139. cartography/intel/okta/organization.py +5 -1
  140. cartography/intel/okta/origins.py +6 -2
  141. cartography/intel/okta/roles.py +15 -5
  142. cartography/intel/okta/users.py +20 -8
  143. cartography/intel/okta/utils.py +6 -4
  144. cartography/intel/pagerduty/__init__.py +8 -7
  145. cartography/intel/pagerduty/escalation_policies.py +18 -6
  146. cartography/intel/pagerduty/schedules.py +12 -4
  147. cartography/intel/pagerduty/services.py +11 -4
  148. cartography/intel/pagerduty/teams.py +8 -3
  149. cartography/intel/pagerduty/users.py +3 -1
  150. cartography/intel/pagerduty/vendors.py +3 -1
  151. cartography/intel/semgrep/__init__.py +24 -6
  152. cartography/intel/semgrep/dependencies.py +50 -28
  153. cartography/intel/semgrep/deployment.py +3 -1
  154. cartography/intel/semgrep/findings.py +42 -18
  155. cartography/intel/snipeit/__init__.py +17 -3
  156. cartography/intel/snipeit/asset.py +12 -6
  157. cartography/intel/snipeit/user.py +8 -5
  158. cartography/intel/snipeit/util.py +9 -4
  159. cartography/models/aws/apigateway.py +21 -17
  160. cartography/models/aws/apigatewaycertificate.py +28 -22
  161. cartography/models/aws/apigatewayresource.py +28 -20
  162. cartography/models/aws/apigatewaystage.py +33 -25
  163. cartography/models/aws/cloudtrail/__init__.py +0 -0
  164. cartography/models/aws/cloudtrail/trail.py +61 -0
  165. cartography/models/aws/dynamodb/gsi.py +30 -22
  166. cartography/models/aws/dynamodb/tables.py +25 -17
  167. cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
  168. cartography/models/aws/ec2/images.py +36 -34
  169. cartography/models/aws/ec2/instances.py +51 -45
  170. cartography/models/aws/ec2/keypair.py +21 -16
  171. cartography/models/aws/ec2/keypair_instance.py +28 -21
  172. cartography/models/aws/ec2/launch_configurations.py +30 -26
  173. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  174. cartography/models/aws/ec2/launch_templates.py +21 -17
  175. cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
  176. cartography/models/aws/ec2/load_balancers.py +47 -37
  177. cartography/models/aws/ec2/network_acl_rules.py +38 -30
  178. cartography/models/aws/ec2/network_acls.py +38 -29
  179. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  180. cartography/models/aws/ec2/networkinterfaces.py +53 -37
  181. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  182. cartography/models/aws/ec2/reservations.py +18 -14
  183. cartography/models/aws/ec2/route_table_associations.py +44 -34
  184. cartography/models/aws/ec2/route_tables.py +50 -43
  185. cartography/models/aws/ec2/routes.py +45 -37
  186. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  187. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  188. cartography/models/aws/ec2/subnet_instance.py +24 -19
  189. cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
  190. cartography/models/aws/ec2/volumes.py +47 -40
  191. cartography/models/aws/eks/clusters.py +23 -21
  192. cartography/models/aws/emr.py +32 -30
  193. cartography/models/aws/iam/instanceprofile.py +33 -24
  194. cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
  195. cartography/models/aws/identitycenter/awspermissionset.py +37 -29
  196. cartography/models/aws/identitycenter/awsssouser.py +23 -21
  197. cartography/models/aws/inspector/findings.py +77 -65
  198. cartography/models/aws/inspector/packages.py +35 -29
  199. cartography/models/aws/s3/__init__.py +0 -0
  200. cartography/models/aws/s3/account_public_access_block.py +51 -0
  201. cartography/models/aws/sns/__init__.py +0 -0
  202. cartography/models/aws/sns/topic.py +50 -0
  203. cartography/models/aws/ssm/instance_information.py +51 -39
  204. cartography/models/aws/ssm/instance_patch.py +32 -26
  205. cartography/models/bigfix/bigfix_computer.py +42 -38
  206. cartography/models/bigfix/bigfix_root.py +3 -3
  207. cartography/models/core/common.py +12 -10
  208. cartography/models/core/nodes.py +5 -2
  209. cartography/models/core/relationships.py +14 -6
  210. cartography/models/crowdstrike/hosts.py +37 -35
  211. cartography/models/cve/cve.py +34 -32
  212. cartography/models/cve/cve_feed.py +6 -6
  213. cartography/models/digitalocean/__init__.py +0 -0
  214. cartography/models/digitalocean/account.py +21 -0
  215. cartography/models/digitalocean/droplet.py +56 -0
  216. cartography/models/digitalocean/project.py +48 -0
  217. cartography/models/duo/api_host.py +3 -3
  218. cartography/models/duo/endpoint.py +43 -41
  219. cartography/models/duo/group.py +14 -14
  220. cartography/models/duo/phone.py +27 -27
  221. cartography/models/duo/token.py +16 -16
  222. cartography/models/duo/user.py +46 -44
  223. cartography/models/duo/web_authn_credential.py +27 -19
  224. cartography/models/entra/ou.py +48 -0
  225. cartography/models/entra/tenant.py +24 -18
  226. cartography/models/entra/user.py +64 -48
  227. cartography/models/gcp/iam.py +23 -23
  228. cartography/models/github/orgs.py +5 -4
  229. cartography/models/github/teams.py +37 -31
  230. cartography/models/github/users.py +34 -23
  231. cartography/models/kandji/device.py +22 -16
  232. cartography/models/kandji/tenant.py +6 -4
  233. cartography/models/lastpass/tenant.py +3 -3
  234. cartography/models/lastpass/user.py +32 -28
  235. cartography/models/semgrep/dependencies.py +36 -24
  236. cartography/models/semgrep/deployment.py +5 -5
  237. cartography/models/semgrep/findings.py +58 -42
  238. cartography/models/semgrep/locations.py +27 -21
  239. cartography/models/snipeit/asset.py +30 -21
  240. cartography/models/snipeit/tenant.py +6 -4
  241. cartography/models/snipeit/user.py +19 -12
  242. cartography/stats.py +3 -3
  243. cartography/sync.py +107 -31
  244. cartography/util.py +84 -62
  245. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/METADATA +3 -14
  246. cartography-0.103.0rc1.dist-info/RECORD +396 -0
  247. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
  248. cartography-0.102.0rc2.dist-info/RECORD +0 -381
  249. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
  250. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
  251. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
@@ -17,12 +17,15 @@ logger = logging.getLogger(__name__)
17
17
 
18
18
  @timeit
19
19
  @aws_handle_regions
20
- def get_ecs_cluster_arns(boto3_session: boto3.session.Session, region: str) -> List[str]:
21
- client = boto3_session.client('ecs', region_name=region)
22
- paginator = client.get_paginator('list_clusters')
20
+ def get_ecs_cluster_arns(
21
+ boto3_session: boto3.session.Session,
22
+ region: str,
23
+ ) -> List[str]:
24
+ client = boto3_session.client("ecs", region_name=region)
25
+ paginator = client.get_paginator("list_clusters")
23
26
  cluster_arns: List[str] = []
24
27
  for page in paginator.paginate():
25
- cluster_arns.extend(page.get('clusterArns', []))
28
+ cluster_arns.extend(page.get("clusterArns", []))
26
29
  return cluster_arns
27
30
 
28
31
 
@@ -33,15 +36,18 @@ def get_ecs_clusters(
33
36
  region: str,
34
37
  cluster_arns: List[str],
35
38
  ) -> List[Dict[str, Any]]:
36
- client = boto3_session.client('ecs', region_name=region)
39
+ client = boto3_session.client("ecs", region_name=region)
37
40
  # TODO: also include attachment info, and make relationships between the attachements
38
41
  # and the cluster.
39
- includes = ['SETTINGS', 'CONFIGURATIONS']
42
+ includes = ["SETTINGS", "CONFIGURATIONS"]
40
43
  clusters: List[Dict[str, Any]] = []
41
44
  for i in range(0, len(cluster_arns), 100):
42
- cluster_arn_chunk = cluster_arns[i:i + 100]
43
- cluster_chunk = client.describe_clusters(clusters=cluster_arn_chunk, include=includes)
44
- clusters.extend(cluster_chunk.get('clusters', []))
45
+ cluster_arn_chunk = cluster_arns[i : i + 100]
46
+ cluster_chunk = client.describe_clusters(
47
+ clusters=cluster_arn_chunk,
48
+ include=includes,
49
+ )
50
+ clusters.extend(cluster_chunk.get("clusters", []))
45
51
  return clusters
46
52
 
47
53
 
@@ -52,40 +58,46 @@ def get_ecs_container_instances(
52
58
  boto3_session: boto3.session.Session,
53
59
  region: str,
54
60
  ) -> List[Dict[str, Any]]:
55
- client = boto3_session.client('ecs', region_name=region)
56
- paginator = client.get_paginator('list_container_instances')
61
+ client = boto3_session.client("ecs", region_name=region)
62
+ paginator = client.get_paginator("list_container_instances")
57
63
  container_instances: List[Dict[str, Any]] = []
58
64
  container_instance_arns: List[str] = []
59
65
  for page in paginator.paginate(cluster=cluster_arn):
60
- container_instance_arns.extend(page.get('containerInstanceArns', []))
61
- includes = ['CONTAINER_INSTANCE_HEALTH']
66
+ container_instance_arns.extend(page.get("containerInstanceArns", []))
67
+ includes = ["CONTAINER_INSTANCE_HEALTH"]
62
68
  for i in range(0, len(container_instance_arns), 100):
63
- container_instance_arn_chunk = container_instance_arns[i:i + 100]
69
+ container_instance_arn_chunk = container_instance_arns[i : i + 100]
64
70
  container_instance_chunk = client.describe_container_instances(
65
71
  cluster=cluster_arn,
66
72
  containerInstances=container_instance_arn_chunk,
67
73
  include=includes,
68
74
  )
69
- container_instances.extend(container_instance_chunk.get('containerInstances', []))
75
+ container_instances.extend(
76
+ container_instance_chunk.get("containerInstances", []),
77
+ )
70
78
  return container_instances
71
79
 
72
80
 
73
81
  @timeit
74
82
  @aws_handle_regions
75
- def get_ecs_services(cluster_arn: str, boto3_session: boto3.session.Session, region: str) -> List[Dict[str, Any]]:
76
- client = boto3_session.client('ecs', region_name=region)
77
- paginator = client.get_paginator('list_services')
83
+ def get_ecs_services(
84
+ cluster_arn: str,
85
+ boto3_session: boto3.session.Session,
86
+ region: str,
87
+ ) -> List[Dict[str, Any]]:
88
+ client = boto3_session.client("ecs", region_name=region)
89
+ paginator = client.get_paginator("list_services")
78
90
  services: List[Dict[str, Any]] = []
79
91
  service_arns: List[str] = []
80
92
  for page in paginator.paginate(cluster=cluster_arn):
81
- service_arns.extend(page.get('serviceArns', []))
93
+ service_arns.extend(page.get("serviceArns", []))
82
94
  for i in range(0, len(service_arns), 10):
83
- service_arn_chunk = service_arns[i:i + 10]
95
+ service_arn_chunk = service_arns[i : i + 10]
84
96
  service_chunk = client.describe_services(
85
97
  cluster=cluster_arn,
86
98
  services=service_arn_chunk,
87
99
  )
88
- services.extend(service_chunk.get('services', []))
100
+ services.extend(service_chunk.get("services", []))
89
101
  return services
90
102
 
91
103
 
@@ -96,32 +108,36 @@ def get_ecs_task_definitions(
96
108
  region: str,
97
109
  tasks: List[Dict[str, Any]],
98
110
  ) -> List[Dict[str, Any]]:
99
- client = boto3_session.client('ecs', region_name=region)
111
+ client = boto3_session.client("ecs", region_name=region)
100
112
  task_definitions: List[Dict[str, Any]] = []
101
113
  for task in tasks:
102
114
  task_definition = client.describe_task_definition(
103
- taskDefinition=task['taskDefinitionArn'],
115
+ taskDefinition=task["taskDefinitionArn"],
104
116
  )
105
- task_definitions.append(task_definition['taskDefinition'])
117
+ task_definitions.append(task_definition["taskDefinition"])
106
118
  return task_definitions
107
119
 
108
120
 
109
121
  @timeit
110
122
  @aws_handle_regions
111
- def get_ecs_tasks(cluster_arn: str, boto3_session: boto3.session.Session, region: str) -> List[Dict[str, Any]]:
112
- client = boto3_session.client('ecs', region_name=region)
113
- paginator = client.get_paginator('list_tasks')
123
+ def get_ecs_tasks(
124
+ cluster_arn: str,
125
+ boto3_session: boto3.session.Session,
126
+ region: str,
127
+ ) -> List[Dict[str, Any]]:
128
+ client = boto3_session.client("ecs", region_name=region)
129
+ paginator = client.get_paginator("list_tasks")
114
130
  tasks: List[Dict[str, Any]] = []
115
131
  task_arns: List[str] = []
116
132
  for page in paginator.paginate(cluster=cluster_arn):
117
- task_arns.extend(page.get('taskArns', []))
133
+ task_arns.extend(page.get("taskArns", []))
118
134
  for i in range(0, len(task_arns), 100):
119
- task_arn_chunk = task_arns[i:i + 100]
135
+ task_arn_chunk = task_arns[i : i + 100]
120
136
  task_chunk = client.describe_tasks(
121
137
  cluster=cluster_arn,
122
138
  tasks=task_arn_chunk,
123
139
  )
124
- tasks.extend(task_chunk.get('tasks', []))
140
+ tasks.extend(task_chunk.get("tasks", []))
125
141
  return tasks
126
142
 
127
143
 
@@ -207,7 +223,7 @@ def load_ecs_container_instances(
207
223
  """
208
224
  instances: List[Dict[str, Any]] = []
209
225
  for instance in data:
210
- instance['registeredAt'] = dict_date_to_epoch(instance, 'registeredAt')
226
+ instance["registeredAt"] = dict_date_to_epoch(instance, "registeredAt")
211
227
  instances.append(instance)
212
228
 
213
229
  neo4j_session.run(
@@ -269,7 +285,7 @@ def load_ecs_services(
269
285
  """ # noqa:E501
270
286
  services: List[Dict[str, Any]] = []
271
287
  for service in data:
272
- service['createdAt'] = dict_date_to_epoch(service, 'createdAt')
288
+ service["createdAt"] = dict_date_to_epoch(service, "createdAt")
273
289
  services.append(service)
274
290
 
275
291
  neo4j_session.run(
@@ -331,8 +347,14 @@ def load_ecs_task_definitions(
331
347
  container_definitions: List[Dict[str, Any]] = []
332
348
  task_definitions: List[Dict[str, Any]] = []
333
349
  for task_definition in data:
334
- task_definition['registeredAt'] = dict_date_to_epoch(task_definition, 'registeredAt')
335
- task_definition['deregisteredAt'] = dict_date_to_epoch(task_definition, 'deregisteredAt')
350
+ task_definition["registeredAt"] = dict_date_to_epoch(
351
+ task_definition,
352
+ "registeredAt",
353
+ )
354
+ task_definition["deregisteredAt"] = dict_date_to_epoch(
355
+ task_definition,
356
+ "deregisteredAt",
357
+ )
336
358
  for container in task_definition.get("containerDefinitions", []):
337
359
  container["_taskDefinitionArn"] = task_definition["taskDefinitionArn"]
338
360
  container_definitions.append(container)
@@ -418,14 +440,14 @@ def load_ecs_tasks(
418
440
  containers: List[Dict[str, Any]] = []
419
441
  tasks: List[Dict[str, Any]] = []
420
442
  for task in data:
421
- task['connectivityAt'] = dict_date_to_epoch(task, 'connectivityAt')
422
- task['createdAt'] = dict_date_to_epoch(task, 'createdAt')
423
- task['executionStoppedAt'] = dict_date_to_epoch(task, 'executionStoppedAt')
424
- task['pullStartedAt'] = dict_date_to_epoch(task, 'pullStartedAt')
425
- task['pullStoppedAt'] = dict_date_to_epoch(task, 'pullStoppedAt')
426
- task['startedAt'] = dict_date_to_epoch(task, 'startedAt')
427
- task['stoppedAt'] = dict_date_to_epoch(task, 'stoppedAt')
428
- task['stoppingAt'] = dict_date_to_epoch(task, 'stoppingAt')
443
+ task["connectivityAt"] = dict_date_to_epoch(task, "connectivityAt")
444
+ task["createdAt"] = dict_date_to_epoch(task, "createdAt")
445
+ task["executionStoppedAt"] = dict_date_to_epoch(task, "executionStoppedAt")
446
+ task["pullStartedAt"] = dict_date_to_epoch(task, "pullStartedAt")
447
+ task["pullStoppedAt"] = dict_date_to_epoch(task, "pullStoppedAt")
448
+ task["startedAt"] = dict_date_to_epoch(task, "startedAt")
449
+ task["stoppedAt"] = dict_date_to_epoch(task, "stoppedAt")
450
+ task["stoppingAt"] = dict_date_to_epoch(task, "stoppingAt")
429
451
  containers.extend(task["containers"])
430
452
  tasks.append(task)
431
453
 
@@ -542,21 +564,35 @@ def load_ecs_containers(
542
564
 
543
565
  @timeit
544
566
  def cleanup_ecs(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
545
- run_cleanup_job('aws_import_ecs_cleanup.json', neo4j_session, common_job_parameters)
567
+ run_cleanup_job("aws_import_ecs_cleanup.json", neo4j_session, common_job_parameters)
546
568
 
547
569
 
548
570
  @timeit
549
571
  def sync(
550
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
551
- update_tag: int, common_job_parameters: Dict,
572
+ neo4j_session: neo4j.Session,
573
+ boto3_session: boto3.session.Session,
574
+ regions: List[str],
575
+ current_aws_account_id: str,
576
+ update_tag: int,
577
+ common_job_parameters: Dict,
552
578
  ) -> None:
553
579
  for region in regions:
554
- logger.info("Syncing ECS for region '%s' in account '%s'.", region, current_aws_account_id)
580
+ logger.info(
581
+ "Syncing ECS for region '%s' in account '%s'.",
582
+ region,
583
+ current_aws_account_id,
584
+ )
555
585
  cluster_arns = get_ecs_cluster_arns(boto3_session, region)
556
586
  clusters = get_ecs_clusters(boto3_session, region, cluster_arns)
557
587
  if len(clusters) == 0:
558
588
  continue
559
- load_ecs_clusters(neo4j_session, clusters, region, current_aws_account_id, update_tag)
589
+ load_ecs_clusters(
590
+ neo4j_session,
591
+ clusters,
592
+ region,
593
+ current_aws_account_id,
594
+ update_tag,
595
+ )
560
596
  for cluster_arn in cluster_arns:
561
597
  cluster_instances = get_ecs_container_instances(
562
598
  cluster_arn,
@@ -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