17
17
from logger import configure_logger
18
18
from paginator import paginator
19
19
from partition import get_organization_api_region
20
+ from cache import Cache
20
21
21
22
LOGGER = configure_logger (__name__ )
22
23
AWS_REGION = os .getenv ("AWS_REGION" )
@@ -38,7 +39,7 @@ class Organizations: # pylint: disable=R0904
38
39
)
39
40
40
41
def __init__ (
41
- self , role = None , account_id = None , org_client = None , tagging_client = None
42
+ self , role = None , account_id = None , org_client = None , tagging_client = None , cache = None
42
43
):
43
44
if role :
44
45
LOGGER .warning (
@@ -69,8 +70,8 @@ def __init__(
69
70
if not tagging_client
70
71
else tagging_client
71
72
)
73
+ self .cache = cache or Cache ()
72
74
self .account_id = account_id
73
- self .root_id = None
74
75
75
76
def get_parent_info (self , account_id = None ):
76
77
"""
@@ -302,7 +303,9 @@ def get_accounts(self, protected_ou_ids=None, include_root=True):
302
303
return accounts
303
304
304
305
def get_organization_info (self ):
305
- response = self .client .describe_organization ()
306
+ if not self .cache .exists ('organization' ):
307
+ self .cache .add ('organization' , self .client .describe_organization ())
308
+ response = self .cache .get ('organization' )
306
309
return {
307
310
"organization_management_account_id" : (
308
311
response
@@ -315,19 +318,26 @@ def get_organization_info(self):
315
318
316
319
def describe_ou_name (self , ou_id ):
317
320
try :
318
- response = self .client .describe_organizational_unit (
319
- OrganizationalUnitId = ou_id
320
- )
321
- return response ["OrganizationalUnit" ]["Name" ]
321
+ cache_key = f'ou_name_{ ou_id } '
322
+ if not self .cache .exists (cache_key ):
323
+ response = self .client .describe_organizational_unit (
324
+ OrganizationalUnitId = ou_id
325
+ )
326
+ self .cache .add (cache_key , response ["OrganizationalUnit" ]["Name" ])
327
+ return self .cache .get (cache_key )
328
+
322
329
except ClientError as error :
323
330
raise RootOUIDError (
324
331
"OU is the Root of the Organization" ,
325
332
) from error
326
333
327
334
def describe_account_name (self , account_id ):
328
335
try :
329
- response = self .client .describe_account (AccountId = account_id )
330
- return response ["Account" ]["Name" ]
336
+ cache_key = f'account_name_{ account_id } '
337
+ if not self .cache .exists (cache_key ):
338
+ response = self .client .describe_account (AccountId = account_id )
339
+ self .cache .add (cache_key , response ["Account" ]["Name" ])
340
+ return self .cache .get (cache_key )
331
341
except ClientError as error :
332
342
LOGGER .error (
333
343
"Failed to retrieve account name for account ID %s" ,
@@ -340,7 +350,10 @@ def determine_ou_path(ou_path, ou_child_name):
340
350
return f"{ ou_path } /{ ou_child_name } " if ou_path else ou_child_name
341
351
342
352
def list_parents (self , ou_id ):
343
- return self .client .list_parents (ChildId = ou_id ).get ("Parents" )[0 ]
353
+ cache_key = f'parents_{ ou_id } '
354
+ if not self .cache .exists (cache_key ):
355
+ self .cache .add (cache_key , self .client .list_parents (ChildId = ou_id ).get ("Parents" )[0 ])
356
+ return self .cache .get (cache_key )
344
357
345
358
def get_accounts_for_parent (self , parent_id ):
346
359
return paginator (self .client .list_accounts_for_parent , ParentId = parent_id )
@@ -351,7 +364,9 @@ def get_child_ous(self, parent_id):
351
364
)
352
365
353
366
def get_ou_root_id (self ):
354
- return self .client .list_roots ().get ("Roots" )[0 ].get ("Id" )
367
+ if not self .cache .exists ('root_id' ):
368
+ self .cache .add ('root_id' , self .client .list_roots ().get ("Roots" )[0 ].get ("Id" ))
369
+ return self .cache .get ('root_id' )
355
370
356
371
def ou_path_to_id (self , path ):
357
372
nested_dir_paths = path .split ('/' )[1 :]
@@ -399,23 +414,17 @@ def get_accounts_in_path(
399
414
)
400
415
return accounts
401
416
402
- def build_account_path (self , ou_id , account_path , cache ):
417
+ def build_account_path (self , ou_id , account_path ):
403
418
"""
404
419
Builds a path tree to the account from the root of the Organization
405
420
"""
406
421
current = self .list_parents (ou_id )
407
422
408
423
# While not at the root of the Organization
409
424
while current .get ("Type" ) != "ROOT" :
410
- # check cache for ou name of id
411
- if not cache .exists (current .get ("Id" )):
412
- cache .add (
413
- current .get ("Id" ),
414
- self .describe_ou_name (current .get ("Id" )),
415
- )
416
- ou_name = cache .get (current .get ("Id" ))
425
+ ou_name = self .describe_ou_name (current .get ("Id" ))
417
426
account_path .append (ou_name )
418
- return self .build_account_path (current .get ("Id" ), account_path , cache )
427
+ return self .build_account_path (current .get ("Id" ), account_path )
419
428
return Organizations .determine_ou_path (
420
429
"/" .join (list (reversed (account_path ))),
421
430
self .describe_ou_name (self .get_parent_info ().get ("ou_parent_id" )),
@@ -440,16 +449,23 @@ def get_account_ids_for_tags(self, tags):
440
449
account_ids .append (account_id )
441
450
return account_ids
442
451
443
- def list_organizational_units_for_parent (self , parent_ou ):
444
- organizational_units = [
452
+ def _list_organizational_units_for_parent (self , parent_ou ):
453
+ return [
445
454
ou
446
455
for org_units in (
447
456
self .client .get_paginator ("list_organizational_units_for_parent" )
448
457
.paginate (ParentId = parent_ou )
449
458
)
450
459
for ou in org_units ["OrganizationalUnits" ]
451
460
]
452
- return organizational_units
461
+
462
+ def list_organizational_units_for_parent (self , parent_ou ):
463
+ LOGGER .debug ('Looking for children in %s' , parent_ou )
464
+ cache_key = f'children_{ parent_ou } '
465
+ if not self .cache .exists (cache_key ):
466
+ LOGGER .debug ('Cache MISS for children of OU %s' , parent_ou )
467
+ self .cache .add (cache_key , self ._list_organizational_units_for_parent (parent_ou ))
468
+ return self .cache .get (cache_key )
453
469
454
470
def get_account_id (self , account_name ):
455
471
for account in self .list_accounts ():
@@ -462,21 +478,22 @@ def list_accounts(self):
462
478
"""
463
479
Retrieves all accounts in organization.
464
480
"""
465
- existing_accounts = [
466
- account
467
- for accounts in self .client .get_paginator ("list_accounts" ).paginate ()
468
- for account in accounts ["Accounts" ]
469
- ]
470
- return existing_accounts
481
+ if not self .cache .exists ('accounts' ):
482
+ self .cache .add ('accounts' , [
483
+ account
484
+ for accounts in self .client .get_paginator ("list_accounts" ).paginate ()
485
+ for account in accounts ["Accounts" ]
486
+ ])
487
+ return self .cache .get ('accounts' )
471
488
472
489
def get_ou_id (self , ou_path , parent_ou_id = None ):
473
490
# Return root OU if '/' is provided
474
491
if ou_path .strip () == "/" :
475
- return self .root_id
492
+ return self .get_ou_root_id ()
476
493
477
494
# Set initial OU to start looking for given ou_path
478
495
if parent_ou_id is None :
479
- parent_ou_id = self .root_id
496
+ parent_ou_id = self .get_ou_root_id ()
480
497
481
498
# Parse ou_path and find the ID
482
499
ou_hierarchy = ou_path .strip ("/" ).split ("/" )
@@ -497,7 +514,6 @@ def get_ou_id(self, ou_path, parent_ou_id=None):
497
514
return parent_ou_id
498
515
499
516
def move_account (self , account_id , ou_path ):
500
- self .root_id = self .get_ou_root_id ()
501
517
ou_id = self .get_ou_id (ou_path )
502
518
response = self .client .list_parents (ChildId = account_id )
503
519
source_parent_id = response ["Parents" ][0 ]["Id" ]
0 commit comments