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
@@ -10,10 +10,8 @@ import boto3
10
10
  import botocore.exceptions
11
11
  import neo4j
12
12
 
13
- from . import ec2
14
- from . import organizations
15
- from .resources import RESOURCE_FUNCTIONS
16
13
  from cartography.config import Config
14
+ from cartography.intel.aws.util.common import parse_and_validate_aws_regions
17
15
  from cartography.intel.aws.util.common import parse_and_validate_aws_requested_syncs
18
16
  from cartography.stats import get_stats_client
19
17
  from cartography.util import merge_module_sync_metadata
@@ -23,22 +21,29 @@ from cartography.util import run_cleanup_job
23
21
  from cartography.util import run_scoped_analysis_job
24
22
  from cartography.util import timeit
25
23
 
24
+ from . import ec2
25
+ from . import organizations
26
+ from .resources import RESOURCE_FUNCTIONS
26
27
 
27
28
  stat_handler = get_stats_client(__name__)
28
29
  logger = logging.getLogger(__name__)
29
30
 
30
31
 
31
32
  def _build_aws_sync_kwargs(
32
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
33
- sync_tag: int, common_job_parameters: Dict[str, Any],
33
+ neo4j_session: neo4j.Session,
34
+ boto3_session: boto3.session.Session,
35
+ regions: List[str],
36
+ current_aws_account_id: str,
37
+ sync_tag: int,
38
+ common_job_parameters: Dict[str, Any],
34
39
  ) -> Dict[str, Any]:
35
40
  return {
36
- 'neo4j_session': neo4j_session,
37
- 'boto3_session': boto3_session,
38
- 'regions': regions,
39
- 'current_aws_account_id': current_aws_account_id,
40
- 'update_tag': sync_tag,
41
- 'common_job_parameters': common_job_parameters,
41
+ "neo4j_session": neo4j_session,
42
+ "boto3_session": boto3_session,
43
+ "regions": regions,
44
+ "current_aws_account_id": current_aws_account_id,
45
+ "update_tag": sync_tag,
46
+ "common_job_parameters": common_job_parameters,
42
47
  }
43
48
 
44
49
 
@@ -48,57 +53,71 @@ def _sync_one_account(
48
53
  current_aws_account_id: str,
49
54
  update_tag: int,
50
55
  common_job_parameters: Dict[str, Any],
51
- regions: List[str] = [],
56
+ regions: list[str] | None = None,
52
57
  aws_requested_syncs: Iterable[str] = RESOURCE_FUNCTIONS.keys(),
53
58
  ) -> None:
59
+ # Autodiscover the regions supported by the account unless the user has specified the regions to sync.
54
60
  if not regions:
55
61
  regions = _autodiscover_account_regions(boto3_session, current_aws_account_id)
56
62
 
57
63
  sync_args = _build_aws_sync_kwargs(
58
- neo4j_session, boto3_session, regions, current_aws_account_id, update_tag, common_job_parameters,
64
+ neo4j_session,
65
+ boto3_session,
66
+ regions,
67
+ current_aws_account_id,
68
+ update_tag,
69
+ common_job_parameters,
59
70
  )
60
71
 
61
72
  for func_name in aws_requested_syncs:
62
73
  if func_name in RESOURCE_FUNCTIONS:
63
74
  # Skip permission relationships and tags for now because they rely on data already being in the graph
64
- if func_name not in ['permission_relationships', 'resourcegroupstaggingapi']:
75
+ if func_name not in [
76
+ "permission_relationships",
77
+ "resourcegroupstaggingapi",
78
+ ]:
65
79
  RESOURCE_FUNCTIONS[func_name](**sync_args)
66
80
  else:
67
81
  continue
68
82
  else:
69
- raise ValueError(f'AWS sync function "{func_name}" was specified but does not exist. Did you misspell it?')
83
+ raise ValueError(
84
+ f'AWS sync function "{func_name}" was specified but does not exist. Did you misspell it?',
85
+ )
70
86
 
71
87
  # MAP IAM permissions
72
- if 'permission_relationships' in aws_requested_syncs:
73
- RESOURCE_FUNCTIONS['permission_relationships'](**sync_args)
88
+ if "permission_relationships" in aws_requested_syncs:
89
+ RESOURCE_FUNCTIONS["permission_relationships"](**sync_args)
74
90
 
75
91
  # AWS Tags - Must always be last.
76
- if 'resourcegroupstaggingapi' in aws_requested_syncs:
77
- RESOURCE_FUNCTIONS['resourcegroupstaggingapi'](**sync_args)
92
+ if "resourcegroupstaggingapi" in aws_requested_syncs:
93
+ RESOURCE_FUNCTIONS["resourcegroupstaggingapi"](**sync_args)
78
94
 
79
95
  run_scoped_analysis_job(
80
- 'aws_ec2_iaminstanceprofile.json',
96
+ "aws_ec2_iaminstanceprofile.json",
81
97
  neo4j_session,
82
98
  common_job_parameters,
83
99
  )
84
100
 
85
101
  run_analysis_job(
86
- 'aws_lambda_ecr.json',
102
+ "aws_lambda_ecr.json",
87
103
  neo4j_session,
88
104
  common_job_parameters,
89
105
  )
90
106
 
91
107
  merge_module_sync_metadata(
92
108
  neo4j_session,
93
- group_type='AWSAccount',
109
+ group_type="AWSAccount",
94
110
  group_id=current_aws_account_id,
95
- synced_type='AWSAccount',
111
+ synced_type="AWSAccount",
96
112
  update_tag=update_tag,
97
113
  stat_handler=stat_handler,
98
114
  )
99
115
 
100
116
 
101
- def _autodiscover_account_regions(boto3_session: boto3.session.Session, account_id: str) -> List[str]:
117
+ def _autodiscover_account_regions(
118
+ boto3_session: boto3.session.Session,
119
+ account_id: str,
120
+ ) -> List[str]:
102
121
  regions: List[str] = []
103
122
  try:
104
123
  regions = ec2.get_ec2_regions(boto3_session)
@@ -116,27 +135,39 @@ def _autodiscover_account_regions(boto3_session: boto3.session.Session, account_
116
135
 
117
136
 
118
137
  def _autodiscover_accounts(
119
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, account_id: str,
120
- sync_tag: int, common_job_parameters: Dict,
138
+ neo4j_session: neo4j.Session,
139
+ boto3_session: boto3.session.Session,
140
+ account_id: str,
141
+ sync_tag: int,
142
+ common_job_parameters: Dict,
121
143
  ) -> None:
122
144
  logger.info("Trying to autodiscover accounts.")
123
145
  try:
124
146
  # Fetch all accounts
125
- client = boto3_session.client('organizations')
126
- paginator = client.get_paginator('list_accounts')
147
+ client = boto3_session.client("organizations")
148
+ paginator = client.get_paginator("list_accounts")
127
149
  accounts: List[Dict] = []
128
150
  for page in paginator.paginate():
129
- accounts.extend(page['Accounts'])
151
+ accounts.extend(page["Accounts"])
130
152
 
131
153
  # Filter out every account which is not in the ACTIVE status
132
154
  # and select only the Id and Name fields
133
- filtered_accounts: Dict[str, str] = {x['Name']: x['Id'] for x in accounts if x['Status'] == 'ACTIVE'}
155
+ filtered_accounts: Dict[str, str] = {
156
+ x["Name"]: x["Id"] for x in accounts if x["Status"] == "ACTIVE"
157
+ }
134
158
 
135
159
  # Add them to the graph
136
160
  logger.info("Loading autodiscovered accounts.")
137
- organizations.load_aws_accounts(neo4j_session, filtered_accounts, sync_tag, common_job_parameters)
161
+ organizations.load_aws_accounts(
162
+ neo4j_session,
163
+ filtered_accounts,
164
+ sync_tag,
165
+ common_job_parameters,
166
+ )
138
167
  except botocore.exceptions.ClientError:
139
- logger.warning(f"The current account ({account_id}) doesn't have enough permissions to perform autodiscovery.")
168
+ logger.warning(
169
+ f"The current account ({account_id}) doesn't have enough permissions to perform autodiscovery.",
170
+ )
140
171
 
141
172
 
142
173
  def _sync_multiple_accounts(
@@ -146,8 +177,9 @@ def _sync_multiple_accounts(
146
177
  common_job_parameters: Dict[str, Any],
147
178
  aws_best_effort_mode: bool,
148
179
  aws_requested_syncs: List[str] = [],
180
+ regions: list[str] | None = None,
149
181
  ) -> bool:
150
- logger.info("Syncing AWS accounts: %s", ', '.join(accounts.values()))
182
+ logger.info("Syncing AWS accounts: %s", ", ".join(accounts.values()))
151
183
  organizations.sync(neo4j_session, accounts, sync_tag, common_job_parameters)
152
184
 
153
185
  failed_account_ids = []
@@ -156,7 +188,11 @@ def _sync_multiple_accounts(
156
188
  num_accounts = len(accounts)
157
189
 
158
190
  for profile_name, account_id in accounts.items():
159
- logger.info("Syncing AWS account with ID '%s' using configured profile '%s'.", account_id, profile_name)
191
+ logger.info(
192
+ "Syncing AWS account with ID '%s' using configured profile '%s'.",
193
+ account_id,
194
+ profile_name,
195
+ )
160
196
  common_job_parameters["AWS_ID"] = account_id
161
197
  if num_accounts == 1:
162
198
  # Use the default boto3 session because boto3 gets confused if you give it a profile name with 1 account
@@ -164,7 +200,13 @@ def _sync_multiple_accounts(
164
200
  else:
165
201
  boto3_session = boto3.Session(profile_name=profile_name)
166
202
 
167
- _autodiscover_accounts(neo4j_session, boto3_session, account_id, sync_tag, common_job_parameters)
203
+ _autodiscover_accounts(
204
+ neo4j_session,
205
+ boto3_session,
206
+ account_id,
207
+ sync_tag,
208
+ common_job_parameters,
209
+ )
168
210
 
169
211
  try:
170
212
  _sync_one_account(
@@ -173,6 +215,7 @@ def _sync_multiple_accounts(
173
215
  account_id,
174
216
  sync_tag,
175
217
  common_job_parameters,
218
+ regions=regions,
176
219
  aws_requested_syncs=aws_requested_syncs, # Could be replaced later with per-account requested syncs
177
220
  )
178
221
  except Exception as e:
@@ -180,8 +223,10 @@ def _sync_multiple_accounts(
180
223
  timestamp = datetime.datetime.now()
181
224
  failed_account_ids.append(account_id)
182
225
  exception_traceback = traceback.TracebackException.from_exception(e)
183
- traceback_string = ''.join(exception_traceback.format())
184
- exception_tracebacks.append(f'{timestamp} - Exception for account ID: {account_id}\n{traceback_string}')
226
+ traceback_string = "".join(exception_traceback.format())
227
+ exception_tracebacks.append(
228
+ f"{timestamp} - Exception for account ID: {account_id}\n{traceback_string}",
229
+ )
185
230
  logger.warning(
186
231
  f"Caught exception syncing account {account_id}. aws-best-effort-mode is on so we are continuing "
187
232
  f"on to the next AWS account. All exceptions will be aggregated and re-logged at the end of the "
@@ -193,24 +238,28 @@ def _sync_multiple_accounts(
193
238
  raise
194
239
 
195
240
  if failed_account_ids:
196
- logger.error(f'AWS sync failed for accounts {failed_account_ids}')
197
- raise Exception('\n'.join(exception_tracebacks))
241
+ logger.error(f"AWS sync failed for accounts {failed_account_ids}")
242
+ raise Exception("\n".join(exception_tracebacks))
198
243
 
199
244
  del common_job_parameters["AWS_ID"]
200
245
 
201
246
  # There may be orphan Principals which point outside of known AWS accounts. This job cleans
202
247
  # up those nodes after all AWS accounts have been synced.
203
248
  if not failed_account_ids:
204
- run_cleanup_job('aws_post_ingestion_principals_cleanup.json', neo4j_session, common_job_parameters)
249
+ run_cleanup_job(
250
+ "aws_post_ingestion_principals_cleanup.json",
251
+ neo4j_session,
252
+ common_job_parameters,
253
+ )
205
254
  return True
206
255
  return False
207
256
 
208
257
 
209
258
  @timeit
210
259
  def _perform_aws_analysis(
211
- requested_syncs: List[str],
212
- neo4j_session: neo4j.Session,
213
- common_job_parameters: Dict[str, Any],
260
+ requested_syncs: List[str],
261
+ neo4j_session: neo4j.Session,
262
+ common_job_parameters: Dict[str, Any],
214
263
  ) -> None:
215
264
  """
216
265
  Performs AWS analysis jobs that span multiple accounts.
@@ -218,13 +267,13 @@ def _perform_aws_analysis(
218
267
  requested_syncs_as_set = set(requested_syncs)
219
268
 
220
269
  ec2_asset_exposure_requirements = {
221
- 'ec2:instance',
222
- 'ec2:security_group',
223
- 'ec2:load_balancer',
224
- 'ec2:load_balancer_v2',
270
+ "ec2:instance",
271
+ "ec2:security_group",
272
+ "ec2:load_balancer",
273
+ "ec2:load_balancer_v2",
225
274
  }
226
275
  run_analysis_and_ensure_deps(
227
- 'aws_ec2_asset_exposure.json',
276
+ "aws_ec2_asset_exposure.json",
228
277
  ec2_asset_exposure_requirements,
229
278
  requested_syncs_as_set,
230
279
  common_job_parameters,
@@ -232,23 +281,23 @@ def _perform_aws_analysis(
232
281
  )
233
282
 
234
283
  run_analysis_and_ensure_deps(
235
- 'aws_ec2_keypair_analysis.json',
236
- {'ec2:keypair'},
284
+ "aws_ec2_keypair_analysis.json",
285
+ {"ec2:keypair"},
237
286
  requested_syncs_as_set,
238
287
  common_job_parameters,
239
288
  neo4j_session,
240
289
  )
241
290
 
242
291
  run_analysis_and_ensure_deps(
243
- 'aws_eks_asset_exposure.json',
244
- {'eks'},
292
+ "aws_eks_asset_exposure.json",
293
+ {"eks"},
245
294
  requested_syncs_as_set,
246
295
  common_job_parameters,
247
296
  neo4j_session,
248
297
  )
249
298
 
250
299
  run_analysis_and_ensure_deps(
251
- 'aws_foreign_accounts.json',
300
+ "aws_foreign_accounts.json",
252
301
  set(), # This job has no requirements
253
302
  requested_syncs_as_set,
254
303
  common_job_parameters,
@@ -277,7 +326,9 @@ def start_aws_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
277
326
  return
278
327
 
279
328
  if config.aws_sync_all_profiles:
280
- aws_accounts = organizations.get_aws_accounts_from_botocore_config(boto3_session)
329
+ aws_accounts = organizations.get_aws_accounts_from_botocore_config(
330
+ boto3_session,
331
+ )
281
332
  else:
282
333
  aws_accounts = organizations.get_aws_account_default(boto3_session)
283
334
 
@@ -297,7 +348,14 @@ def start_aws_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
297
348
 
298
349
  requested_syncs: List[str] = list(RESOURCE_FUNCTIONS.keys())
299
350
  if config.aws_requested_syncs:
300
- requested_syncs = parse_and_validate_aws_requested_syncs(config.aws_requested_syncs)
351
+ requested_syncs = parse_and_validate_aws_requested_syncs(
352
+ config.aws_requested_syncs,
353
+ )
354
+
355
+ if config.aws_regions:
356
+ regions = parse_and_validate_aws_regions(config.aws_regions)
357
+ else:
358
+ regions = None
301
359
 
302
360
  sync_successful = _sync_multiple_accounts(
303
361
  neo4j_session,
@@ -306,6 +364,7 @@ def start_aws_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
306
364
  common_job_parameters,
307
365
  config.aws_best_effort_mode,
308
366
  requested_syncs,
367
+ regions=regions,
309
368
  )
310
369
 
311
370
  if sync_successful: