Best Python code snippet using fMBT_python
CT_CheckAccount.py
Source:CT_CheckAccount.py
1#!/usr/bin/env python32from pprint import pprint3import sys4import Inventory_Modules5import argparse6from colorama import init, Fore7from botocore.exceptions import ClientError8from prettytable import PrettyTable9from ArgumentsClass import CommonArguments10from account_class import aws_acct_access11import logging12init()13parser = CommonArguments()14parser.verbosity()15parser.singleprofile()16parser.multiregion()17parser.my_parser.add_argument(18 "--explain",19 dest="pExplain",20 const=True,21 default=False,22 action="store_const",23 help="This flag prints out the explanation of what this script would do.")24parser.my_parser.add_argument(25 "-a", "--account",26 dest="pChildAccountId",27 metavar="New Account to be adopted into Control Tower",28 default=None,29 help="This is the account number of the account you're checking, to see if it can be adopted into AWS Control Tower.")30parser.my_parser.add_argument(31 "-q", "--quick",32 dest="Quick",33 metavar="Shortcut the checking to only a single region",34 const=True,35 default=False,36 action="store_const",37 help="This flag only checks 'us-east-1', so makes the whole script run really fast.")38parser.my_parser.add_argument(39 "+fix", "+delete",40 dest="FixRun",41 const=True,42 default=False,43 action="store_const",44 help="This will fix the issues found. If default VPCs must be deleted, you'll be asked to confirm.")45# TODO: There should be an additional parameter here that would take a role name for access into the account,46# since it's likely that users won't be able to use the AWSControlTowerExecution role47parser.my_parser.add_argument(48 "+force",49 dest="pVPCConfirm",50 const=True,51 default=False,52 action="store_const",53 help="This will remediate issues found with NO confirmation. You still have to specify the +fix too")54args = parser.my_parser.parse_args()55Quick = args.Quick56pProfile = args.Profile57pRegions = args.Regions58verbose = args.loglevel59pChildAccountId = args.pChildAccountId60FixRun = args.FixRun61pExplain = args.pExplain62pVPCConfirm = args.pVPCConfirm63logging.basicConfig(level=args.loglevel, format="[%(filename)s:%(lineno)s - %(funcName)30s() ] %(message)s")64# This is hard-coded, because this is the listing of regions that are supported by AWS Control Tower.65if Quick:66 RegionList = ['us-east-1']67else:68 # RegionList=Inventory_Modules.get_ec2_regions('all', pProfile)69 # ap-northeast-3 doesn't support Config (and therefore doesn't support Control Tower),70 # but is a region that is normally included within EC2. Therefore - this is easier.71 # Updated as of November 24, 2021 to the regions supported by AWS Control Tower.72 # So - the enroll OU feature doesn't only limit its checks to the regions supported by Control Tower,73 # which is why this script failed to prepare accounts before.74 # I've updated the list here, but we do need to replace this list with a dynamically generated one.75 RegionList = ['us-east-1', 'us-east-2', 'us-west-2', 'us-west-1',76 'eu-central-1', 'eu-north-1', 'eu-west-1', 'eu-west-2', 'eu-west-3',77 'ap-northeast-1', 'ap-northeast-2', 'ap-northeast-3', 'ap-south-1', 'ap-southeast-1', 'ap-southeast-2',78 'ca-central-1', 'sa-east-1']79 # TODO: 'ap-east-1' isn't included here, because it's an opt-in region, which we can't include if we haven't opted-in.80 # TODO: This is a larger problem of Control Tower not publishing its regions via an API like other services do.81ERASE_LINE = '\x1b[2K'82ExplainMessage = """83Objective: This script aims to identify issues and make it easier to "adopt" an existing account into a Control Tower environment.840. The targeted account MUST allow the Management account access into the Child IAM role called "AWSControlTowerExecution" or another coded role, so that we have access to do read-only operations (by default).850a. There must be an "AWSControlTowerExecution" role present in the account so that StackSets can assume it and deploy stack instances. This role must trust the Organizations Management account or at least the necessary Lambda functions.86** TODO ** - update the JSON to be able to update the role to ensure it trusts the least privileged roles from management account, instead of the whole account.870b. STS must be active in all regions checked. You can check from the Account Settings page in IAM. Since we're using STS to connect to the account from the Management, this requirement is checked by successfully completing step 0.881. Previously - this was a default VPC check, but this is no longer needed.892. There must be no active config channel and recorder in the account as âthere can be only oneâ of each. This must also be deleted via CLI, not console, switching config off in the console is NOT good enough and just disables it. To Delete the delivery channel and the configuration recorder (can be done via CLI and Python script only):90aws configservice describe-delivery-channels91aws configservice describe-delivery-channel-status92aws configservice describe-configuration-recorders93aws configservice stop-configuration-recorder --configuration-recorder-name <NAME-FROM-DESCRIBE-OUTPUT>94aws configservice delete-delivery-channel --delivery-channel-name <NAME-FROM-DESCRIBE-OUTPUT>95aws configservice delete-configuration-recorder --configuration-recorder-name <NAME-FROM-DESCRIBE-OUTPUT963. The account must not have a Cloudtrail Trail name with 'ControlTower' in the name ("aws-controltower-BaselineCloudTrail")974. The account must not have a pending guard duty invite. You can check from the Guard Duty Console985. 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.99** TODO ** - If the existing account will be a child account in the Organization, use the Account Factory and enter the appropriate email address.1006. 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.101-- not yet implemented --1027. SNS topics name containing "ControlTower"1038. Lambda Functions name containing "ControlTower"1049. Role name containing "ControlTower"105Bucket created for AWS Config -- not yet implemented106SNS topic created for AWS Config -- not yet implemented10710. CloudWatch Log group containing "aws-controltower/CloudTrailLogs" -- not yet implemented --108"""109if pExplain:110 print(ExplainMessage)111 sys.exit("Exiting after Script Explanation...")112aws_acct = aws_acct_access(pProfile)113logging.info(f"Confirming that this profile {pProfile} represents a Management Account")114if aws_acct.AccountType.lower() == 'root' and pChildAccountId is None:115 # Creates a list of the account numbers in the Org.116 ChildAccountList = [d['AccountId'] for d in aws_acct.ChildAccounts]117 print(f"Since you didn't specify a specific account, we'll check all {len(aws_acct.ChildAccounts)} accounts in the Org.")118elif aws_acct.AccountType.lower() == 'root' and pChildAccountId is not None:119 print(f"Account {aws_acct.acct_number} is a {aws_acct.AccountType} account.\n"120 f"We're specifically checking to validate that account {pChildAccountId} can be adopted into the Landing Zone")121 ChildAccountList = [pChildAccountId]122else:123 sys.exit(f"Account {aws_acct.acct_number} is a {aws_acct.AccountType} account.\n"124 f" This script should be run with Management Account credentials.")125print()126# Step 0 -127# 0. The Child account MUST allow the Management account access into the Child IAM role called "AWSControlTowerExecution"128if verbose < 50:129 print("This script does the following... ")130 print(f"{Fore.BLUE} 0.{Fore.RESET} Checks to ensure you have the necessary cross-account role access to the child account.")131 print(f"{Fore.BLUE} 1.{Fore.RESET} This check previously checked for default VPCs, but has since been removed.")132 print(f"{Fore.BLUE} 2.{Fore.RESET} Checks the child account in each of the regions")133 print(f" to see if there's already a {Fore.RED}Config Recorder and Delivery Channel {Fore.RESET}enabled...")134 print(f"{Fore.BLUE} 3.{Fore.RESET} Checks that there isn't a duplicate {Fore.RED}CloudTrail{Fore.RESET} trail in the account.")135 print(f"{Fore.BLUE} 4.{Fore.RESET} This check previously checked for the presence of GuardDuty within this account, but has since been removed.")136 # print(Fore.BLUE+" 4."+Fore.RESET+" Checks to see if "+Fore.RED+"GuardDuty"+Fore.RESET+" has been enabled for this child account.")137 # print(" If it has been, it needs to be deleted before we can adopt this new account into the Control Tower Organization.")138 print(f"{Fore.BLUE} 5.{Fore.RESET} This child account {Fore.RED}must exist{Fore.RESET} within the Parent Organization.")139 print(" If it doesn't - then you must move it into this Org - this script can't do that for you.")140 print(f"{Fore.BLUE} 6.{Fore.RESET} The target account {Fore.RED}can't exist{Fore.RESET} within an already managed OU.")141 print(" If it does - then you're already managing this account with Control Tower and just don't know it.")142 print(f"{Fore.BLUE} 7.{Fore.RESET} Looking for {Fore.RED}SNS Topics{Fore.RESET} with duplicate names.")143 print(" If found, we can delete them, but you probably want to do that manually - to be sure.")144 print(f"{Fore.BLUE} 8.{Fore.RESET} Looking for {Fore.RED}Lambda Functions{Fore.RESET} with duplicate names.")145 print(" If found, we can delete them, but you probably want to do that manually - to be sure.")146 print(f"{Fore.BLUE} 9.{Fore.RESET} Looking for {Fore.RED}IAM Roles{Fore.RESET} with duplicate names.")147 print(" If found, we can delete them, but you probably want to do that manually - to be sure.")148 print(f"{Fore.BLUE} 10.{Fore.RESET} Looking for duplicate {Fore.RED}CloudWatch Log Groups.{Fore.RESET}")149 print(" If found, we can delete them, but you probably want to do that manually - to be sure.")150 print()151 print("Since this script is fairly new - All comments or suggestions are enthusiastically encouraged")152 print()153def DoSteps(fChildAccountId, aws_account, fFixRun, fRegionList):154 def InitDict(StepCount):155 fProcessStatus = {}156 # fProcessStatus['ChildAccountIsReady']=True157 # fProcessStatus['IssuesFound']=0158 # fProcessStatus['IssuesFixed']=0159 for item in range(StepCount):160 Step = f"Step{str(item)}"161 fProcessStatus[Step] = {}162 fProcessStatus[Step]['Success'] = True163 fProcessStatus[Step]['IssuesFound'] = 0164 fProcessStatus[Step]['IssuesFixed'] = 0165 fProcessStatus[Step]['ProblemsFound'] = []166 return (fProcessStatus)167 NumOfSteps = 11168 # Step 0169 ProcessStatus = InitDict(NumOfSteps)170 Step = 'Step0'171 CTRoles = ['AWSControlTowerExecution', 'AWSCloudFormationStackSetExecutionRole', 'Owner']172 # TODO: I don't use this next variable, but eventually I intend to supply the JSON code needed to update a role with.173 json_formatted_str_TP = ""174 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")175 print(f"Confirming we have the necessary cross-account access to account {fChildAccountId}")176 try:177 account_credentials = Inventory_Modules.get_child_access3(aws_account, fChildAccountId, 'us-east-1', CTRoles)178 except ClientError as my_Error:179 if str(my_Error).find("AuthFailure") > 0:180 # TODO: This whole section is waiting on an enhancement. Until then, we have to assume that ProServe or someone familiar with Control Tower is running this script181 print(f"{aws_account.acct_number}: Authorization Failure for account {fChildAccountId}")182 print("The child account MUST allow access into the proper IAM role from the Organization's Management Account for the rest of this script (and the overall migration) to run.")183 print("You must add the following lines to the Trust Policy of that role in the child account")184 print(json_formatted_str_TP)185 print(my_Error)186 ProcessStatus[Step]['Success'] = False187 sys.exit("Exiting due to Authorization Failure...")188 elif str(my_Error).find("AccessDenied") > 0:189 # TODO: This whole section is waiting on an enhancement. Until then, we have to assume that ProServe or someone familiar with Control Tower is running this script190 print(f"{aws_account.acct_number}: Access Denied Failure for account {fChildAccountId}")191 print("The child account MUST allow access into the proper IAM role from the Organization's Management Account for the rest of this script (and the overall migration) to run.")192 print("You must add the following lines to the Trust Policy of that role in the child account")193 print(json_formatted_str_TP)194 print(my_Error)195 ProcessStatus[Step]['Success'] = False196 sys.exit("Exiting due to Access Denied Failure...")197 else:198 print(f"{aws_account.acct_number}: Other kind of failure for account {fChildAccountId}")199 print(my_Error)200 ProcessStatus[Step]['Success'] = False201 sys.exit("Exiting for other failure...")202 finally:203 if account_credentials['AccessError']:204 print(f"{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}")205 print(f"The following list of roles were tried, but none were allowed access to account {fChildAccountId} using the {aws_account.acct_number} profile")206 print(Fore.RED, CTRoles, Fore.RESET)207 logging.debug(account_credentials)208 ProcessStatus[Step]['Success'] = False209 sys.exit("Exiting due to cross-account access failure")210 logging.info("Was able to successfully connect using the credentials... ")211 print()212 print(f"Confirmed the role {Fore.GREEN}{account_credentials['Role']}{Fore.RESET}"213 f" exists in account {Fore.GREEN}{fChildAccountId}{Fore.RESET}"214 f" and trusts {Fore.GREEN}our role{Fore.RESET} within the Management Account")215 print(f"{Fore.GREEN}** Step 0 completed without issues{Fore.RESET}")216 print()217 218 """219 # Step 1 -- Obsoleted due to Control Tower no longer checking this --220 # 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.221 222 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.223 224 """225 226 # Step 2227 # 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.228 Step = 'Step2'229 try:230 # fRegionList=Inventory_Modules.get_service_regions('config', 'all')231 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")232 print(f" Checking account {fChildAccountId} for a Config Recorders and Delivery Channels in any region")233 ConfigList = []234 DeliveryChanList = []235 """236 TODO: Need to find a way to gracefully handle the error processing of opt-in regions.237 Until then - we're using a hard-coded listing of regions, instead of dynamically finding those.238 """239 # fRegionList.remove('me-south-1') # Opt-in region, which causes a failure if we check and it's not opted-in240 # fRegionList.remove('ap-east-1') # Opt-in region, which causes a failure if we check and it's not opted-in241 for region in fRegionList:242 print(ERASE_LINE, f"Checking account {fChildAccountId} in region {region} for Config Recorder", end='\r')243 logging.info("Looking for Config Recorders in account %s from Region %s", fChildAccountId, region)244 ConfigRecorder = Inventory_Modules.find_config_recorders2(account_credentials, region)245 logging.debug("Tried to capture Config Recorder")246 if len(ConfigRecorder['ConfigurationRecorders']) > 0:247 ConfigList.append({248 'Name': ConfigRecorder['ConfigurationRecorders'][0]['name'],249 'roleARN': ConfigRecorder['ConfigurationRecorders'][0]['roleARN'],250 'AccountID': fChildAccountId,251 'Region': region252 })253 print(f"{ERASE_LINE}Checking account {fChildAccountId} in region {region} for Delivery Channel", end='\r')254 DeliveryChannel = Inventory_Modules.find_delivery_channels2(account_credentials, region)255 logging.debug("Tried to capture Delivery Channel")256 if len(DeliveryChannel['DeliveryChannels']) > 0:257 DeliveryChanList.append({258 'Name': DeliveryChannel['DeliveryChannels'][0]['name'],259 'AccountID': fChildAccountId,260 'Region': region261 })262 logging.warning(f"Checked account {fChildAccountId} in {len(fRegionList)} regions. Found {len(ConfigList)+len(DeliveryChanList)} issues with Config Recorders and Delivery Channels")263 except ClientError as my_Error:264 logging.warning("Failed to capture Config Recorder and Delivery Channels")265 ProcessStatus[Step]['Success'] = False266 print(my_Error)267 268 for _ in range(len(ConfigList)):269 logging.warning(f"{Fore.RED}Found a config recorder for account %s in region %s", ConfigList[_]['AccountID'], ConfigList[_]['Region']+Fore.RESET)270 ProcessStatus[Step]['Success'] = False271 ProcessStatus[Step]['IssuesFound'] += 1272 ProcessStatus[Step]['ProblemsFound'].extend(ConfigList)273 if fFixRun:274 logging.warning("Deleting %s in account %s in region %s", ConfigList[_]['Name'], ConfigList[_]['AccountID'], ConfigList[_]['Region'])275 DelConfigRecorder = Inventory_Modules.del_config_recorder2(account_credentials, ConfigList[_]['Region'], ConfigList[_]['Name'])276 # We assume the process worked. We should probably NOT assume this.277 ProcessStatus[Step]['IssuesFixed'] += 1278 for _ in range(len(DeliveryChanList)):279 logging.warning(f"{Fore.RED}Found a delivery channel for account {DeliveryChanList[_]['AccountID']} in region {DeliveryChanList[_]['Region']}{Fore.RESET}")280 ProcessStatus[Step]['Success'] = False281 ProcessStatus[Step]['IssuesFound'] += 1282 ProcessStatus[Step]['ProblemsFound'].extend(DeliveryChanList)283 if fFixRun:284 logging.warning("Deleting %s in account %s in region %s", DeliveryChanList[_]['Name'], DeliveryChanList[_]['AccountID'], DeliveryChanList[_]['Region'])285 DelDeliveryChannel = Inventory_Modules.del_delivery_channel2(account_credentials, DeliveryChanList[_]['Region'], DeliveryChanList[_]['Name'])286 # We assume the process worked. We should probably NOT assume this.287 ProcessStatus[Step]['IssuesFixed'] += 1288 if ProcessStatus[Step]['Success']:289 print(f"{ERASE_LINE + Fore.GREEN}** Step 2 completed with no issues{Fore.RESET}")290 elif ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed'] == 0:291 print(f"{ERASE_LINE + Fore.GREEN}** Step 2 found {ProcessStatus[Step]['IssuesFound']} issues, but they were fixed by deleting the existing Config Recorders and Delivery Channels{Fore.RESET}")292 ProcessStatus[Step]['Success'] = True293 elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step]['IssuesFixed']:294 print(f"{ERASE_LINE + Fore.RED}** Step 2 completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} items found that weren't deleted{Fore.RESET}")295 else:296 print(f"{ERASE_LINE + Fore.RED}** Step 2 completed with blockers found{Fore.RESET}")297 print()298 299 # Step 3300 # 3. The account must not have a Cloudtrail Trail name the same name as the CT Trail ("AWS-Landing-Zone-BaselineCloudTrail")301 Step = 'Step3'302 try:303 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")304 print(f" Checking account {fChildAccountId} for a specially named CloudTrail in all regions")305 CTtrails2 = []306 for region in fRegionList:307 print(ERASE_LINE, f"Checking account {fChildAccountId} in region {region} for CloudTrail trails", end='\r')308 CTtrails = Inventory_Modules.find_cloudtrails2(account_credentials, region, ['aws-controltower-BaselineCloudTrail'])309 if len(CTtrails) > 0:310 logging.warning(f"Unfortunately, we've found a CloudTrail log named {CTtrails[0]['Name']} in account {fChildAccountId} "311 f"in the {region} region, which means we'll have to delete it before this account can be adopted.")312 CTtrails2.append(CTtrails[0])313 ProcessStatus[Step]['Success'] = False314 except ClientError as my_Error:315 print(my_Error)316 ProcessStatus[Step]['Success'] = False317 318 for _ in range(len(CTtrails2)):319 logging.warning(f"{Fore.RED}Found a CloudTrail trail for account {fChildAccountId} in region {CTtrails2[_]['HomeRegion']} named {CTtrails2[_]['Name']}{Fore.RESET}")320 ProcessStatus[Step]['IssuesFound'] += 1321 ProcessStatus[Step]['ProblemsFound'].extend(CTtrails2)322 if fFixRun:323 try:324 logging.warning("CloudTrail trail deletion commencing...")325 delresponse = Inventory_Modules.del_cloudtrails2(account_credentials, region, CTtrails2[_]['TrailARN'])326 ProcessStatus[Step]['IssuesFixed'] += 1327 except ClientError as my_Error:328 print(my_Error)329 330 if ProcessStatus[Step]['Success']:331 print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}")332 elif ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed'] == 0:333 print(f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but they were fixed by deleting the existing CloudTrail trail names{Fore.RESET}")334 ProcessStatus[Step]['Success'] = True335 elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step]['IssuesFixed']:336 print(f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} trail names found that wasn't deleted{Fore.RESET}")337 else:338 print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}")339 print()340 341 """ Step 4342 # Step 4 -- The lack of or use of GuardDuty isn't a pre-requisite for Control Tower --343 # 4. This section checks for a pending guard duty invite. You can also check from the Guard Duty Console344 Step='Step4'345 try:346 print(Fore.BLUE + "{}:".format(Step) + Fore.RESET)347 print(" Checking account {} for any GuardDuty invites".format(fChildAccountId))348 GDinvites2=[]349 for region in fRegionList:350 logging.warning("Checking account %s in region %s for", fChildAccountId, region+Fore.RED+" GuardDuty"+Fore.RESET+" invitations")351 logging.warning("Checking account %s in region %s for GuardDuty invites", fChildAccountId, region)352 GDinvites=Inventory_Modules.find_gd_invites(account_credentials, region)353 if len(GDinvites) > 0:354 for x in range(len(GDinvites['Invitations'])):355 logging.warning("GD Invite: %s", str(GDinvites['Invitations'][x]))356 logging.warning("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'])357 ProcessStatus[Step]['IssuesFound']+=1358 GDinvites2.append({359 'AccountId': GDinvites['Invitations'][x]['AccountId'],360 'InvitationId': GDinvites['Invitations'][x]['InvitationId'],361 'Region': region362 })363 except ClientError as my_Error:364 print(my_Error)365 ProcessStatus[Step]['Success']=False366 367 for i in range(len(GDinvites2)):368 logging.warning(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)369 ProcessStatus[Step]['IssuesFound']+=1370 ProcessStatus[Step]['Success']=False371 if fFixRun:372 for x in range(len(GDinvites2)):373 try:374 logging.warning("GuardDuty invite deletion commencing...")375 delresponse=Inventory_Modules.delete_gd_invites(account_credentials, region, GDinvites2[x]['AccountId'])376 ProcessStatus[Step]['IssuesFixed']+=1377 # We assume the process worked. We should probably NOT assume this.378 except ClientError as my_Error:379 print(my_Error)380 381 if ProcessStatus[Step]['Success']:382 print(ERASE_LINE+Fore.GREEN+"** {} completed with no issues".format(Step)+Fore.RESET)383 elif ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed']==0:384 print(ERASE_LINE+Fore.GREEN+"** {} found {} guardduty invites, but they were deleted".format(Step,ProcessStatus[Step]['IssuesFound'])+Fore.RESET)385 ProcessStatus[Step]['Success']=True386 elif (ProcessStatus[Step]['IssuesFound']>ProcessStatus[Step]['IssuesFixed']):387 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)388 else:389 print(ERASE_LINE+Fore.RED+"** {} completed with blockers found".format(Step)+Fore.RESET)390 print()391 """392 393 # Step 4a394 # 4a. STS must be active in all regions. You can check from the Account Settings page in IAM.395 """396 TODO397 398 We would have already verified this - since we've used STS to connect to each region already for the previous steps.399 - Except for the "quick" shortcut - which means we probably need to point that out in this section. 400 """401 402 # Step 5403 '''404 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.405 - If the existing account is to be imported as a Core Account, modify the manifest.yaml file to use it.406 - 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.407 '''408 # try:409 Step = 'Step5'410 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")411 print(" Checking that the account is part of the AWS Organization.")412 OrgAccountList = [d['AccountId'] for d in aws_account.ChildAccounts]413 if not (fChildAccountId in OrgAccountList):414 print()415 print(f"Account # {fChildAccountId} is not a part of the Organization. This account needs to be moved into the Organization to be adopted into the Landing Zone tool")416 print("This is easiest done manually right now.")417 ProcessStatus[Step]['Success'] = False418 ProcessStatus[Step]['IssuesFound'] += 1419 420 if ProcessStatus[Step]['Success']:421 print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}")422 elif ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed'] == 0:423 print(f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issue, but we were able to fix it{Fore.RESET}")424 ProcessStatus[Step]['Success'] = True425 elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step]['IssuesFixed']:426 print(f"{ERASE_LINE + Fore.RED}** {Step} completed, but there was {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blocker found that wasn't fixed{Fore.RESET}")427 else:428 print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}")429 print()430 431 # Step 6432 # 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.433 """434 So we'll need to verify that the parent OU of the account is the root of the organization.435 436 TODO Here437 """438 Step = 'Step6'439 try:440 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")441 print(f" Checking account {fChildAccountId} to make sure it's not already in a Control-Tower managed OU")442 print(" -- Not yet implemented -- ")443 except ClientError as my_Error:444 print(my_Error)445 ProcessStatus[Step]['Success'] = False446 print()447 448 # Step 7 - Check for other resources which have 'controltower' in the name449 # Checking for SNS Topics450 Step = 'Step7'451 try:452 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")453 print(f" Checking account {fChildAccountId} for any SNS topics containing the 'controltower' string")454 SNSTopics2 = []455 for region in fRegionList:456 logging.warning("Checking account %s in region %s for", fChildAccountId, f"{region + Fore.RED} SNS Topics{Fore.RESET}")457 print(ERASE_LINE, f"Checking account {fChildAccountId} in region {region} for SNS Topics", end='\r')458 SNSTopics = Inventory_Modules.find_sns_topics2(account_credentials, region, ['controltower', 'ControlTower'])459 if len(SNSTopics) > 0:460 for x in range(len(SNSTopics)):461 logging.warning("SNS Topic: %s", str(SNSTopics[x]))462 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)463 ProcessStatus[Step]['Success'] = False464 ProcessStatus[Step]['IssuesFound'] += 1465 SNSTopics2.append({466 'AccountId': fChildAccountId,467 'TopicArn': SNSTopics[x],468 'Region': region469 })470 ProcessStatus[Step]['ProblemsFound'].extend(SNSTopics2)471 except ClientError as my_Error:472 print(my_Error)473 ProcessStatus[Step]['Success'] = False474 475 if ProcessStatus[Step]['Success']:476 print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}")477 elif ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed'] == 0:478 print(f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending SNS Topics{Fore.RESET}")479 ProcessStatus[Step]['Success'] = True480 elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step]['IssuesFixed']:481 print(f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that wasn't fixed{Fore.RESET}")482 else:483 print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}")484 print()485 486 # Step 8487 # Checking for Lambda functions488 Step = 'Step8'489 try:490 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")491 print(f" Checking account {fChildAccountId} for any Lambda functions containing the 'controltower' string")492 LambdaFunctions2 = []493 for region in fRegionList:494 logging.warning(f"Checking account %s in region %s for {Fore.RED}Lambda functions{Fore.RESET}", fChildAccountId, region)495 print(ERASE_LINE, f"Checking account {fChildAccountId} in region {region} for Lambda Functions", end='\r')496 LambdaFunctions = Inventory_Modules.find_lambda_functions2(account_credentials, region, ['controltower', 'CpntrolTower'])497 if len(LambdaFunctions) > 0:498 logging.info(499 "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))500 for x in range(len(LambdaFunctions)):501 logging.warning("Found Lambda function %s in region %s", LambdaFunctions[x]['FunctionName'], region)502 ProcessStatus[Step]['Success'] = False503 ProcessStatus[Step]['IssuesFound'] += 1504 LambdaFunctions2.append({505 'AccountId': fChildAccountId,506 'FunctionName': LambdaFunctions[x]['FunctionName'],507 'FunctionArn': LambdaFunctions[x]['FunctionArn'],508 'Role': LambdaFunctions[x]['Role'],509 'Region' : region510 })511 ProcessStatus[Step]['ProblemsFound'].extend(LambdaFunctions2)512 except ClientError as my_Error:513 print(my_Error)514 ProcessStatus[Step]['Success'] = False515 if ProcessStatus[Step]['Success']:516 print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}")517 elif ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed'] == 0:518 print(f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending Lambda Functions{Fore.RESET}")519 ProcessStatus[Step]['Success'] = True520 elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step]['IssuesFixed']:521 print(f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that wasn't fixed{Fore.RESET}")522 else:523 print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}")524 print()525 526 # Step 9527 # Checking for Role names528 Step = 'Step9'529 try:530 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")531 print(f" Checking account {fChildAccountId} for any Role names containing the 'controltower' string")532 RoleNames2 = []533 logging.warning("Checking account %s for", f"{fChildAccountId + Fore.RED} Role names{Fore.RESET}")534 RoleNames = Inventory_Modules.find_role_names2(account_credentials, 'us-east-1', ['controltower', 'ControlTower'])535 if len(RoleNames) > 0:536 logging.info(f"Unfortunately, account {fChildAccountId} contains {len(RoleNames)} roles with reserved names,"537 f" which means we'll have to delete them before this account can be adopted.")538 for x in range(len(RoleNames)):539 logging.warning(f"Role Name: {str(RoleNames[x])}")540 ProcessStatus[Step]['Success'] = False541 ProcessStatus[Step]['IssuesFound'] += 1542 RoleNames2.append({543 'AccountId': fChildAccountId,544 'RoleName': RoleNames[x]545 })546 ProcessStatus[Step]['ProblemsFound'].extend(RoleNames2)547 except ClientError as my_Error:548 print(my_Error)549 ProcessStatus[Step]['Success'] = False550 551 if ProcessStatus[Step]['Success']:552 print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}")553 elif ProcessStatus[Step]['IssuesFound']-ProcessStatus[Step]['IssuesFixed'] == 0:554 print(f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending IAM roles{Fore.RESET}")555 ProcessStatus[Step]['Success'] = True556 elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step]['IssuesFixed']:557 print(f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that remain to be fixed{Fore.RESET}")558 else:559 print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}")560 print()561 # Step 10562 # 10. The existing account can not have any CloudWatch Log Groups named "controltower"563 """564 So we'll need to find and remove the CloudWatch Log Groups - if there are any.565 TODO Here566 """567 Step = 'Step10'568 try:569 print(f"{Fore.BLUE}{Step}:{Fore.RESET}")570 print(f"Checking account {fChildAccountId} to make sure there are no duplicate CloudWatch Log Groups")571 LogGroupNames2 = []572 for region in fRegionList:573 logging.warning(f"Checking account {fChildAccountId} for {Fore.RED}duplicate CloudWatch Log Group names{Fore.RESET}")574 LogGroupNames = Inventory_Modules.find_cw_log_group_names2(account_credentials, region, ['controltower', 'ControlTower'])575 if len(LogGroupNames) > 0:576 logging.info(f"Unfortunately, account {fChildAccountId} contains {len(LogGroupNames)} log groups with reserved names,"577 f" which means we'll have to delete them before this account can be adopted.")578 for _ in range(len(LogGroupNames)):579 logging.warning(f"Log Group Name: {str(LogGroupNames[_])}")580 ProcessStatus[Step]['Success'] = False581 ProcessStatus[Step]['IssuesFound'] += 1582 LogGroupNames2.append({583 'AccountId': fChildAccountId,584 'LogGroupName': LogGroupNames[_]585 })586 ProcessStatus[Step]['ProblemsFound'].extend(LogGroupNames2)587 except ClientError as my_Error:588 print(my_Error)589 ProcessStatus[Step]['Success'] = False590 if ProcessStatus[Step]['Success']:591 print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}")592 elif ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed'] == 0:593 print(f"{ERASE_LINE}{Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending CW Log groups{Fore.RESET}")594 ProcessStatus[Step]['Success'] = True595 elif ProcessStatus[Step]['IssuesFound'] > ProcessStatus[Step]['IssuesFixed']:596 print(f"{ERASE_LINE}{Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that remain to be fixed {Fore.RESET}")597 else:598 print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}")599 print()600 """ Function Summary """601 TotalIssuesFound = 0602 TotalIssuesFixed = 0603 MemberReady = True604 for item in ProcessStatus:605 TotalIssuesFound = TotalIssuesFound + ProcessStatus[item]['IssuesFound']606 TotalIssuesFixed = TotalIssuesFixed + ProcessStatus[item]['IssuesFixed']607 MemberReady = MemberReady and ProcessStatus[item]['Success']608 ProcessStatus['AccountId'] = fChildAccountId609 ProcessStatus['Ready'] = MemberReady610 ProcessStatus['IssuesFound'] = TotalIssuesFound611 ProcessStatus['IssuesFixed'] = TotalIssuesFixed612 return(ProcessStatus)613####614# Summary at the end615####616Results = []617OrgResults = []618for MemberAccount in ChildAccountList:619 Results = DoSteps(MemberAccount, aws_acct, FixRun, RegionList)620 OrgResults.append(Results.copy())621print()622x = PrettyTable()623y = PrettyTable()624x.field_names = ['Account', 'Issues Found', 'Issues Fixed', 'Ready?']625# The following headers represent Step0, Step2,626y.field_names = ['Account', 'Account Access', 'Config', 'CloudTrail', 'GuardDuty', 'Org Member', 'CT OU', 'SNS Topics', 'Lambda', 'Roles', 'CW Log Groups', 'Ready?']627for i in OrgResults:628 x.add_row([i['AccountId'], i['IssuesFound'], i['IssuesFixed'], i['Ready']])629 y.add_row([630 i['AccountId'],631 i['Step0']['IssuesFound'] - i['Step0']['IssuesFixed'],632 i['Step2']['IssuesFound'] - i['Step2']['IssuesFixed'],633 i['Step3']['IssuesFound'] - i['Step3']['IssuesFixed'],634 i['Step4']['IssuesFound'] - i['Step4']['IssuesFixed'],635 i['Step5']['IssuesFound'] - i['Step5']['IssuesFixed'],636 i['Step6']['IssuesFound'] - i['Step6']['IssuesFixed'],637 i['Step7']['IssuesFound'] - i['Step7']['IssuesFixed'],638 i['Step8']['IssuesFound'] - i['Step8']['IssuesFixed'],639 i['Step9']['IssuesFound'] - i['Step9']['IssuesFixed'],640 i['Step10']['IssuesFound'] - i['Step10']['IssuesFixed'],641 i['Step0']['Success'] and i['Step2']['Success'] and i['Step3']['Success'] and i['Step4']['Success'] and i['Step5']['Success'] and i['Step6']['Success'] and i['Step7']['Success'] and i['Step8']['Success'] and i['Step9']['Success'] and i['Step10']['Success']642 ])643print("The following table represents the accounts looked at, and whether they are ready to be incorporated into a Control Tower environment.")644print(x)645print()646print("The following table represents the accounts looked at, and gives details under each type of issue as to what might prevent a successful migration of this account into a Control Tower environment.")647print(y)648if verbose < 50:649 for account in OrgResults:650 print()651 FixesWorked = (account['IssuesFound'] - account['IssuesFixed'] == 0)652 if account['Ready'] and account['IssuesFound'] == 0:653 print(f"{Fore.GREEN}**** We've found NO issues that would hinder the adoption of account {account['AccountId']} ****{Fore.RESET}")654 elif account['Ready'] and FixesWorked:655 print(f"{Fore.GREEN}We've found and fixed{Fore.RED}", f"{account['IssuesFixed']}{Fore.RESET}", f"{Fore.GREEN}issues that would have otherwise blocked the adoption of account {account['AccountId']}{Fore.RESET}")656 else:657 print(f"{Fore.RED}Account # {account['AccountId']} has {account['IssuesFound'] - account['IssuesFixed']} issues that would hinder the adoption of this account{Fore.RESET}")658 for step in account:659 if step[:4] == 'Step' and len(account[step]['ProblemsFound']) > 0:660 print(f"{Fore.LIGHTRED_EX}Issues Found for {step} in account {account['AccountId']}:{Fore.RESET}")661 pprint(account[step]['ProblemsFound'])...
ALZ_CheckAccount.py
Source:ALZ_CheckAccount.py
1#!/usr/bin/env python32import sys3import boto34import Inventory_Modules5import vpc_modules6from colorama import init, Fore7from ArgumentsClass import CommonArguments8from botocore.exceptions import ClientError9from account_class import aws_acct_access10from prettytable import PrettyTable11import logging12init()13parser = CommonArguments()14parser.verbosity()15parser.singleprofile()16parser.my_parser.add_argument(17 "--explain",18 dest="pExplain",19 const=True,20 default=False,21 action="store_const",22 help="This flag prints out the explanation of what this script would do.")23parser.my_parser.add_argument(24 "-R", "--role",25 dest="Role",26 metavar="rolename",27 default=None,28 help="This allows the provision of a role to be used to access the child account(s).")29parser.my_parser.add_argument(30 "-a", "--account",31 dest="pChildAccountId",32 metavar="New Account to be adopted into LZ",33 default=None,34 # required=True,35 help="This is the account number of the account you're checking, to see if it can be adopted into the ALZ. By default, we will check every account in the Organization")36parser.my_parser.add_argument(37 "-q", "--quick",38 dest="Quick",39 metavar="Shortcut the checking to only a single region",40 const=True,41 default=False,42 action="store_const",43 help="This flag only checks 'us-east-1', so makes the whole script run really fast.")44parser.my_parser.add_argument(45 "+fix", "+delete",46 dest="FixRun",47 const=True,48 default=False,49 action="store_const",50 help="This will fix the issues found. If default VPCs must be deleted, you'll be asked to confirm.")51parser.my_parser.add_argument(52 "+force",53 dest="pVPCConfirm",54 const=True,55 default=False,56 action="store_const",57 help="This will delete the default VPCs found with NO confirmation. You still have to specify the +fix too")58# TODO: There should be an additional parameter here that would take a role name for access into the account,59# since it's likely that users won't be able to use the AWSControlTowerExecution role60args = parser.my_parser.parse_args()61Quick = args.Quick62pProfile = args.Profile63pChildAccountId = args.pChildAccountId64verbose = args.loglevel65pRole = args.Role66FixRun = args.FixRun67pExplain = args.pExplain68pVPCConfirm = args.pVPCConfirm69logging.basicConfig(level=args.loglevel, format="[%(filename)s:%(lineno)s - %(funcName)30s() ] %(message)s")70# This is hard-coded, because this is the listing of regions that are supported by Automated Landing Zone.71if Quick:72 RegionList = ['us-east-1']73else:74 # RegionList = Inventory_Modules.get_ec2_regions('all', pProfile)75 # ap-northeast-3 doesn't support Config (and therefore doesn't support ALZ), but is a region that is normally included within EC2. Therefore - this is easier.76 RegionList = ['ap-northeast-1', 'ap-northeast-2', 'ap-south-1', 'ap-southeast-1', 'ap-southeast-2', 'ca-central-1',77 'eu-central-1', 'eu-north-1', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'sa-east-1', 'us-east-1',78 'us-east-2', 'us-west-1', 'us-west-2']79ERASE_LINE = '\x1b[2K'80ExplainMessage = """810. The Child account MUST allow the Management account access into the Child IAM role called "AWSCloudFormationStackSetExecutionRole"820a. There must be an "AWSCloudFormationStackSetExecution" or "AWSControlTowerExecutionRole" role present in the account so that StackSets can assume it and deploy stack instances. This role must trust the Organizations Management account. In LZ the account is created with that role name so stacksets just works. You can add this role manually via CloudFormation in the existing account. [I did this as a step 0]830b. STS must be active in all regions. You can check from the Account Settings page in IAM. Since we're using STS to connect to the account from the Management Account, this requirement is checked by successfully completing step 0.841. The account must not contain any resources/config associated with the Default VPCs in ANY region e.g. security groups cannot exist associated with the Default VPC. Default VPCs will be deleted in the account in all regions, if they contain some dependency (usually a Security Group or an EIP) then deleting the VPC fails and the deployment rolls back. You can either manually delete them all or verify there are no dependencies, in some cases manually deleting them all is faster than roll back.852. There must be no active config channel and recorder in the account as âthere can be only oneâ of each. This must also be deleted via CLI, not console, switching config off in the console is NOT good enough and just disables it. To Delete the delivery channel and the configuration recorder (can be done via CLI and Python script only):86aws configservice describe-delivery-channels87aws configservice describe-delivery-channel-status88aws configservice describe-configuration-recorders89aws configservice stop-configuration-recorder --configuration-recorder-name <NAME-FROM-DESCRIBE-OUTPUT>90aws configservice delete-delivery-channel --delivery-channel-name <NAME-FROM-DESCRIBE-OUTPUT>91aws configservice delete-configuration-recorder --configuration-recorder-name <NAME-FROM-DESCRIBE-OUTPUT923. The account must not have a Cloudtrail Trail name the same name as the LZ Trail ("AWS-Landing-Zone-BaselineCloudTrail")934. The account must not have a pending guard duty invite. You can check from the Guard Duty Console945. The account must be part of the Organization and the email address being entered into the LZ 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, LZ just finds the account and uses the CFN roles.95- If the existing account is to be imported as a Core Account, modify the manifest.yaml file to use it.96- 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.976. The existing account can not be in any of the LZ-managed Organizations OUs. By default, these OUs are Core and Applications, but the customer may have chosen different or additional OUs to manage by LZ.98"""99print()100if pExplain:101 print(ExplainMessage)102 sys.exit("Exiting after Script Explanation...")103# TODO: This is supposed to be the recommended Trust Policy for the cross-account role access,104# so that we can recommend people add it to their "AWSCloudFormationStackSetExecutionRole"105json_formatted_str_TP = ""106##########107def _initdict(StepCount, faccountList):108 fProcessStatus = {}109 for account in faccountList:110 fProcessStatus[account] = {'ChildIsReady': False, 'IssuesFound': 0, 'IssuesFixed': 0}111 for _ in range(StepCount):112 Step = f"Step{str(_)}"113 fProcessStatus[account][Step] = dict()114 fProcessStatus[account][Step]['Success'] = False115 fProcessStatus[account][Step]['IssuesFound'] = 0116 fProcessStatus[account][Step]['IssuesFixed'] = 0117 return (fProcessStatus)118###########119# Step 0 -120"""1210. The Child account MUST allow the Management account access into the Child IAM role called "AWSCloudFormationStackSetExecutionRole"122Technically, only the Lambda function roles REQUIRE access, but for this script to run, we need that same level of access.123TODO: Eventually, we should update this script to run as that role - which may mean we have to update this script to become a Lambda function, 124but that's sometime in the future.125"""126print("This script does 6 things... ")127print(128 f"{Fore.BLUE} 0.{Fore.RESET} Checks to ensure you have the necessary cross-account role access to the child account.")129print(f"{Fore.BLUE} 1.{Fore.RESET} Checks to ensure the {Fore.RED}Default VPCs {Fore.RESET}in each region are deleted")130if FixRun and not pVPCConfirm:131 print(132 f"{Fore.BLUE} You've asked to delete any default VPCs we find - with confirmation on each one.{Fore.RESET}")133elif FixRun and pVPCConfirm:134 print()135 print(136 f"{Fore.RED} You've asked to delete any default VPCs we find - WITH NO CONFIRMATION on each one.{Fore.RESET}")137 print()138elif pVPCConfirm and not FixRun:139 print()140 print(141 f"{Fore.BLUE} You asked us to delete the default VPCs with no confirmation, but didn't provide the '+fixrun' parameter, so we're proceeding with NOT deleting. You can safely interupt this script and run it again with the necessary parameters.{Fore.RESET}")142 print()143print(f"{Fore.BLUE} 2.{Fore.RESET} Checks the child account in each of the regions")144print(f" to see if there's already a {Fore.RED}Config Recorder and Delivery Channel {Fore.RESET}enabled...")145print(146 f"{Fore.BLUE} 3.{Fore.RESET} Checks that there isn't a duplicate {Fore.RED}CloudTrail{Fore.RESET} trail in the account.")147print(148 f"{Fore.BLUE} 4.{Fore.RESET} Checks to see if {Fore.RED}GuardDuty{Fore.RESET} has been enabled for this child account.")149print(" If it has been, it needs to be deleted before we can adopt this new account")150print(" into the Org's Automated Landing Zone.")151print(152 f"{Fore.BLUE} 5.{Fore.RESET} This child account {Fore.RED}must exist{Fore.RESET} within the Parent Organization.")153print(" If it doesn't - then you must move it into this Org")154print(" (this script can't do that for you).")155print()156print("Since this script is fairly new - All comments or suggestions are enthusiastically encouraged")157print()158DefaultVPCs = []159aws_account = aws_acct_access(pProfile)160if aws_account.AccountType.lower() == 'root' and pChildAccountId is None:161 # Creates a list of the account numbers in the Org.162 ChildAccountList = [d['AccountId'] for d in aws_account.ChildAccounts]163 print(164 f"Since you didn't specify a specific account, we'll check all {len(aws_account.ChildAccounts)} accounts in the Org.")165elif pChildAccountId is None:166 sys.exit(167 f"Account {aws_account.acct_number} is a {aws_account.AccountType} account. This script should be run with Management Account credentials.")168else:169 print(f"Account {aws_account.acct_number} is a {aws_account.AccountType} account."170 f"We're checking to validate that account {pChildAccountId} can be adopted into the Landing Zone")171 ChildAccountList = [pChildAccountId]172Steps = 6173ProcessStatus = _initdict(Steps, ChildAccountList)174logging.error(f"There are {Steps} steps to go through for {len(ChildAccountList)} accounts")175# Determining access to each child account176accountsleft = len(ChildAccountList)177for childaccount in ChildAccountList:178 accountsleft -= 1179 # Step 0180 """181 This part will ascertain whether access to the child works. 182 """183 try:184 account_credentials = Inventory_Modules.get_child_access3(aws_account, childaccount)185 except ClientError as my_Error:186 if str(my_Error).find("AuthFailure") > 0:187 # TODO: This whole section is waiting on an enhancement. Until then, we have to assume that ProServe or someone familiar with ALZ is running this script188 print(f"Authorization Failure for account {childaccount}")189 print(190 f"The child account MUST allow access into the necessary IAM roles from the Organization's Master Account for the rest of this script (and the overall migration) to run.")191 print("You must add the following lines to the Trust Policy of that role in the child account")192 print(json_formatted_str_TP)193 print(my_Error)194 ProcessStatus[childaccount]['Step0']['Success'] = False195 elif str(my_Error).find("AccessDenied") > 0:196 # TODO: This whole section is waiting on an enhancement. Until then, we have to assume that ProServe or someone familiar with ALZ is running this script197 print(f"Access Denied Failure for account {childaccount}")198 print(199 f"The child account MUST allow access into the necessary IAM roles from the Organization's Master Account for the rest of this script (and the overall migration) to run.")200 print("You must add the following lines to the Trust Policy of that role in the child account")201 print(json_formatted_str_TP)202 print(my_Error)203 ProcessStatus[childaccount]['Step0']['Success'] = False204 else:205 print(f"Other kind of failure for account {childaccount}")206 print(my_Error)207 ProcessStatus[childaccount]['Step0']['Success'] = False208 finally:209 if account_credentials['AccessKeyId'] is None:210 logging.error(211 f"Was {Fore.RED}not{Fore.RESET} able to successfully connect to account {childaccount} using credentials from account {aws_account.acct_number}... ")212 print()213 print(f"{Fore.RED}** Step 0 failed for account {childaccount}{Fore.RESET}")214 print()215 ProcessStatus[childaccount]['Step0']['Success'] = False216 ProcessStatus[childaccount]['Step0']['IssuesFound'] += 1217 continue218 else:219 logging.error(220 f"Was able to successfully connect to account {childaccount} using credentials from account {aws_account.acct_number}... ")221 print()222 print(f"{Fore.GREEN}** Step 0 completed without issues{Fore.RESET}")223 print()224 ProcessStatus[childaccount]['Step0']['Success'] = True225 # Step 1226 """227 This part will find and delete the Default VPCs in each region for the child account. 228 We only delete if you provided that in the parameters list.229 """230 try:231 print(f"Checking account {childaccount} for default VPCs in any region")232 DefaultVPCFound = False233 for region in RegionList:234 print(ERASE_LINE,235 f"Checking account {childaccount} in region {region} for {Fore.RED}default VPCs{Fore.RESET}",236 end='\r')237 logging.info("Looking for Default VPCs in account {} from Region {}}", childaccount, region)238 DefaultVPC = Inventory_Modules.find_account_vpcs2(account_credentials, region, True)239 if len(DefaultVPC['Vpcs']) > 0:240 DefaultVPCs.append({241 'VPCId' : DefaultVPC['Vpcs'][0]['VpcId'],242 'AccountID': childaccount,243 'Region' : region244 })245 ProcessStatus[childaccount]['Step1']['IssuesFound'] += 1246 DefaultVPCFound = True247 if DefaultVPCFound:248 ProcessStatus[childaccount]['Step1']['Success'] = False249 else:250 ProcessStatus[childaccount]['Step1']['Success'] = True251 except ClientError as my_Error:252 logging.warning("Failed to identify the Default VPCs in the region properly")253 ProcessStatus[childaccount]['Step1']['Success'] = False254 print(my_Error)255 print(ERASE_LINE, end='\r')256 for i in range(len(DefaultVPCs)):257 logging.info(258 f"I found a default VPC for account {DefaultVPCs[i]['AccountID']} in region {DefaultVPCs[i]['Region']}")259 if FixRun:260 logging.warning(261 f"Deleting VpcId {DefaultVPCs[i]['VPCId']} in account {DefaultVPCs[i]['AccountID']} in region {DefaultVPCs[i]['Region']}")262 try: # confirm the user really want to delete the VPC. This is irreversible263 if pVPCConfirm:264 ReallyDelete = True265 else:266 ReallyDelete = (input(267 f"Deletion of {DefaultVPCs[i]['Region']} default VPC has been requested. Are you still sure? (y/n): ") in [268 'y', 'Y'])269 if ReallyDelete:270 DelVPC_Success = (vpc_modules.del_vpc(account_credentials, DefaultVPCs[i]['VPCId'],271 DefaultVPCs[i]['Region']) == 0)272 if DelVPC_Success:273 ProcessStatus[childaccount]['Step1']['IssuesFixed'] += 1274 else:275 print("Something went wrong with the VPC Deletion")276 ProcessStatus[childaccount]['Step1']['Success'] = False277 sys.exit(9)278 else:279 logging.warning("User answered False to the 'Are you sure' question")280 print(281 f"Skipping VPC ID {DefaultVPCs[i]['VPCId']} in account {DefaultVPCs[i]['AccountID']} in region {DefaultVPCs[i]['Region']}")282 ProcessStatus[childaccount]['Step1']['Success'] = False283 except ClientError as my_Error:284 logging.error("Failed to delete the Default VPCs in the region properly")285 ProcessStatus[childaccount]['Step1']['Success'] = False286 print(my_Error)287 print()288 if ProcessStatus[childaccount]['Step1']['Success']:289 print(f"{ERASE_LINE + Fore.GREEN}** Step 1 completed with no issues{Fore.RESET}")290 elif ProcessStatus[childaccount]['Step1']['IssuesFound'] - ProcessStatus[childaccount]['Step1']['IssuesFixed'] == 0:291 print(292 f"{ERASE_LINE + Fore.GREEN}** Step 1 found {ProcessStatus[childaccount]['Step1']['IssuesFound']} issues, but they were fixed by deleting the default vpcs{Fore.RESET}")293 ProcessStatus[childaccount]['Step1']['Success'] = True294 elif (ProcessStatus[childaccount]['Step1']['IssuesFound'] > ProcessStatus[childaccount]['Step1']['IssuesFixed']):295 print(296 f"{ERASE_LINE + Fore.RED}** Step 1 completed, but there were {ProcessStatus[childaccount]['Step1']['IssuesFound'] - ProcessStatus[childaccount]['Step1']['IssuesFixed']} vpcs that couldn't be fixed{Fore.RESET}")297 else:298 print(f"{ERASE_LINE + Fore.RED}** Step 1 completed with blockers found{Fore.RESET}")299 # Step 2300 # 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.301 ConfigList = []302 DeliveryChanList = []303 config_deliv_channels_found = False304 try:305 # RegionList=Inventory_Modules.get_service_regions('config', 'all')306 print(f"Checking account {childaccount} for Config Recorders and Delivery Channels in any region")307 # TODO: Need to find a way to gracefully handle the error processing of opt-in regions.308 # Until then - we're using a hard-coded listing of regions, instead of dynamically finding those.309 for region in RegionList:310 print(ERASE_LINE, f"Checking account {childaccount} in region {region} for Config Recorder", end='\r')311 logging.info(f"Looking for Config Recorders in account {childaccount} from Region {region}")312 ConfigRecorder = Inventory_Modules.find_config_recorders2(account_credentials, region)313 logging.debug("Tried to capture Config Recorder")314 if len(ConfigRecorder['ConfigurationRecorders']) > 0:315 ConfigList.append({316 'Name' : ConfigRecorder['ConfigurationRecorders'][0]['name'],317 'roleARN' : ConfigRecorder['ConfigurationRecorders'][0]['roleARN'],318 'AccountID': childaccount,319 'Region' : region320 })321 print(ERASE_LINE, f"Checking account {childaccount} in region {region} for Delivery Channel", end='\r')322 DeliveryChannel = Inventory_Modules.find_delivery_channels2(account_credentials, region)323 logging.debug("Tried to capture Delivery Channel")324 if len(DeliveryChannel['DeliveryChannels']) > 0:325 DeliveryChanList.append({326 'Name' : DeliveryChannel['DeliveryChannels'][0]['name'],327 'AccountID': childaccount,328 'Region' : region329 })330 logging.error(331 f"Checked account {childaccount} in {len(RegionList)} regions. Found {len(ConfigList) + len(DeliveryChanList)} issues with Config Recorders and Delivery Channels")332 except ClientError as my_Error:333 logging.warning("Failed to capture Config Recorder and Delivery Channels")334 ProcessStatus[childaccount]['Step2']['Success'] = False335 print(my_Error)336 for i in range(len(ConfigList)):337 logging.error(f"{Fore.RED}Found a config recorder for account %s in region %s", ConfigList[i]['AccountID'],338 ConfigList[i]['Region'] + Fore.RESET)339 ProcessStatus[childaccount]['Step2']['IssuesFound'] += 1340 config_deliv_channels_found = True341 if FixRun:342 logging.warning("Deleting %s in account %s in region %s", ConfigList[i]['Name'], ConfigList[i]['AccountID'],343 ConfigList[i]['Region'])344 DelConfigRecorder = Inventory_Modules.del_config_recorder2(account_credentials, ConfigList[i]['Region'],345 ConfigList[i]['Name'])346 # We assume the process worked. We should probably NOT assume this.347 ProcessStatus[childaccount]['Step2']['IssuesFixed'] += 1348 for i in range(len(DeliveryChanList)):349 logging.error(f"{Fore.RED}I found a delivery channel for account %s in region %s",350 DeliveryChanList[i]['AccountID'], DeliveryChanList[i]['Region'] + Fore.RESET)351 ProcessStatus[childaccount]['Step2']['IssuesFound'] += 1352 config_deliv_channels_found = True353 if FixRun:354 logging.warning("Deleting %s in account %s in region %s", DeliveryChanList[i]['Name'],355 DeliveryChanList[i]['AccountID'], DeliveryChanList[i]['Region'])356 DelDeliveryChannel = Inventory_Modules.del_delivery_channel2(account_credentials, ConfigList[i]['Region'],357 DeliveryChanList[i]['Name'])358 # We assume the process worked. We should probably NOT assume this.359 ProcessStatus[childaccount]['Step2']['IssuesFixed'] += 1360 if config_deliv_channels_found:361 ProcessStatus[childaccount]['Step2']['Success'] = False362 else:363 ProcessStatus[childaccount]['Step2']['Success'] = True364 if ProcessStatus[childaccount]['Step2']['Success']:365 print(f"{ERASE_LINE + Fore.GREEN}** Step 2 completed with no issues{Fore.RESET}")366 elif ProcessStatus[childaccount]['Step2']['IssuesFound'] - ProcessStatus[childaccount]['Step2']['IssuesFixed'] == 0:367 print(368 f"{ERASE_LINE + Fore.GREEN}** Step 2 found {ProcessStatus[childaccount]['Step2']['IssuesFound']} issues, but they were fixed by deleting the existing Config Recorders and Delivery Channels{Fore.RESET}")369 ProcessStatus[childaccount]['Step2']['Success'] = True370 elif (ProcessStatus[childaccount]['Step2']['IssuesFound'] > ProcessStatus[childaccount]['Step2']['IssuesFixed']):371 print(372 f"{ERASE_LINE + Fore.RED}** Step 2 completed, but there were {ProcessStatus[childaccount]['Step2']['IssuesFound'] - ProcessStatus[childaccount]['Step2']['IssuesFixed']} items found that couldn't be deleted{Fore.RESET}")373 else:374 print(f"{ERASE_LINE + Fore.RED}** Step 2 completed with blockers found{Fore.RESET}")375 print()376 # Step 3377 # 3. The account must not have a Cloudtrail Trail name the same name as the LZ Trail ("AWS-Landing-Zone-BaselineCloudTrail")378 CTtrails2 = []379 trailname = None380 ct_trail_found = False381 try:382 print(f"Checking account {childaccount} for a specially named CloudTrail in all regions")383 for region in RegionList:384 print(ERASE_LINE, f"Checking account {childaccount} in region {region} for CloudTrail trails", end='\r')385 CTtrails = Inventory_Modules.find_cloudtrails2(account_credentials, region,386 ['AWS-Landing-Zone-BaselineCloudTrail'])387 if len(CTtrails) > 0:388 logging.error(389 f"Unfortunately, we've found existing CloudTrails in account {childaccount} in the {region} region, which means we'll have to delete it before this account can be adopted.")390 # CTtrails['trailList'][0]['region'] = region391 CTtrails2.extend(CTtrails)392 ct_trail_found = True393 except ClientError as my_Error:394 print(my_Error)395 ProcessStatus[childaccount]['Step3']['Success'] = False396 for i in range(len(CTtrails2)):397 logging.error(398 f"{Fore.RED}Found a CloudTrail trail for account {childaccount} in region {CTtrails2[i]['HomeRegion']} named {CTtrails2[i]['Name']}{Fore.RESET}")399 ProcessStatus[childaccount]['Step3']['IssuesFound'] += 1400 if FixRun:401 try:402 logging.error("CloudTrail trail deletion commencing...")403 delresponse = Inventory_Modules.del_cloudtrails2(account_credentials, CTtrails2[i]['HomeRegion'],404 CTtrails2[i]['TrailARN'])405 ProcessStatus[childaccount]['Step3']['IssuesFixed'] += 1406 except ClientError as my_Error:407 print(my_Error)408 if ct_trail_found:409 ProcessStatus[childaccount]['Step3']['Success'] = False410 else:411 ProcessStatus[childaccount]['Step3']['Success'] = True412 if ProcessStatus[childaccount]['Step3']['Success']:413 print(f"{ERASE_LINE + Fore.GREEN}** Step 3 completed with no issues{Fore.RESET}")414 elif ProcessStatus[childaccount]['Step3']['IssuesFound'] - ProcessStatus[childaccount]['Step3']['IssuesFixed'] == 0:415 print(416 f"{ERASE_LINE + Fore.GREEN}** Step 3 found {ProcessStatus[childaccount]['Step3']['IssuesFound']} issues, but they were fixed by deleting the existing CloudTrail trail names{Fore.RESET}")417 ProcessStatus[childaccount]['Step3']['Success'] = True418 elif (ProcessStatus[childaccount]['Step3']['IssuesFound'] > ProcessStatus[childaccount]['Step3']['IssuesFixed']):419 print(420 f"{ERASE_LINE + Fore.RED}** Step 3 completed, but there were {ProcessStatus[childaccount]['Step3']['IssuesFound'] - ProcessStatus[childaccount]['Step3']['IssuesFixed']} trail names found that couldn't be deleted{Fore.RESET}")421 else:422 print(f"{ERASE_LINE + Fore.RED}** Step 3 completed with blockers found{Fore.RESET}")423 print()424 # Step 4425 # 4. The account must not have a pending guard duty invite. You can check from the Guard Duty Console426 GDinvites2 = []427 gdinvites_found = False428 try:429 print(f"Checking account {childaccount} for any GuardDuty invites")430 for region in RegionList:431 print(432 f"{ERASE_LINE}Checking account {childaccount} in region {region} for {Fore.RED}GuardDuty{Fore.RESET}invitations",433 end='\r')434 GDinvites = Inventory_Modules.find_gd_invites2(account_credentials, region)435 if len(GDinvites['Invitations']) > 0:436 gdinvites_found = True437 for x in range(len(GDinvites['Invitations'])):438 logging.warning("GD Invite: %s", str(GDinvites['Invitations'][x]))439 logging.error(440 f"Unfortunately, we've found a GuardDuty invitation for account {childaccount} in the {region} region from account {GDinvites['Invitations'][x]['AccountId']}, which means we'll have to delete it before this account can be adopted.")441 ProcessStatus[childaccount]['Step4']['IssuesFound'] += 1442 GDinvites2.append({443 'AccountId' : GDinvites['Invitations'][x]['AccountId'],444 'InvitationId': GDinvites['Invitations'][x]['InvitationId'],445 'Region' : region446 })447 except ClientError as my_Error:448 print(my_Error)449 ProcessStatus[childaccount]['Step4']['Success'] = False450 for i in range(len(GDinvites2)):451 logging.error(f"{Fore.RED}I found a GuardDuty invitation for account %s in region %s from account %s ",452 childaccount, GDinvites2[i]['Region'], GDinvites2[i]['AccountId'] + Fore.RESET)453 ProcessStatus[childaccount]['Step4']['IssuesFound'] += 1454 if FixRun:455 for x in range(len(GDinvites2)):456 try:457 logging.warning("GuardDuty invite deletion commencing...")458 delresponse = Inventory_Modules.delete_gd_invites2(account_credentials, GDinvites2[x]['Region'],459 GDinvites2[x]['AccountId'])460 ProcessStatus[childaccount]['Step4']['IssuesFixed'] += 1461 # We assume the process worked. We should probably NOT assume this.462 except ClientError as my_Error:463 print(my_Error)464 if gdinvites_found:465 ProcessStatus[childaccount]['Step4']['Success'] = False466 else:467 ProcessStatus[childaccount]['Step4']['Success'] = True468 if ProcessStatus[childaccount]['Step4']['Success']:469 print(f"{ERASE_LINE + Fore.GREEN}** Step 4 completed with no issues{Fore.RESET}")470 elif ProcessStatus[childaccount]['Step4']['IssuesFound'] - ProcessStatus[childaccount]['Step4']['IssuesFixed'] == 0:471 print(472 f"{ERASE_LINE + Fore.GREEN}** Step 4 found {ProcessStatus[childaccount]['Step4']['IssuesFound']} guardduty invites, but they were deleted{Fore.RESET}")473 ProcessStatus[childaccount]['Step4']['Success'] = True474 elif (ProcessStatus[childaccount]['Step4']['IssuesFound'] > ProcessStatus[childaccount]['Step4']['IssuesFixed']):475 print(476 f"{ERASE_LINE + Fore.RED}** Step 4 completed, but there were {ProcessStatus[childaccount]['Step4']['IssuesFound'] - ProcessStatus[childaccount]['Step4']['IssuesFixed']} guardduty invites found that couldn't be deleted{Fore.RESET}")477 else:478 print(f"{ERASE_LINE + Fore.RED}** Step 4 completed with blockers found{Fore.RESET}")479 print()480 """481 # Step 4.5482 # STS must be active in all regions. You can check from the Account Settings page in IAM.483 We would have already verified this - since we've used STS to connect to each region already for the previous steps.484 """485 # Step 5486 '''487 5. The account must be part of the Organization and the email address being entered into the LZ 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, LZ just finds the account and uses the CFN roles.488 - If the existing account is to be imported as a Core Account, modify the manifest.yaml file to use it.489 - 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.490 '''491 print("Checking that the account is part of the AWS Organization.")492 if (childaccount in [d['AccountId'] for d in aws_account.ChildAccounts]):493 ProcessStatus[childaccount]['Step5']['Success'] = True494 else:495 print()496 print(497 f"Account # {childaccount} is not a part of the Organization. This account needs to be moved into the Organization to be adopted into the Landing Zone tool")498 print("This is easiest done manually right now.")499 ProcessStatus[childaccount]['Step5']['Success'] = False500 ProcessStatus[childaccount]['Step5']['IssuesFound'] += 1501 if ProcessStatus[childaccount]['Step5']['Success']:502 print(f"{ERASE_LINE + Fore.GREEN}** Step 5 completed with no issues{Fore.RESET}")503 elif ProcessStatus[childaccount]['Step5']['IssuesFound'] - ProcessStatus[childaccount]['Step5']['IssuesFixed'] == 0:504 print(505 f"{ERASE_LINE + Fore.GREEN}** Step 5 found {ProcessStatus[childaccount]['Step5']['IssuesFound']} issues, but we were able to move the account into the they were able to be fixed{Fore.RESET}")506 ProcessStatus[childaccount]['Step5']['Success'] = True507 elif (ProcessStatus[childaccount]['Step5']['IssuesFound'] > ProcessStatus[childaccount]['Step5']['IssuesFixed']):508 print(509 f"{ERASE_LINE + Fore.RED}** Step 5 completed, but there were {ProcessStatus[childaccount]['Step5']['IssuesFound'] - ProcessStatus[childaccount]['Step5']['IssuesFixed']} blockers found that couldn't be fixed{Fore.RESET}")510 else:511 print(f"{ERASE_LINE + Fore.RED}** Step 5 completed with blockers found{Fore.RESET}")512 print()513 print(f"{Fore.CYAN}Account {childaccount} is complete. {accountsleft} more to go!!{Fore.RESET}")514 """515 # Step 6516 # 6. The existing account can not be in any of the LZ-managed Organizations OUs. By default, these OUs are Core and Applications, but the customer may have chosen different or additional OUs to manage by LZ.517 So we'll need to verify that the parent OU of the account is the root of the organization.518 """519x = PrettyTable()520y = PrettyTable()521x.field_names = ['Account', 'Issues Found', 'Issues Fixed', 'Ready?']522# The following headers represent Step0 through Step5,523y.field_names = ['Account', 'Account Access', 'Default VPCs', 'Recorders', 'CloudTrail', 'GuardDuty', 'Org Member',524 'Ready?']525for item in ProcessStatus:526 for _ in range(Steps):527 Step = f"Step{str(_)}"528 ProcessStatus[item]['IssuesFound'] += ProcessStatus[item][Step]['IssuesFound']529 ProcessStatus[item]['IssuesFound'] += ProcessStatus[item][Step]['IssuesFixed']530 x.add_row([item, ProcessStatus[item]['IssuesFound'], ProcessStatus[item]['IssuesFixed'],531 ProcessStatus[item]['ChildIsReady']])532 y.add_row([533 item,534 ProcessStatus[item]['Step0']['IssuesFound'] - ProcessStatus[item]['Step0']['IssuesFixed'],535 ProcessStatus[item]['Step1']['IssuesFound'] - ProcessStatus[item]['Step1']['IssuesFixed'],536 ProcessStatus[item]['Step2']['IssuesFound'] - ProcessStatus[item]['Step2']['IssuesFixed'],537 ProcessStatus[item]['Step3']['IssuesFound'] - ProcessStatus[item]['Step3']['IssuesFixed'],538 ProcessStatus[item]['Step4']['IssuesFound'] - ProcessStatus[item]['Step4']['IssuesFixed'],539 ProcessStatus[item]['Step5']['IssuesFound'] - ProcessStatus[item]['Step5']['IssuesFixed'],540 ProcessStatus[item]['Step0']['Success'] and ProcessStatus[item]['Step1']['Success'] and541 ProcessStatus[item]['Step2']['Success'] and ProcessStatus[item]['Step3']['Success'] and542 ProcessStatus[item]['Step4']['Success'] and ProcessStatus[item]['Step5']['Success']543 ])544print(545 "The following table represents the accounts looked at, and whether they are ready to be incorporated into an ALZ environment.")546print(x)547print()548print(549 "The following table represents the accounts looked at, and gives details under each type of issue as to what might prevent a successful migration of this account into an ALZ environment.")550print(y)...
main.py
Source:main.py
1import vk2import time3import settings4import psutil5import datetime6session = vk.Session()7api = vk.API(session, access_token=settings.vk_token, v='5.60', lang='ru')8find = 09while True:10 find = 011 for proc in psutil.process_iter():12 if proc.name() == "osu.exe":13 pid = proc.pid14 processNameFull = psutil.Process(pid).name()15 processName = processNameFull[:-4]16 process = psutil.Process(pid).create_time()17 processD = datetime.datetime.fromtimestamp(process).strftime("%H:%M:%S")18 processStatus = psutil.Process(pid).status()19 print("Current status: IN GAME | " + str(processName) + " (" + str(processStatus) + ")")20 find = 121 time.sleep(10)22 if proc.name() == "pycharm64.exe":23 pid = proc.pid24 processName = psutil.Process(pid).name()[:-6]25 processStatus = psutil.Process(pid).status()26 print("Current status: PROGRAM | " + str(processName) + " (" + str(processStatus) + ")")27 find = 128 time.sleep(10)29 if proc.name() == "chrome.exe":30 pid = proc.pid31 processName = psutil.Process(pid).name()[:-4]32 processStatus = psutil.Process(pid).status()33 print("Current status: IN BROWSER | " + str(processName) + " ("+str(processStatus)+")")34 find = 135 time.sleep(10)36 if find == 0:...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!