reverse-diagrams 1.3.4__py3-none-any.whl → 2.0.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.
@@ -1,480 +1,359 @@
1
1
  """Describe Organizations."""
2
2
  import logging
3
3
  import os
4
+ from typing import List, Dict, Any, Optional
5
+ from pathlib import Path
4
6
 
5
7
  import emoji
6
8
  from colorama import Fore
7
9
 
10
+ from .client_manager import get_client_manager
11
+ from .exceptions import AWSServiceError
8
12
  from ..dgms.graph_mapper import create_file, create_mapper, find_ou_name
9
13
  from ..dgms.graph_template import graph_template
10
14
  from ..reports.save_results import save_results
11
- from .describe_sso import client
15
+ from ..utils.progress import track_operation, get_progress_tracker
16
+ from ..config import get_config
17
+ from ..models import AWSAccount, OrganizationalUnit, AWSOrganization, validate_aws_response
12
18
 
13
-
14
- def describe_organization(region, org_client):
15
- """
16
- Describe the organization.
17
-
18
- :param region: AWS Region
19
- :return:
20
- """
21
- print(f"{Fore.GREEN}❇️ Describe Organization {Fore.RESET}")
22
-
23
- organization = org_client.describe_organization()
24
- organization = organization["Organization"]
25
- return organization
26
-
27
-
28
- def list_roots(region):
29
- """
30
- List the roots.
31
-
32
- :param region: AWS Region
33
- :return:
34
- """
35
- org_client = client("organizations", region_name=region)
36
- roots = org_client.list_roots()
37
- return roots["Roots"]
38
-
39
-
40
- # def list organizational units pagination
41
- def list_organizational_units_pag(parent_id, region, next_token=None):
42
- """
43
- List organizational Units with pagination.
44
-
45
- :param parent_id:
46
- :param region:
47
- :param next_token:
48
- :return:
49
- """
50
- org_client = client("organizations", region_name=region)
51
- paginator = org_client.get_paginator("list_organizational_units_for_parent")
52
- response_iterator = paginator.paginate(
53
- ParentId=parent_id,
54
- PaginationConfig={"MaxItems": 1000, "PageSize": 4, "StartingToken": next_token},
55
- )
56
- response_iterator = response_iterator.build_full_result()
57
- return response_iterator["OrganizationalUnits"]
58
-
59
-
60
- def list_organizational_units(parent_id, region, org_client, org_units=None):
61
- """
62
- List Organizational units.
63
-
64
- :param org_client:
65
- :param parent_id:
66
- :param region:
67
- :param org_units:
68
- :return:
69
- """
70
-
71
- if org_units is None:
72
- org_units = []
73
- ous = org_client.list_organizational_units_for_parent(
74
- ParentId=parent_id,
75
- MaxResults=20,
76
- )
77
- ous = ous["OrganizationalUnits"]
78
-
79
- if len(ous) >= 20:
80
- logging.info("Paginating ...")
81
- add_ous = list_organizational_units_pag(parent_id=parent_id,
82
- region=region, next_token=ous["NextToken"]
83
- )
84
- for ou in add_ous:
85
- ous.append(ou)
86
- logging.debug(add_ous)
87
-
88
- for o in ous:
89
- org_units.append(o)
90
- logging.debug(
91
- f"The parent Id is: {parent_id}",
92
- )
93
- logging.debug(ous)
94
- if len(ous) > 0:
95
- for ou in ous:
96
- logging.debug(ou)
97
- if "Id" in ou.keys():
98
- logging.debug(f"Search nested for: {ou['Name']}")
99
- ous_next = list_organizational_units(parent_id=ou["Id"],
100
- region=region, org_units=org_units,
101
- org_client=org_client,
102
- )
103
- logging.debug(ous_next)
104
- if len(ous_next) > 0:
105
- logging.debug("Find Nested")
106
-
107
- return org_units
108
-
109
-
110
- def list_parents(child_id, region):
111
- """
112
- List the parents of a child.
113
-
114
- :param child_id:
115
- :param region:
116
- :return:
117
- """
118
- org_client = client("organizations", region_name=region)
119
- response = org_client.list_parents(
120
- ChildId=child_id,
121
- )
122
- return response["Parents"]
19
+ logger = logging.getLogger(__name__)
123
20
 
124
21
 
125
- def index_ous(list_ous, region):
22
+ def describe_organization(region: str) -> Dict[str, Any]:
126
23
  """
127
- Index the parents of a child.
128
-
129
- :param list_ous:
130
- :param region:
131
- :return list_ous:
24
+ Describe the organization.
132
25
 
133
- """
134
- org_client = client("organizations", region_name=region)
135
- for ou in list_ous:
136
- if "Id" in ou.keys() and len(ou) > 0:
137
- response = org_client.list_parents(
138
- ChildId=ou["Id"],
26
+ Args:
27
+ region: AWS Region
28
+
29
+ Returns:
30
+ Organization details
31
+
32
+ Raises:
33
+ AWSServiceError: If the API call fails
34
+ """
35
+ client_manager = get_client_manager(region=region)
36
+ progress = get_progress_tracker()
37
+
38
+ progress.show_success("🏢 Getting Organization Info")
39
+
40
+ try:
41
+ response = client_manager.call_api("organizations", "describe_organization")
42
+ validate_aws_response(response, ["Organization"])
43
+
44
+ organization = response["Organization"]
45
+ logger.info(f"Retrieved organization: {organization.get('Id', 'Unknown')}")
46
+ return organization
47
+
48
+ except Exception as e:
49
+ logger.error(f"Failed to describe organization: {e}")
50
+ raise AWSServiceError(f"Failed to describe organization: {e}")
51
+
52
+
53
+ def list_roots(region: str) -> List[Dict[str, Any]]:
54
+ """
55
+ List the organization roots.
56
+
57
+ Args:
58
+ region: AWS Region
59
+
60
+ Returns:
61
+ List of root objects
62
+ """
63
+ client_manager = get_client_manager(region=region)
64
+
65
+ try:
66
+ response = client_manager.call_api("organizations", "list_roots")
67
+ validate_aws_response(response, ["Roots"])
68
+
69
+ roots = response["Roots"]
70
+ logger.info(f"Found {len(roots)} organization roots")
71
+ return roots
72
+
73
+ except Exception as e:
74
+ logger.error(f"Failed to list roots: {e}")
75
+ raise AWSServiceError(f"Failed to list roots: {e}")
76
+
77
+
78
+ def list_organizational_units(parent_id: str, region: str) -> List[Dict[str, Any]]:
79
+ """
80
+ List organizational units recursively.
81
+
82
+ Args:
83
+ parent_id: Parent ID to start from
84
+ region: AWS region
85
+
86
+ Returns:
87
+ List of all organizational units
88
+ """
89
+ client_manager = get_client_manager(region=region)
90
+ config = get_config()
91
+ all_ous = []
92
+
93
+ def _get_ous_recursive(current_parent_id: str, depth: int = 0) -> None:
94
+ """Recursively get all OUs."""
95
+ if depth > 10: # Prevent infinite recursion
96
+ logger.warning(f"Maximum recursion depth reached for parent {current_parent_id}")
97
+ return
98
+
99
+ try:
100
+ # Get OUs for current parent
101
+ ous = client_manager.paginate_api_call(
102
+ "organizations",
103
+ "list_organizational_units_for_parent",
104
+ "OrganizationalUnits",
105
+ ParentId=current_parent_id,
106
+ PaginationConfig={
107
+ 'MaxItems': config.pagination.max_items,
108
+ 'PageSize': config.pagination.default_page_size
109
+ }
139
110
  )
140
- logging.debug(response["Parents"])
141
- ou["Parents"] = response["Parents"]
142
- return list_ous
143
-
144
-
145
- def list_accounts_pag(region, next_token: str = None):
146
- """
147
- List accounts with pagination.
148
-
149
- :param region:
150
- :param next_token:
151
- :return:
152
- """
153
- org_client = client("organizations", region_name=region)
154
- paginator = org_client.get_paginator("list_accounts")
155
- response_iterator = paginator.paginate(
156
- PaginationConfig={"MaxItems": 1000, "PageSize": 20, "StartingToken": next_token}
157
- )
158
- response = response_iterator.build_full_result()
159
- logging.info(response_iterator.build_full_result())
160
- return response["Accounts"]
161
-
162
-
163
- def list_accounts(region, org_client):
164
- """
165
- List accounts.
166
-
167
- :param org_client:
168
- :param region:
169
- :return:
170
- """
171
-
172
- accounts = org_client.list_accounts()
173
- logging.info(accounts)
174
- l_account = accounts["Accounts"]
175
- logging.info(len(accounts["Accounts"]))
176
- if len(accounts["Accounts"]) >= 20:
177
- logging.info("Paginating ...")
178
- ad_accounts = list_accounts_pag(region=region, next_token=accounts["NextToken"])
179
- for ad in ad_accounts:
180
- l_account.append(ad)
181
- logging.info(f"You Organizations have {len(l_account)} Accounts")
182
-
183
- return l_account
184
-
185
-
186
- def index_accounts(list_account, region, org_client):
187
- """
188
- Index accounts.
189
-
190
- :param list_account:
191
- :param region:
192
- :return:
193
- """
194
- accounts = []
195
-
196
- for a in list_account:
197
- response = org_client.list_parents(
198
- ChildId=a["Id"],
199
- )
200
-
201
- accounts.append(
202
- {"account": a["Id"], "name": a["Name"], "parents": response["Parents"]}
203
- )
204
- return accounts
205
-
206
-
207
- # create organization complete map
208
- def find_ou_index(ous, search_id):
209
- """
210
- Find OU Name in list.
211
-
212
- :param ous:
213
- :param search_id:
214
- :return:
215
- """
216
- for a in ous:
217
- if a["Id"] == search_id:
218
- return a
219
-
220
-
221
- # search ou in map
222
- def search_ou_map(map_ou: dict, ou_id, level=0, tree="."):
223
- """
224
- Search OU in map.
225
-
226
- :param tree:
227
- :param level:
228
- :param map_ou:
229
- :param ou_id:
230
- :return:
231
- """
232
- for a in map_ou.keys():
233
- # print(f'Searching {ou_id}... in {map_ou[a]["nestedOus"]}')
234
-
235
- if len(map_ou[a]["nestedOus"]) > 0:
236
- level += 1
237
- tree += f".{a}"
238
-
239
- if ou_id in map_ou[a]["nestedOus"].keys():
240
- # search_ou_map(map_ou=map_ou[a]["nestedOus"], ou_id=ou_id, level=level, tree=tree)
241
-
242
- return map_ou[a]
243
- # else:
244
- # search_ou_map(map_ou=map_ou[a]["nestedOus"], ou_id=ou_id, level=level, tree=tree)
245
- return None
246
-
247
-
248
- def init_org_complete(
249
- root_id,
250
- org,
251
- list_ous,
252
- ):
253
- """
254
- Init organization dictionary.
255
-
256
- :param root_id:
257
- :param org:
258
- :param list_ous:
259
- :return:
111
+
112
+ for ou in ous:
113
+ # Add parent information
114
+ parents_response = client_manager.call_api(
115
+ "organizations",
116
+ "list_parents",
117
+ ChildId=ou["Id"]
118
+ )
119
+ ou["Parents"] = parents_response.get("Parents", [])
120
+ all_ous.append(ou)
121
+
122
+ # Recursively get child OUs
123
+ _get_ous_recursive(ou["Id"], depth + 1)
124
+
125
+ except Exception as e:
126
+ logger.warning(f"Failed to get OUs for parent {current_parent_id}: {e}")
127
+
128
+ with track_operation("Listing organizational units") as task_id:
129
+ _get_ous_recursive(parent_id)
130
+
131
+ logger.info(f"Found {len(all_ous)} organizational units")
132
+ return all_ous
133
+
134
+
135
+ def list_accounts(region: str) -> List[Dict[str, Any]]:
136
+ """
137
+ List all accounts in the organization.
138
+
139
+ Args:
140
+ region: AWS region
141
+
142
+ Returns:
143
+ List of account objects with parent information
144
+ """
145
+ client_manager = get_client_manager(region=region)
146
+ config = get_config()
147
+
148
+ with track_operation("Listing organization accounts") as task_id:
149
+ try:
150
+ # Get all accounts using pagination
151
+ accounts = client_manager.paginate_api_call(
152
+ "organizations",
153
+ "list_accounts",
154
+ "Accounts",
155
+ PaginationConfig={
156
+ 'MaxItems': config.pagination.max_items,
157
+ 'PageSize': config.pagination.default_page_size
158
+ }
159
+ )
160
+
161
+ logger.info(f"Found {len(accounts)} accounts in organization")
162
+
163
+ # Add parent information for each account
164
+ indexed_accounts = []
165
+ progress = get_progress_tracker()
166
+
167
+ with progress.track_operation(f"Getting parent info for {len(accounts)} accounts", total=len(accounts)) as parent_task:
168
+ for i, account in enumerate(accounts):
169
+ try:
170
+ parents_response = client_manager.call_api(
171
+ "organizations",
172
+ "list_parents",
173
+ ChildId=account["Id"]
174
+ )
175
+
176
+ indexed_accounts.append({
177
+ "account": account["Id"],
178
+ "name": account["Name"],
179
+ "email": account.get("Email", ""),
180
+ "status": account.get("Status", "ACTIVE"),
181
+ "parents": parents_response.get("Parents", [])
182
+ })
183
+
184
+ progress.update_progress(parent_task)
185
+
186
+ except Exception as e:
187
+ logger.warning(f"Failed to get parents for account {account['Id']}: {e}")
188
+ indexed_accounts.append({
189
+ "account": account["Id"],
190
+ "name": account["Name"],
191
+ "email": account.get("Email", ""),
192
+ "status": account.get("Status", "ACTIVE"),
193
+ "parents": []
194
+ })
195
+
196
+ return indexed_accounts
197
+
198
+ except Exception as e:
199
+ logger.error(f"Failed to list accounts: {e}")
200
+ raise AWSServiceError(f"Failed to list accounts: {e}")
201
+
202
+
203
+ def create_organization_complete_map(
204
+ root_id: str,
205
+ organization: Dict[str, Any],
206
+ organizational_units: List[Dict[str, Any]],
207
+ accounts: List[Dict[str, Any]]
208
+ ) -> Dict[str, Any]:
209
+ """
210
+ Create a complete organization map with nested structure.
211
+
212
+ Args:
213
+ root_id: Organization root ID
214
+ organization: Organization details
215
+ organizational_units: List of OUs
216
+ accounts: List of accounts
217
+
218
+ Returns:
219
+ Complete organization structure
260
220
  """
261
221
  organizations_complete = {
262
222
  "rootId": root_id,
263
- "masterAccountId": org["MasterAccountId"],
223
+ "masterAccountId": organization["MasterAccountId"],
264
224
  "noOutAccounts": [],
265
225
  "organizationalUnits": {},
266
226
  }
267
- # Iterate in ous for getting ous tree
268
- for a, i in zip(list_ous, range(len(list_ous))):
269
- for p in a["Parents"]:
270
- if p["Type"] == "ROOT":
271
- organizations_complete["organizationalUnits"][a["Name"]] = {
272
- "Id": a["Id"],
273
- "Name": a["Name"],
227
+
228
+ # Build OU hierarchy
229
+ for ou in organizational_units:
230
+ for parent in ou.get("Parents", []):
231
+ if parent["Type"] == "ROOT":
232
+ organizations_complete["organizationalUnits"][ou["Name"]] = {
233
+ "Id": ou["Id"],
234
+ "Name": ou["Name"],
274
235
  "accounts": {},
275
236
  "nestedOus": {},
276
237
  }
277
- return organizations_complete
278
-
279
-
280
- # create organization complete map
281
- def map_organizations_complete(
282
- organizations_complete: dict,
283
- list_ous,
284
- llist_accounts,
285
- reference_outs_list,
286
- ):
287
- """
288
- Create complete mapper file.
289
-
290
- :param reference_outs_list:
291
- :param organizations_complete:
292
- :param list_ous:
293
- :param llist_accounts:
294
- :return:
295
- """
296
- # Iterate in ous for getting ous tree
297
- for a, i in zip(list_ous, range(len(list_ous))):
298
- for p in a["Parents"]:
299
- if p["Type"] == "ORGANIZATIONAL_UNIT":
300
- o = find_ou_name(reference_outs_list, p["Id"])
301
-
302
- if o not in organizations_complete["organizationalUnits"].keys():
303
- p = search_ou_map(
304
- organizations_complete["organizationalUnits"], ou_id=o
305
- )
306
- o = p["Name"]
307
-
308
- organizations_complete["organizationalUnits"][o]["nestedOus"][
309
- find_ou_name(reference_outs_list, a["Id"])
310
- ] = {"Id": a["Id"], "Name": a["Name"], "accounts": [], "nestedOus": {}}
311
- # print(organizations_complete["organizationalUnits"][o]["nestedOus"])
312
- if (
313
- len(organizations_complete["organizationalUnits"][o]["nestedOus"])
314
- > 0
315
- ):
316
- new_list_ous = organizations_complete["organizationalUnits"][o][
317
- "nestedOus"
318
- ]
319
-
320
- new_list_ous = plop_dict_out(ous_list=list_ous, ou=new_list_ous)
321
- organizations_complete = map_organizations_complete(
322
- organizations_complete=organizations_complete,
323
- list_ous=new_list_ous,
324
- llist_accounts=llist_accounts,
325
- reference_outs_list=reference_outs_list,
326
- )
327
-
328
- return organizations_complete
329
-
330
-
331
- def plop_dict_out(
332
- ous_list: list,
333
- ou,
334
- ):
335
- """
336
- Clean list.
337
-
338
- :param ous_list:
339
- :param ou:
340
- :return:
341
- """
342
- for o in ou.keys():
343
- # for c in ou.keys():
344
- for unit in ous_list:
345
- if unit["Id"] == ou[o]["Id"]:
346
- ous_list.remove(unit)
347
-
348
- return ous_list
349
-
350
-
351
- def set_accounts_tree(llist_accounts, organizations_complete, list_ous):
352
- """
353
- Set accounts tree.
354
-
355
- :param llist_accounts:
356
- :param organizations_complete:
357
- :param list_ous:
358
- :return:
359
- """
360
- # Iterate in list accounts to get parent ous
361
- for c, i in zip(llist_accounts, range(len(llist_accounts))):
362
- # print(f"\n aa_{i}= OrganizationsAccount(\"{c['account']}\")", file=f)
363
- for p in c["parents"]:
364
- if p["Type"] == "ROOT":
365
- organizations_complete["noOutAccounts"].append(
366
- {"account": c["account"], "name": c["name"]}
367
- )
368
-
369
- for o, j in zip(list_ous, range(len(list_ous))):
370
- if p["Id"] == o["Id"] and p["Type"] == "ORGANIZATIONAL_UNIT":
371
- organizations_complete["organizationalUnits"][
372
- find_ou_name(list_ous, o["Id"])
373
- ]["accounts"][c["name"]] = {
374
- "account": c["account"],
375
- "name": c["name"],
238
+
239
+ # Add accounts to appropriate OUs or root
240
+ for account in accounts:
241
+ for parent in account.get("parents", []):
242
+ if parent["Type"] == "ROOT":
243
+ organizations_complete["noOutAccounts"].append({
244
+ "account": account["account"],
245
+ "name": account["name"]
246
+ })
247
+ elif parent["Type"] == "ORGANIZATIONAL_UNIT":
248
+ # Find the OU name
249
+ ou_name = find_ou_name(organizational_units, parent["Id"])
250
+ if ou_name and ou_name in organizations_complete["organizationalUnits"]:
251
+ organizations_complete["organizationalUnits"][ou_name]["accounts"][account["name"]] = {
252
+ "account": account["account"],
253
+ "name": account["name"]
376
254
  }
377
-
255
+
378
256
  return organizations_complete
379
257
 
380
258
 
381
- def graph_organizations(diagrams_path, region, auto):
259
+ def graph_organizations(diagrams_path: str, region: str, auto: bool) -> None:
382
260
  """
383
- Create organizations Graph.
261
+ Create organizations graph with improved error handling and progress tracking.
384
262
 
385
- :param auto:
386
- :param diagrams_path:
387
- :param region:
388
- :return:
263
+ Args:
264
+ diagrams_path: Output directory path
265
+ region: AWS region
266
+ auto: Whether to automatically create diagrams
389
267
  """
390
268
  template_file = "graph_org.py"
391
- code_path = f"{diagrams_path}/code"
392
- json_path = f"{diagrams_path}/json"
393
- create_file(
394
- template_content=graph_template,
395
- file_name=template_file,
396
- directory_path=code_path,
397
- )
398
- org_client = client("organizations", region_name=region)
399
- organization = describe_organization(region=region, org_client=org_client)
400
- print(Fore.BLUE + emoji.emojize(":sparkle: Getting Organization Info" + Fore.RESET))
401
- logging.debug(organization)
402
- logging.debug("The Roots Info")
403
- roots = list_roots(region=region)
404
- logging.debug(roots)
405
-
406
- print(
407
- Fore.BLUE + emoji.emojize(":sparkle: List Organizational Units " + Fore.RESET)
408
- )
409
- logging.debug("The Organizational Units list ")
410
-
411
- ous = list_organizational_units(parent_id=roots[0]["Id"], region=region, org_client=org_client)
412
- logging.debug(ous)
413
- logging.debug("The Organizational Units list with parents info")
414
- i_ous = index_ous(ous, region=region)
415
- logging.debug(i_ous)
416
-
417
- print(
418
- Fore.BLUE
419
- + emoji.emojize(":sparkle: Getting the Account list info" + Fore.RESET)
420
- )
421
-
422
- l_accounts = list_accounts(region=region, org_client=org_client)
423
- logging.debug(l_accounts)
424
- logging.debug("The Account list with parents info")
425
-
426
- print(
427
- Fore.YELLOW
428
- + emoji.emojize(
429
- f":information: There are {len(l_accounts)} Accounts in your organization"
430
- + Fore.RESET
269
+ code_path = Path(diagrams_path) / "code"
270
+ json_path = Path(diagrams_path) / "json"
271
+
272
+ # Ensure directories exist
273
+ code_path.mkdir(parents=True, exist_ok=True)
274
+ json_path.mkdir(parents=True, exist_ok=True)
275
+
276
+ progress = get_progress_tracker()
277
+
278
+ try:
279
+ # Create template file
280
+ create_file(
281
+ template_content=graph_template,
282
+ file_name=template_file,
283
+ directory_path=str(code_path),
431
284
  )
432
- )
433
-
434
- i_accounts = index_accounts(l_accounts, region=region, org_client=org_client)
435
- logging.debug(i_accounts)
436
-
437
- file_name = "organizations.json"
438
- save_results(results=i_accounts, filename=file_name, directory_path=json_path)
439
-
440
- create_mapper(
441
- template_file=f"{code_path}/{template_file}",
442
- org=organization,
443
- root_id=roots[0]["Id"],
444
- list_ous=ous,
445
- list_accounts=i_accounts,
446
- )
447
- file_name = "organizations_complete.json"
448
- # view in console
449
- organizations_complete_f = map_organizations_complete(
450
- organizations_complete=init_org_complete(
451
- org=organization, root_id=roots[0]["Id"], list_ous=i_ous
452
- ),
453
- llist_accounts=i_accounts,
454
- list_ous=i_ous,
455
- reference_outs_list=i_ous.copy(),
456
- )
457
- organizations_complete_f = (
458
- set_accounts_tree(
459
- llist_accounts=i_accounts,
460
- organizations_complete=organizations_complete_f,
461
- list_ous=i_ous,
462
- ),
463
- )
464
-
465
- save_results(results=organizations_complete_f, filename=f"{json_path}/{file_name}")
466
-
467
- if auto:
468
- print(f"{Fore.GREEN}❇️ Creating diagrams in {code_path}")
469
- command = os.system(f"cd {code_path} && python3 {template_file}")
470
- if command != 0:
471
- print(Fore.RED + "❌ Error creating diagrams")
472
- print(command)
473
-
474
- else:
475
- print(
476
- Fore.YELLOW
477
- + emoji.emojize(
478
- f":sparkles: Run -> python3 {code_path}/graph_org.py " + Fore.RESET
479
- )
285
+
286
+ # Get organization data
287
+ organization = describe_organization(region=region)
288
+ roots = list_roots(region=region)
289
+
290
+ if not roots:
291
+ raise AWSServiceError("No organization roots found")
292
+
293
+ root_id = roots[0]["Id"]
294
+
295
+ # Get organizational units
296
+ progress.show_success("📋 Listing Organizational Units")
297
+ organizational_units = list_organizational_units(parent_id=root_id, region=region)
298
+
299
+ # Get accounts
300
+ progress.show_success("👥 Getting Account Information")
301
+ accounts = list_accounts(region=region)
302
+
303
+ progress.show_summary(
304
+ "Organization Summary",
305
+ [
306
+ f"Organization ID: {organization.get('Id', 'Unknown')}",
307
+ f"Master Account: {organization.get('MasterAccountId', 'Unknown')}",
308
+ f"Organizational Units: {len(organizational_units)}",
309
+ f"Accounts: {len(accounts)}"
310
+ ]
311
+ )
312
+
313
+ # Save basic accounts data
314
+ save_results(
315
+ results=accounts,
316
+ filename="organizations.json",
317
+ directory_path=str(json_path)
318
+ )
319
+
320
+ # Create diagram mapper
321
+ create_mapper(
322
+ template_file=str(code_path / template_file),
323
+ org=organization,
324
+ root_id=root_id,
325
+ list_ous=organizational_units,
326
+ list_accounts=accounts,
480
327
  )
328
+
329
+ # Create complete organization map
330
+ organizations_complete = create_organization_complete_map(
331
+ root_id=root_id,
332
+ organization=organization,
333
+ organizational_units=organizational_units,
334
+ accounts=accounts
335
+ )
336
+
337
+ save_results(
338
+ results=organizations_complete,
339
+ filename="organizations_complete.json",
340
+ directory_path=str(json_path)
341
+ )
342
+
343
+ if auto:
344
+ progress.show_success(f"🎨 Creating diagrams in {code_path}")
345
+ command = os.system(f"cd {code_path} && python3 {template_file}")
346
+ if command != 0:
347
+ progress.show_error("Failed to create diagrams", f"Command exit code: {command}")
348
+ else:
349
+ progress.show_success("Diagrams created successfully")
350
+ else:
351
+ progress.show_success(
352
+ "Ready to create diagrams",
353
+ f"Run: python3 {code_path / template_file}"
354
+ )
355
+
356
+ except Exception as e:
357
+ logger.error(f"Failed to create organization graph: {e}")
358
+ progress.show_error("Organization diagram generation failed", str(e))
359
+ raise