except ClientError as my_Error: logging.critical("Failed to capture Config Recorder and Delivery Channels") Step2Success = False print(my_Error) for i in range(len(ConfigList)): logging.error( Fore.RED + "I found a config recorder for account %s in region %s", ConfigList[i]['AccountID'], ConfigList[i]['Region'] + Fore.RESET) Step2Success = False Issues += 1 if DeletionRun: logging.warning("Deleting %s in account %s in region %s", ConfigList[i]['Name'], ConfigList[i]['AccountID'], ConfigList[i]['Region']) DelConfigRecorder = Inventory_Modules.del_config_recorder( account_credentials, region, ConfigList[i]['Name']) Issues -= 1 for i in range(len(DeliveryChanList)): logging.error( Fore.RED + "I found a delivery channel for account %s in region %s", DeliveryChanList[i]['AccountID'], DeliveryChanList[i]['Region'] + Fore.RESET) Step2Success = False Issues += 1 if DeletionRun: logging.warning("Deleting %s in account %s in region %s", DeliveryChanList[i]['Name'], DeliveryChanList[i]['AccountID'], DeliveryChanList[i]['Region']) DelDeliveryChannel = Inventory_Modules.del_delivery_channel( account_credentials, region, DeliveryChanList[i]['Name'])
def DoSteps(fChildAccountId, fProfile, fFixRun, fRegionList): def InitDict(StepCount): fProcessStatus = {} # fProcessStatus['ChildAccountIsReady']=True # fProcessStatus['IssuesFound']=0 # fProcessStatus['IssuesFixed']=0 for item in range(StepCount): Step = 'Step' + str(item) fProcessStatus[Step] = {} fProcessStatus[Step]['Success'] = True fProcessStatus[Step]['IssuesFound'] = 0 fProcessStatus[Step]['IssuesFixed'] = 0 fProcessStatus[Step]['ProblemsFound'] = [] return (fProcessStatus) NumOfSteps = 11 #Step 0 ProcessStatus = InitDict(NumOfSteps) Step = 'Step0' CTRoles = [ 'AWSControlTowerExecution', 'AWSCloudFormationStackSetExecutionRole', 'Owner' ] role = CTRoles[0] json_formatted_str_TP = "" print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print( "Confirming we have the necessary cross-account access to account {}". format(fChildAccountId)) try: account_credentials, role = Inventory_Modules.get_child_access2( fProfile, fChildAccountId, 'us-east-1', CTRoles) if role.find("failed") > 0: print( Fore.RED, "We weren't able to connect to the Child Account from this Management Account. Please check the role Trust Policy and re-run this script.", Fore.RESET) print( "The following list of roles were tried, but none were allowed access to account {} using the {} profile" .format(fChildAccountId, fProfile)) print(Fore.RED, account_credentials, Fore.RESET) ProcessStatus[Step]['Success'] = False sys.exit("Exiting due to cross-account access failure") except ClientError as my_Error: if str(my_Error).find("AuthFailure") > 0: print("{}: Authorization Failure for account {}".format( fProfile, fChildAccountId)) print( "The child account MUST allow access into the IAM role {} from the Organization's Management Account for the rest of this script (and the overall migration) to run." .format(role)) print( "You must add the following lines to the Trust Policy of that role in the child account" ) print(json_formatted_str_TP) print(my_Error) ProcessStatus[Step]['Success'] = False sys.exit("Exiting due to Authorization Failure...") elif str(my_Error).find("AccessDenied") > 0: print("{}: Access Denied Failure for account {}".format( fProfile, fChildAccountId)) print( "The child account MUST allow access into the IAM role {} from the Organization's Management Account for the rest of this script (and the overall migration) to run." .format(role)) print( "You must add the following lines to the Trust Policy of that role in the child account" ) print(json_formatted_str_TP) print(my_Error) ProcessStatus[Step]['Success'] = False sys.exit("Exiting due to Access Denied Failure...") else: print("{}: Other kind of failure for account {}".format( fProfile, fChildAccountId)) print(my_Error) ProcessStatus[Step]['Success'] = False sys.exit("Exiting for other failure...") account_credentials['AccountNumber'] = fChildAccountId logging.error("Was able to successfully connect using the credentials... ") print() calling_creds = Inventory_Modules.find_calling_identity(fProfile) print("Confirmed the role " + Fore.GREEN + role + Fore.RESET + " exists in account " + Fore.GREEN + fChildAccountId + Fore.RESET + " and trusts " + Fore.GREEN + "{}".format(calling_creds['Short']) + Fore.RESET + " within the Management Account") print(Fore.GREEN + "** Step 0 completed without issues" + Fore.RESET) print() """ # Step 1 -- Obsoleted due to Control Tower no longer checking this -- # This part will find and delete the Default VPCs in each region for the child account. We only delete if you provided that in the parameters list. If you're really interested in the code that used to be here - check out the "ALZ_CheckAccount.py" script; the code is still in there. """ # Step 2 # This part will check the Config Recorder and Delivery Channel. If they have one, we need to delete it, so we can create another. We'll ask whether this is ok before we delete. Step = 'Step2' try: # fRegionList=Inventory_Modules.get_service_regions('config', 'all') print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print( " Checking account {} for a Config Recorders and Delivery Channels in any region" .format(fChildAccountId)) ConfigList = [] DeliveryChanList = [] """ TO-DO: Need to find a way to gracefully handle the error processing of opt-in regions. Until then - we're using a hard-coded listing of regions, instead of dynamically finding those. """ # fRegionList.remove('me-south-1') # Opt-in region, which causes a failure if we check and it's not opted-in # fRegionList.remove('ap-east-1') # Opt-in region, which causes a failure if we check and it's not opted-in for region in fRegionList: print( ERASE_LINE, "Checking account {} in region {} for Config Recorder".format( fChildAccountId, region), end='\r') logging.info( "Looking for Config Recorders in account %s from Region %s", fChildAccountId, region) # ConfigRecorder=client_cfg.describe_configuration_recorders() ConfigRecorder = Inventory_Modules.find_config_recorders( account_credentials, region) logging.debug("Tried to capture Config Recorder") if len(ConfigRecorder['ConfigurationRecorders']) > 0: ConfigList.append({ 'Name': ConfigRecorder['ConfigurationRecorders'][0]['name'], 'roleARN': ConfigRecorder['ConfigurationRecorders'][0]['roleARN'], 'AccountID': fChildAccountId, 'Region': region }) print( ERASE_LINE, "Checking account {} in region {} for Delivery Channel".format( fChildAccountId, region), end='\r') DeliveryChannel = Inventory_Modules.find_delivery_channels( account_credentials, region) logging.debug("Tried to capture Delivery Channel") if len(DeliveryChannel['DeliveryChannels']) > 0: DeliveryChanList.append({ 'Name': DeliveryChannel['DeliveryChannels'][0]['name'], 'AccountID': fChildAccountId, 'Region': region }) logging.error( "Checked account %s in %s regions. Found %s issues with Config Recorders and Delivery Channels", fChildAccountId, len(fRegionList), len(ConfigList) + len(DeliveryChanList)) except ClientError as my_Error: logging.warning( "Failed to capture Config Recorder and Delivery Channels") ProcessStatus[Step]['Success'] = False print(my_Error) for i in range(len(ConfigList)): logging.error( Fore.RED + "Found a config recorder for account %s in region %s", ConfigList[i]['AccountID'], ConfigList[i]['Region'] + Fore.RESET) ProcessStatus[Step]['Success'] = False ProcessStatus[Step]['IssuesFound'] += 1 ProcessStatus[Step]['ProblemsFound'].append(ConfigList) if fFixRun: logging.warning("Deleting %s in account %s in region %s", ConfigList[i]['Name'], ConfigList[i]['AccountID'], ConfigList[i]['Region']) DelConfigRecorder = Inventory_Modules.del_config_recorder( account_credentials, ConfigList[i]['Region'], ConfigList[i]['Name']) # We assume the process worked. We should probably NOT assume this. ProcessStatus[Step]['IssuesFixed'] += 1 for i in range(len(DeliveryChanList)): logging.error( Fore.RED + "I found a delivery channel for account %s in region %s", DeliveryChanList[i]['AccountID'], DeliveryChanList[i]['Region'] + Fore.RESET) ProcessStatus[Step]['Success'] = False ProcessStatus[Step]['IssuesFound'] += 1 ProcessStatus[Step]['ProblemsFound'].append(DeliveryChanList) if fFixRun: logging.warning("Deleting %s in account %s in region %s", DeliveryChanList[i]['Name'], DeliveryChanList[i]['AccountID'], DeliveryChanList[i]['Region']) DelDeliveryChannel = Inventory_Modules.del_delivery_channel( account_credentials, ConfigList[i]['Region'], DeliveryChanList[i]['Name']) # We assume the process worked. We should probably NOT assume this. ProcessStatus[Step]['IssuesFixed'] += 1 # ProcessStatus[Step]['ProblemsFound'].append() if ProcessStatus[Step]['Success']: print(ERASE_LINE + Fore.GREEN + "** Step 2 completed with no issues" + Fore.RESET) elif ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step][ 'IssuesFixed'] == 0: print( ERASE_LINE + Fore.GREEN + "** Step 2 found {} issues, but they were fixed by deleting the existing Config Recorders and Delivery Channels" .format(ProcessStatus[Step]['IssuesFound']) + Fore.RESET) ProcessStatus[Step]['Success'] = True elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step][ 'IssuesFixed']: print( ERASE_LINE + Fore.RED + "** Step 2 completed, but there were {} items found that weren't deleted" .format(ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']) + Fore.RESET) else: print(ERASE_LINE + Fore.RED + "** Step 2 completed with blockers found" + Fore.RESET) print() # Step 3 # 3. The account must not have a Cloudtrail Trail name the same name as the CT Trail ("AWS-Landing-Zone-BaselineCloudTrail") Step = 'Step3' try: print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print( " Checking account {} for a specially named CloudTrail in all regions" .format(fChildAccountId)) CTtrails2 = [] for region in fRegionList: print(ERASE_LINE, "Checking account {} in region {} for CloudTrail trails". format(fChildAccountId, region), end='\r') CTtrails, trailname = Inventory_Modules.find_cloudtrails( account_credentials, region, ['aws-controltower-BaselineCloudTrail']) # if 'trailList' in CTtrails.keys(): if len(CTtrails['trailList']) > 0: logging.error( "Unfortunately, we've found a CloudTrail log named %s in account %s in the %s region, which means we'll have to delete it before this account can be adopted.", trailname, fChildAccountId, region) CTtrails2.append(CTtrails['trailList'][0]) ProcessStatus[Step]['Success'] = False except ClientError as my_Error: print(my_Error) ProcessStatus[Step]['Success'] = False for i in range(len(CTtrails2)): logging.error( Fore.RED + "Found a CloudTrail trail for account %s in region %s named %s ", fChildAccountId, CTtrails2[i]['HomeRegion'], trailname + Fore.RESET) ProcessStatus[Step]['IssuesFound'] += 1 ProcessStatus[Step]['ProblemsFound'].append(CTtrails2[i]) if fFixRun: try: logging.error("CloudTrail trail deletion commencing...") delresponse = Inventory_Modules.del_cloudtrails( account_credentials, region, CTtrails2[i]['TrailARN']) ProcessStatus[Step]['IssuesFixed'] += 1 except ClientError as my_Error: print(my_Error) if ProcessStatus[Step]['Success']: print(ERASE_LINE + Fore.GREEN + "** {} completed with no issues".format(Step) + Fore.RESET) elif ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step][ 'IssuesFixed'] == 0: print( ERASE_LINE + Fore.GREEN + "** {} found {} issues, but they were fixed by deleting the existing CloudTrail trail names" .format(Step, ProcessStatus[Step]['IssuesFound']) + Fore.RESET) ProcessStatus[Step]['Success'] = True elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step][ 'IssuesFixed']: print( ERASE_LINE + Fore.RED + "** {} completed, but there were {} trail names found that wasn't deleted" .format( Step, ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']) + Fore.RESET) else: print(ERASE_LINE + Fore.RED + "** {} completed with blockers found".format(Step) + Fore.RESET) print() """ Step 4 # Step 4 -- The lack of or use of GuardDuty isn't a pre-requisite for Control Tower -- # 4. This section checks for a pending guard duty invite. You can also check from the Guard Duty Console Step='Step4' try: print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print(" Checking account {} for any GuardDuty invites".format(fChildAccountId)) GDinvites2=[] for region in fRegionList: logging.error("Checking account %s in region %s for", fChildAccountId, region+Fore.RED+" GuardDuty"+Fore.RESET+" invitations") logging.error("Checking account %s in region %s for GuardDuty invites", fChildAccountId, region) GDinvites=Inventory_Modules.find_gd_invites(account_credentials, region) if len(GDinvites) > 0: for x in range(len(GDinvites['Invitations'])): logging.warning("GD Invite: %s", str(GDinvites['Invitations'][x])) logging.error("Unfortunately, we've found a GuardDuty invitation for account %s in the %s region from account %s, which means we'll have to delete it before this account can be adopted.", fChildAccountId, region, GDinvites['Invitations'][x]['AccountId']) ProcessStatus[Step]['IssuesFound']+=1 GDinvites2.append({ 'AccountId': GDinvites['Invitations'][x]['AccountId'], 'InvitationId': GDinvites['Invitations'][x]['InvitationId'], 'Region': region }) except ClientError as my_Error: print(my_Error) ProcessStatus[Step]['Success']=False for i in range(len(GDinvites2)): logging.error(Fore.RED+"I found a GuardDuty invitation for account %s in region %s from account %s ", fChildAccountId, GDinvites2[i]['Region'], GDinvites2[i]['AccountId']+Fore.RESET) ProcessStatus[Step]['IssuesFound']+=1 ProcessStatus[Step]['Success']=False if fFixRun: for x in range(len(GDinvites2)): try: logging.warning("GuardDuty invite deletion commencing...") delresponse=Inventory_Modules.delete_gd_invites(account_credentials, region, GDinvites2[x]['AccountId']) ProcessStatus[Step]['IssuesFixed']+=1 # We assume the process worked. We should probably NOT assume this. except ClientError as my_Error: print(my_Error) if ProcessStatus[Step]['Success']: print(ERASE_LINE+Fore.GREEN+"** {} completed with no issues".format(Step)+Fore.RESET) elif ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed']==0: print(ERASE_LINE+Fore.GREEN+"** {} found {} guardduty invites, but they were deleted".format(Step,ProcessStatus[Step]['IssuesFound'])+Fore.RESET) ProcessStatus[Step]['Success']=True elif (ProcessStatus[Step]['IssuesFound']>ProcessStatus[Step]['IssuesFixed']): print(ERASE_LINE+Fore.RED+"** {} completed, but there were {} guardduty invites found that couldn't be deleted".format(Step,ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed'])+Fore.RESET) else: print(ERASE_LINE+Fore.RED+"** {} completed with blockers found".format(Step)+Fore.RESET) print() """ # Step 4a # 4a. STS must be active in all regions. You can check from the Account Settings page in IAM. """ TODO We would have already verified this - since we've used STS to connect to each region already for the previous steps. - Except for the "quick" shortcut - which means we probably need to point that out in this section. """ # Step 5 ''' 5. The account must be part of the Organization and the email address being entered into the CT parameters must match the account. If you try to add an email from an account which is not part of the Org, you will get an error that you are not using a unique email address. If it’s part of the Org, CT just finds the account and uses the CFN roles. - If the existing account is to be imported as a Core Account, modify the manifest.yaml file to use it. - If the existing account will be a child account in the Organization, use the AVM launch template through Service Catalog and enter the appropriate configuration parameters. ''' # try: Step = 'Step5' print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print(" Checking that the account is part of the AWS Organization.") OrgAccounts = Inventory_Modules.find_child_accounts2(fProfile) OrgAccountList = [] for y in range(len(OrgAccounts)): OrgAccountList.append(OrgAccounts[y]['AccountId']) if not (fChildAccountId in OrgAccountList): print() print( "Account # {} is not a part of the Organization. This account needs to be moved into the Organization to be adopted into the Landing Zone tool" .format(fChildAccountId)) print("This is easiest done manually right now.") ProcessStatus[Step]['Success'] = False ProcessStatus[Step]['IssuesFound'] += 1 if ProcessStatus[Step]['Success']: print(ERASE_LINE + Fore.GREEN + "** {} completed with no issues".format(Step) + Fore.RESET) elif ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step][ 'IssuesFixed'] == 0: print(ERASE_LINE + Fore.GREEN + "** {} found {} issue, but we were able to fix it".format( Step, ProcessStatus[Step]['IssuesFound']) + Fore.RESET) ProcessStatus[Step]['Success'] = True elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step][ 'IssuesFixed']: print( ERASE_LINE + Fore.RED + "** {} completed, but there was {} blocker found that wasn't fixed" .format( Step, ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']) + Fore.RESET) else: print(ERASE_LINE + Fore.RED + "** {} completed with blockers found".format(Step) + Fore.RESET) print() # Step 6 # 6. The existing account can not be in any of the CT-managed Organizations OUs. By default, these OUs are Core and Applications, but the customer may have chosen different or additional OUs to manage by CT. """ So we'll need to verify that the parent OU of the account is the root of the organization. TODO Here """ Step = 'Step6' try: print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print( " Checking account {} to make sure it's not already in a Control-Tower managed OU" .format(fChildAccountId)) print(" -- Not yet implemented -- ") except ClientError as my_Error: print(my_Error) ProcessStatus[Step]['Success'] = False print() # Step 7 - Check for other resources which have 'controltower' in the name # Checking for SNS Topics Step = 'Step7' try: print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print( " Checking account {} for any SNS topics containing the 'controltower' string" .format(fChildAccountId)) SNSTopics2 = [] for region in fRegionList: logging.error("Checking account %s in region %s for", fChildAccountId, region + Fore.RED + " SNS Topics" + Fore.RESET) print(ERASE_LINE, "Checking account {} in region {} for SNS Topics".format( fChildAccountId, region), end='\r') SNSTopics = Inventory_Modules.find_sns_topics( account_credentials, region, ['controltower', 'ControlTower']) if len(SNSTopics) > 0: for x in range(len(SNSTopics)): logging.warning("SNS Topic: %s", str(SNSTopics[x])) logging.info( "Unfortunately, we've found an SNS Topic for account %s in the %s region, which means we'll have to delete it before this account can be adopted.", fChildAccountId, region) ProcessStatus[Step]['Success'] = False ProcessStatus[Step]['IssuesFound'] += 1 SNSTopics2.append({ 'AccountId': fChildAccountId, 'TopicArn': SNSTopics[x], 'Region': region }) ProcessStatus[Step]['ProblemsFound'].append(SNSTopics2) except ClientError as my_Error: print(my_Error) ProcessStatus[Step]['Success'] = False if ProcessStatus[Step]['Success']: print(ERASE_LINE + Fore.GREEN + "** {} completed with no issues".format(Step) + Fore.RESET) elif ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step][ 'IssuesFixed'] == 0: print( ERASE_LINE + Fore.GREEN + "** {} found {} issues, but we were able to remove the offending SNS Topics" .format(Step, ProcessStatus[Step]['IssuesFound']) + Fore.RESET) ProcessStatus[Step]['Success'] = True elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step][ 'IssuesFixed']: print( ERASE_LINE + Fore.RED + "** {} completed, but there were {} blockers found that wasn't fixed" .format( Step, ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']) + Fore.RESET) else: print(ERASE_LINE + Fore.RED + "** {} completed with blockers found".format(Step) + Fore.RESET) print() # Step 8 # Checking for Lambda functions Step = 'Step8' try: print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print( " Checking account {} for any Lambda functions containing the 'controltower' string" .format(fChildAccountId)) LambdaFunctions2 = [] for region in fRegionList: logging.error( "Checking account %s in region %s for " + Fore.RED + "Lambda functions" + Fore.RESET, fChildAccountId, region) print( ERASE_LINE, "Checking account {} in region {} for Lambda Functions".format( fChildAccountId, region), end='\r') LambdaFunctions = Inventory_Modules.find_lambda_functions( account_credentials, region, ['controltower', 'CpntrolTower']) if len(LambdaFunctions) > 0: logging.info( "Unfortunately, account %s contains %s functions with reserved names, which means we'll have to delete them before this account can be adopted.", fChildAccountId, len(LambdaFunctions)) for x in range(len(LambdaFunctions)): logging.warning("Found Lambda function %s in region %s", LambdaFunctions[x]['FunctionName'], region) ProcessStatus[Step]['Success'] = False ProcessStatus[Step]['IssuesFound'] += 1 LambdaFunctions2.append({ 'AccountId': fChildAccountId, 'FunctionName': LambdaFunctions[x]['FunctionName'], 'FunctionArn': LambdaFunctions[x]['FunctionArn'], 'Role': LambdaFunctions[x]['Role'], 'Region': region }) ProcessStatus[Step]['ProblemsFound'].append( LambdaFunctions2) except ClientError as my_Error: print(my_Error) ProcessStatus[Step]['Success'] = False if ProcessStatus[Step]['Success']: print(ERASE_LINE + Fore.GREEN + "** {} completed with no issues".format(Step) + Fore.RESET) elif ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step][ 'IssuesFixed'] == 0: print( ERASE_LINE + Fore.GREEN + "** {} found {} issues, but we were able to remove the offending Lambda Functions" .format(Step, ProcessStatus[Step]['IssuesFound']) + Fore.RESET) ProcessStatus[Step]['Success'] = True elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step][ 'IssuesFixed']: print( ERASE_LINE + Fore.RED + "** {} completed, but there were {} blockers found that wasn't fixed" .format( Step, ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']) + Fore.RESET) else: print(ERASE_LINE + Fore.RED + "** {} completed with blockers found".format(Step) + Fore.RESET) print() #Step 9 # Checking for Role names Step = 'Step9' try: print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print( " Checking account {} for any Role names containing the 'controltower' string" .format(fChildAccountId)) RoleNames2 = [] logging.error("Checking account %s for", fChildAccountId + Fore.RED + " Role names" + Fore.RESET) RoleNames = Inventory_Modules.find_role_names( account_credentials, 'us-east-1', ['controltower', 'ControlTower']) if len(RoleNames) > 0: logging.info( "Unfortunately, account %s contains %s roles with reserved names, which means we'll have to delete them before this account can be adopted.", fChildAccountId, len(RoleNames)) for x in range(len(RoleNames)): logging.warning("Role Name: %s", str(RoleNames[x])) ProcessStatus[Step]['Success'] = False ProcessStatus[Step]['IssuesFound'] += 1 RoleNames2.append({ 'AccountId': fChildAccountId, 'RoleName': RoleNames[x] }) ProcessStatus[Step]['ProblemsFound'].append(RoleNames2) except ClientError as my_Error: print(my_Error) ProcessStatus[Step]['Success'] = False if ProcessStatus[Step]['Success']: print(ERASE_LINE + Fore.GREEN + "** {} completed with no issues".format(Step) + Fore.RESET) elif ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step][ 'IssuesFixed'] == 0: print( ERASE_LINE + Fore.GREEN + "** {} found {} issues, but we were able to remove the offending IAM roles" .format(Step, ProcessStatus[Step]['IssuesFound']) + Fore.RESET) ProcessStatus[Step]['Success'] = True elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step][ 'IssuesFixed']: print( ERASE_LINE + Fore.RED + "** {} completed, but there were {} blockers found that remain to be fixed" .format( Step, ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']) + Fore.RESET) else: print(ERASE_LINE + Fore.RED + "** {} completed with blockers found".format(Step) + Fore.RESET) print() # Step 10 # 10. The existing account can not have any CloudWatch Log Groups named "controltower" """ So we'll need to find and remove the CloudWatch Log Groups - if there are any. TODO Here """ Step = 'Step10' try: print(Fore.BLUE + "{}:".format(Step) + Fore.RESET) print( "Checking account {} to make sure there are no duplicate CloudWatch Log Groups" .format(fChildAccountId)) LogGroupNames2 = [] for region in fRegionList: logging.error( "Checking account %s for", fChildAccountId + Fore.RED + " duplicate CloudWatch Log Group names" + Fore.RESET) LogGroupNames = Inventory_Modules.find_cw_log_group_names( account_credentials, region, ['controltower', 'ControlTower']) if len(LogGroupNames) > 0: logging.info( "Unfortunately, account %s contains %s log groups with reserved names, which means we'll have to delete them before this account can be adopted.", fChildAccountId, len(LogGroupNames)) for x in range(len(LogGroupNames)): logging.warning("Log Group Name: %s", str(LogGroupNames[x])) ProcessStatus[Step]['Success'] = False ProcessStatus[Step]['IssuesFound'] += 1 LogGroupNames2.append({ 'AccountId': fChildAccountId, 'LogGroupName': LogGroupNames[x] }) ProcessStatus[Step]['ProblemsFound'].append(LogGroupNames2) except ClientError as my_Error: print(my_Error) ProcessStatus[Step]['Success'] = False if ProcessStatus[Step]['Success']: print(ERASE_LINE + Fore.GREEN + "** {} completed with no issues".format(Step) + Fore.RESET) elif ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step][ 'IssuesFixed'] == 0: print( ERASE_LINE + Fore.GREEN + "** {} found {} issues, but we were able to remove the offending IAM roles" .format(Step, ProcessStatus[Step]['IssuesFound']) + Fore.RESET) ProcessStatus[Step]['Success'] = True elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step][ 'IssuesFixed']: print( ERASE_LINE + Fore.RED + "** {} completed, but there were {} blockers found that remain to be fixed" .format( Step, ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']) + Fore.RESET) else: print(ERASE_LINE + Fore.RED + "** {} completed with blockers found".format(Step) + Fore.RESET) print() ##### Function Summary ############# TotalIssuesFound = 0 TotalIssuesFixed = 0 MemberReady = True for item in ProcessStatus: TotalIssuesFound = TotalIssuesFound + ProcessStatus[item]['IssuesFound'] TotalIssuesFixed = TotalIssuesFixed + ProcessStatus[item]['IssuesFixed'] MemberReady = MemberReady and ProcessStatus[item]['Success'] ProcessStatus['AccountId'] = fChildAccountId ProcessStatus['Ready'] = MemberReady ProcessStatus['IssuesFound'] = TotalIssuesFound ProcessStatus['IssuesFixed'] = TotalIssuesFixed return (ProcessStatus)