runbooks 0.2.3__py3-none-any.whl → 0.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conftest.py +26 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/__init__.py +88 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/script_test_data.py +0 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +785 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security_baseline/README.md +324 -0
- runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
- runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
- runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
- runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
- runbooks/security_baseline/checklist/root_access_key.py +6 -1
- runbooks/security_baseline/config-origin.json +1 -1
- runbooks/security_baseline/config.json +1 -1
- runbooks/security_baseline/permission.json +1 -1
- runbooks/security_baseline/report_generator.py +10 -2
- runbooks/security_baseline/report_template_en.html +8 -8
- runbooks/security_baseline/report_template_jp.html +8 -8
- runbooks/security_baseline/report_template_kr.html +13 -13
- runbooks/security_baseline/report_template_vn.html +8 -8
- runbooks/security_baseline/requirements.txt +7 -0
- runbooks/security_baseline/run_script.py +8 -2
- runbooks/security_baseline/security_baseline_tester.py +10 -2
- runbooks/security_baseline/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.6.1.dist-info/METADATA +373 -0
- runbooks-0.6.1.dist-info/RECORD +237 -0
- {runbooks-0.2.3.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
- runbooks-0.6.1.dist-info/entry_points.txt +7 -0
- runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
- runbooks-0.6.1.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.3.dist-info/METADATA +0 -435
- runbooks-0.2.3.dist-info/RECORD +0 -61
- runbooks-0.2.3.dist-info/entry_points.txt +0 -3
- runbooks-0.2.3.dist-info/top_level.txt +0 -1
@@ -0,0 +1,711 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import sys
|
5
|
+
|
6
|
+
import ec2_vpc_utils as vpc_modules
|
7
|
+
import Inventory_Modules
|
8
|
+
from account_class import aws_acct_access
|
9
|
+
from ArgumentsClass import CommonArguments
|
10
|
+
from botocore.exceptions import ClientError
|
11
|
+
from colorama import Fore, init
|
12
|
+
from prettytable import PrettyTable
|
13
|
+
|
14
|
+
init()
|
15
|
+
__version__ = "2023.05.04"
|
16
|
+
|
17
|
+
parser = CommonArguments()
|
18
|
+
parser.singleprofile()
|
19
|
+
parser.verbosity()
|
20
|
+
parser.version(__version__)
|
21
|
+
parser.my_parser.add_argument(
|
22
|
+
"--explain",
|
23
|
+
dest="pExplain",
|
24
|
+
const=True,
|
25
|
+
default=False,
|
26
|
+
action="store_const",
|
27
|
+
help="This flag prints out the explanation of what this script would do.",
|
28
|
+
)
|
29
|
+
parser.my_parser.add_argument(
|
30
|
+
"-R",
|
31
|
+
"--role",
|
32
|
+
dest="Role",
|
33
|
+
metavar="rolename",
|
34
|
+
default=None,
|
35
|
+
help="This allows the provision of a role to be used to access the child account(s).",
|
36
|
+
)
|
37
|
+
parser.my_parser.add_argument(
|
38
|
+
"-a",
|
39
|
+
"--account",
|
40
|
+
dest="pChildAccountId",
|
41
|
+
metavar="New Account to be adopted into LZ",
|
42
|
+
default=None,
|
43
|
+
# required=True,
|
44
|
+
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",
|
45
|
+
)
|
46
|
+
parser.my_parser.add_argument(
|
47
|
+
"-q",
|
48
|
+
"--quick",
|
49
|
+
dest="Quick",
|
50
|
+
metavar="Shortcut the checking to only a single region",
|
51
|
+
const=True,
|
52
|
+
default=False,
|
53
|
+
action="store_const",
|
54
|
+
help="This flag only checks 'us-east-1', so makes the whole script run really fast.",
|
55
|
+
)
|
56
|
+
parser.my_parser.add_argument(
|
57
|
+
"+fix",
|
58
|
+
"+delete",
|
59
|
+
dest="FixRun",
|
60
|
+
const=True,
|
61
|
+
default=False,
|
62
|
+
action="store_const",
|
63
|
+
help="This will fix the issues found. If default VPCs must be deleted, you'll be asked to confirm.",
|
64
|
+
)
|
65
|
+
parser.my_parser.add_argument(
|
66
|
+
"+force",
|
67
|
+
dest="pVPCConfirm",
|
68
|
+
const=True,
|
69
|
+
default=False,
|
70
|
+
action="store_const",
|
71
|
+
help="This will delete the default VPCs found with NO confirmation. You still have to specify the +fix too",
|
72
|
+
)
|
73
|
+
# TODO: There should be an additional parameter here that would take a role name for access into the account,
|
74
|
+
# since it's likely that users won't be able to use the AWSControlTowerExecution role
|
75
|
+
|
76
|
+
args = parser.my_parser.parse_args()
|
77
|
+
|
78
|
+
Quick = args.Quick
|
79
|
+
pProfile = args.Profile
|
80
|
+
pChildAccountId = args.pChildAccountId
|
81
|
+
verbose = args.loglevel
|
82
|
+
pRole = args.Role
|
83
|
+
FixRun = args.FixRun
|
84
|
+
pExplain = args.pExplain
|
85
|
+
pVPCConfirm = args.pVPCConfirm
|
86
|
+
logging.basicConfig(level=args.loglevel, format="[%(filename)s:%(lineno)s - %(funcName)30s() ] %(message)s")
|
87
|
+
# This is hard-coded, because this is the listing of regions that are supported by Automated Landing Zone.
|
88
|
+
if Quick:
|
89
|
+
RegionList = ["us-east-1"]
|
90
|
+
else:
|
91
|
+
# RegionList = Inventory_Modules.get_ec2_regions('all', pProfile)
|
92
|
+
# 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.
|
93
|
+
RegionList = [
|
94
|
+
"ap-northeast-1",
|
95
|
+
"ap-northeast-2",
|
96
|
+
"ap-south-1",
|
97
|
+
"ap-southeast-1",
|
98
|
+
"ap-southeast-2",
|
99
|
+
"ca-central-1",
|
100
|
+
"eu-central-1",
|
101
|
+
"eu-north-1",
|
102
|
+
"eu-west-1",
|
103
|
+
"eu-west-2",
|
104
|
+
"eu-west-3",
|
105
|
+
"sa-east-1",
|
106
|
+
"us-east-1",
|
107
|
+
"us-east-2",
|
108
|
+
"us-west-1",
|
109
|
+
"us-west-2",
|
110
|
+
]
|
111
|
+
|
112
|
+
ERASE_LINE = "\x1b[2K"
|
113
|
+
|
114
|
+
ExplainMessage = """
|
115
|
+
|
116
|
+
0. The Child account MUST allow the Management account access into the Child IAM role called "AWSCloudFormationStackSetExecutionRole"
|
117
|
+
0a. 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]
|
118
|
+
0b. 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.
|
119
|
+
|
120
|
+
1. 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.
|
121
|
+
|
122
|
+
2. 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):
|
123
|
+
aws configservice describe-delivery-channels
|
124
|
+
aws configservice describe-delivery-channel-status
|
125
|
+
aws configservice describe-configuration-recorders
|
126
|
+
aws configservice stop-configuration-recorder --configuration-recorder-name <NAME-FROM-DESCRIBE-OUTPUT>
|
127
|
+
aws configservice delete-delivery-channel --delivery-channel-name <NAME-FROM-DESCRIBE-OUTPUT>
|
128
|
+
aws configservice delete-configuration-recorder --configuration-recorder-name <NAME-FROM-DESCRIBE-OUTPUT
|
129
|
+
|
130
|
+
3. The account must not have a Cloudtrail Trail name the same name as the LZ Trail ("AWS-Landing-Zone-BaselineCloudTrail")
|
131
|
+
|
132
|
+
4. The account must not have a pending guard duty invite. You can check from the Guard Duty Console
|
133
|
+
|
134
|
+
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.
|
135
|
+
- If the existing account is to be imported as a Core Account, modify the manifest.yaml file to use it.
|
136
|
+
- 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.
|
137
|
+
|
138
|
+
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.
|
139
|
+
|
140
|
+
"""
|
141
|
+
print()
|
142
|
+
if pExplain:
|
143
|
+
print(ExplainMessage)
|
144
|
+
sys.exit("Exiting after Script Explanation...")
|
145
|
+
|
146
|
+
# TODO: This is supposed to be the recommended Trust Policy for the cross-account role access,
|
147
|
+
# so that we can recommend people add it to their "AWSCloudFormationStackSetExecutionRole"
|
148
|
+
json_formatted_str_TP = ""
|
149
|
+
|
150
|
+
|
151
|
+
##########
|
152
|
+
def _initdict(StepCount, faccountList):
|
153
|
+
fProcessStatus = {}
|
154
|
+
for account in faccountList:
|
155
|
+
fProcessStatus[account] = {"ChildIsReady": False, "IssuesFound": 0, "IssuesFixed": 0}
|
156
|
+
for _ in range(StepCount):
|
157
|
+
Step = f"Step{str(_)}"
|
158
|
+
fProcessStatus[account][Step] = dict()
|
159
|
+
fProcessStatus[account][Step]["Success"] = False
|
160
|
+
fProcessStatus[account][Step]["IssuesFound"] = 0
|
161
|
+
fProcessStatus[account][Step]["IssuesFixed"] = 0
|
162
|
+
return fProcessStatus
|
163
|
+
|
164
|
+
|
165
|
+
###########
|
166
|
+
|
167
|
+
|
168
|
+
# Step 0 -
|
169
|
+
"""
|
170
|
+
0. The Child account MUST allow the Management account access into the Child IAM role called "AWSCloudFormationStackSetExecutionRole"
|
171
|
+
Technically, only the Lambda function roles REQUIRE access, but for this script to run, we need that same level of access.
|
172
|
+
TODO: 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,
|
173
|
+
but that's sometime in the future.
|
174
|
+
"""
|
175
|
+
|
176
|
+
print("This script does 6 things... ")
|
177
|
+
print(
|
178
|
+
f"{Fore.BLUE} 0.{Fore.RESET} Checks to ensure you have the necessary cross-account role access to the child account."
|
179
|
+
)
|
180
|
+
print(f"{Fore.BLUE} 1.{Fore.RESET} Checks to ensure the {Fore.RED}Default VPCs {Fore.RESET}in each region are deleted")
|
181
|
+
if FixRun and not pVPCConfirm:
|
182
|
+
print(
|
183
|
+
f"{Fore.BLUE} You've asked to delete any default VPCs we find - with confirmation on each one.{Fore.RESET}"
|
184
|
+
)
|
185
|
+
elif FixRun and pVPCConfirm:
|
186
|
+
print()
|
187
|
+
print(
|
188
|
+
f"{Fore.RED} You've asked to delete any default VPCs we find - WITH NO CONFIRMATION on each one.{Fore.RESET}"
|
189
|
+
)
|
190
|
+
print()
|
191
|
+
elif pVPCConfirm and not FixRun:
|
192
|
+
print()
|
193
|
+
print(
|
194
|
+
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}"
|
195
|
+
)
|
196
|
+
print()
|
197
|
+
print(f"{Fore.BLUE} 2.{Fore.RESET} Checks the child account in each of the regions")
|
198
|
+
print(f" to see if there's already a {Fore.RED}Config Recorder and Delivery Channel {Fore.RESET}enabled...")
|
199
|
+
print(
|
200
|
+
f"{Fore.BLUE} 3.{Fore.RESET} Checks that there isn't a duplicate {Fore.RED}CloudTrail{Fore.RESET} trail in the account."
|
201
|
+
)
|
202
|
+
print(
|
203
|
+
f"{Fore.BLUE} 4.{Fore.RESET} Checks to see if {Fore.RED}GuardDuty{Fore.RESET} has been enabled for this child account."
|
204
|
+
)
|
205
|
+
print(" If it has been, it needs to be deleted before we can adopt this new account")
|
206
|
+
print(" into the Org's Automated Landing Zone.")
|
207
|
+
print(
|
208
|
+
f"{Fore.BLUE} 5.{Fore.RESET} This child account {Fore.RED}must exist{Fore.RESET} within the Parent Organization."
|
209
|
+
)
|
210
|
+
print(" If it doesn't - then you must move it into this Org")
|
211
|
+
print(" (this script can't do that for you).")
|
212
|
+
print()
|
213
|
+
print("Since this script is fairly new - All comments or suggestions are enthusiastically encouraged")
|
214
|
+
print()
|
215
|
+
|
216
|
+
DefaultVPCs = []
|
217
|
+
|
218
|
+
aws_account = aws_acct_access(pProfile)
|
219
|
+
if aws_account.AccountType.lower() == "root" and pChildAccountId is None:
|
220
|
+
# Creates a list of the account numbers in the Org.
|
221
|
+
ChildAccountList = [d["AccountId"] for d in aws_account.ChildAccounts]
|
222
|
+
print(
|
223
|
+
f"Since you didn't specify a specific account, we'll check all {len(aws_account.ChildAccounts)} accounts in the Org."
|
224
|
+
)
|
225
|
+
elif pChildAccountId is None:
|
226
|
+
sys.exit(
|
227
|
+
f"Account {aws_account.acct_number} is a {aws_account.AccountType} account. This script should be run with Management Account credentials."
|
228
|
+
)
|
229
|
+
else:
|
230
|
+
print(
|
231
|
+
f"Account {aws_account.acct_number} is a {aws_account.AccountType} account."
|
232
|
+
f"We're checking to validate that account {pChildAccountId} can be adopted into the Landing Zone"
|
233
|
+
)
|
234
|
+
ChildAccountList = [pChildAccountId]
|
235
|
+
|
236
|
+
Steps = 6
|
237
|
+
|
238
|
+
ProcessStatus = _initdict(Steps, ChildAccountList)
|
239
|
+
|
240
|
+
logging.error(f"There are {Steps} steps to go through for {len(ChildAccountList)} accounts")
|
241
|
+
|
242
|
+
# Determining access to each child account
|
243
|
+
accountsleft = len(ChildAccountList)
|
244
|
+
for childaccount in ChildAccountList:
|
245
|
+
accountsleft -= 1
|
246
|
+
# Step 0
|
247
|
+
"""
|
248
|
+
This part will ascertain whether access to the child works.
|
249
|
+
"""
|
250
|
+
try:
|
251
|
+
account_credentials = Inventory_Modules.get_child_access3(aws_account, childaccount)
|
252
|
+
except ClientError as my_Error:
|
253
|
+
if "AuthFailure" in str(my_Error):
|
254
|
+
# 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 script
|
255
|
+
print(f"Authorization Failure for account {childaccount}")
|
256
|
+
print(
|
257
|
+
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."
|
258
|
+
)
|
259
|
+
print("You must add the following lines to the Trust Policy of that role in the child account")
|
260
|
+
print(json_formatted_str_TP)
|
261
|
+
print(my_Error)
|
262
|
+
ProcessStatus[childaccount]["Step0"]["Success"] = False
|
263
|
+
elif str(my_Error).find("AccessDenied") > 0:
|
264
|
+
# 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 script
|
265
|
+
print(f"Access Denied Failure for account {childaccount}")
|
266
|
+
print(
|
267
|
+
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."
|
268
|
+
)
|
269
|
+
print("You must add the following lines to the Trust Policy of that role in the child account")
|
270
|
+
print(json_formatted_str_TP)
|
271
|
+
print(my_Error)
|
272
|
+
ProcessStatus[childaccount]["Step0"]["Success"] = False
|
273
|
+
else:
|
274
|
+
print(f"Other kind of failure for account {childaccount}")
|
275
|
+
print(my_Error)
|
276
|
+
ProcessStatus[childaccount]["Step0"]["Success"] = False
|
277
|
+
finally:
|
278
|
+
if not account_credentials.get("Success", False):
|
279
|
+
logging.error(
|
280
|
+
f"Was {Fore.RED}not{Fore.RESET} able to successfully connect to account {childaccount} using credentials from account {aws_account.acct_number}... "
|
281
|
+
)
|
282
|
+
print()
|
283
|
+
print(f"{Fore.RED}** Step 0 failed for account {childaccount}{Fore.RESET}")
|
284
|
+
print()
|
285
|
+
ProcessStatus[childaccount]["Step0"]["Success"] = False
|
286
|
+
ProcessStatus[childaccount]["Step0"]["IssuesFound"] += 1
|
287
|
+
continue
|
288
|
+
else:
|
289
|
+
logging.error(
|
290
|
+
f"Was able to successfully connect to account {childaccount} using credentials from account {aws_account.acct_number}... "
|
291
|
+
)
|
292
|
+
print()
|
293
|
+
print(f"{Fore.GREEN}** Step 0 completed without issues{Fore.RESET}")
|
294
|
+
print()
|
295
|
+
ProcessStatus[childaccount]["Step0"]["Success"] = True
|
296
|
+
|
297
|
+
# Step 1
|
298
|
+
"""
|
299
|
+
This part will find and delete the Default VPCs in each region for the child account.
|
300
|
+
We only delete if you provided that in the parameters list.
|
301
|
+
"""
|
302
|
+
try:
|
303
|
+
print(f"Checking account {childaccount} for default VPCs in any region")
|
304
|
+
DefaultVPCFound = False
|
305
|
+
for region in RegionList:
|
306
|
+
print(
|
307
|
+
ERASE_LINE,
|
308
|
+
f"Checking account {childaccount} in region {region} for {Fore.RED}default VPCs{Fore.RESET}",
|
309
|
+
end="\r",
|
310
|
+
)
|
311
|
+
logging.info("Looking for Default VPCs in account {} from Region {}}", childaccount, region)
|
312
|
+
DefaultVPC = Inventory_Modules.find_account_vpcs2(account_credentials, True)
|
313
|
+
if len(DefaultVPC["Vpcs"]) > 0:
|
314
|
+
DefaultVPCs.append(
|
315
|
+
{"VPCId": DefaultVPC["Vpcs"][0]["VpcId"], "AccountID": childaccount, "Region": region}
|
316
|
+
)
|
317
|
+
ProcessStatus[childaccount]["Step1"]["IssuesFound"] += 1
|
318
|
+
DefaultVPCFound = True
|
319
|
+
if DefaultVPCFound:
|
320
|
+
ProcessStatus[childaccount]["Step1"]["Success"] = False
|
321
|
+
else:
|
322
|
+
ProcessStatus[childaccount]["Step1"]["Success"] = True
|
323
|
+
except ClientError as my_Error:
|
324
|
+
logging.warning("Failed to identify the Default VPCs in the region properly")
|
325
|
+
ProcessStatus[childaccount]["Step1"]["Success"] = False
|
326
|
+
print(my_Error)
|
327
|
+
|
328
|
+
print(ERASE_LINE, end="\r")
|
329
|
+
for i in range(len(DefaultVPCs)):
|
330
|
+
logging.info(
|
331
|
+
f"I found a default VPC for account {DefaultVPCs[i]['AccountID']} in region {DefaultVPCs[i]['Region']}"
|
332
|
+
)
|
333
|
+
if FixRun:
|
334
|
+
logging.warning(
|
335
|
+
f"Deleting VpcId {DefaultVPCs[i]['VPCId']} in account {DefaultVPCs[i]['AccountID']} in region {DefaultVPCs[i]['Region']}"
|
336
|
+
)
|
337
|
+
try: # confirm the user really want to delete the VPC. This is irreversible
|
338
|
+
if pVPCConfirm:
|
339
|
+
ReallyDelete = True
|
340
|
+
else:
|
341
|
+
ReallyDelete = input(
|
342
|
+
f"Deletion of {DefaultVPCs[i]['Region']} default VPC has been requested. Are you still sure? (y/n): "
|
343
|
+
) in ["y", "Y"]
|
344
|
+
if ReallyDelete:
|
345
|
+
DelVPC_Success = (
|
346
|
+
vpc_modules.del_vpc(account_credentials, DefaultVPCs[i]["VPCId"], DefaultVPCs[i]["Region"]) == 0
|
347
|
+
)
|
348
|
+
if DelVPC_Success:
|
349
|
+
ProcessStatus[childaccount]["Step1"]["IssuesFixed"] += 1
|
350
|
+
else:
|
351
|
+
print("Something went wrong with the VPC Deletion")
|
352
|
+
ProcessStatus[childaccount]["Step1"]["Success"] = False
|
353
|
+
sys.exit(9)
|
354
|
+
else:
|
355
|
+
logging.warning("User answered False to the 'Are you sure' question")
|
356
|
+
print(
|
357
|
+
f"Skipping VPC ID {DefaultVPCs[i]['VPCId']} in account {DefaultVPCs[i]['AccountID']} in region {DefaultVPCs[i]['Region']}"
|
358
|
+
)
|
359
|
+
ProcessStatus[childaccount]["Step1"]["Success"] = False
|
360
|
+
except ClientError as my_Error:
|
361
|
+
logging.error("Failed to delete the Default VPCs in the region properly")
|
362
|
+
ProcessStatus[childaccount]["Step1"]["Success"] = False
|
363
|
+
print(my_Error)
|
364
|
+
|
365
|
+
print()
|
366
|
+
if ProcessStatus[childaccount]["Step1"]["Success"]:
|
367
|
+
print(f"{ERASE_LINE + Fore.GREEN}** Step 1 completed with no issues{Fore.RESET}")
|
368
|
+
elif ProcessStatus[childaccount]["Step1"]["IssuesFound"] - ProcessStatus[childaccount]["Step1"]["IssuesFixed"] == 0:
|
369
|
+
print(
|
370
|
+
f"{ERASE_LINE + Fore.GREEN}** Step 1 found {ProcessStatus[childaccount]['Step1']['IssuesFound']} issues, but they were fixed by deleting the default vpcs{Fore.RESET}"
|
371
|
+
)
|
372
|
+
ProcessStatus[childaccount]["Step1"]["Success"] = True
|
373
|
+
elif ProcessStatus[childaccount]["Step1"]["IssuesFound"] > ProcessStatus[childaccount]["Step1"]["IssuesFixed"]:
|
374
|
+
print(
|
375
|
+
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}"
|
376
|
+
)
|
377
|
+
else:
|
378
|
+
print(f"{ERASE_LINE + Fore.RED}** Step 1 completed with blockers found{Fore.RESET}")
|
379
|
+
|
380
|
+
# Step 2
|
381
|
+
# 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.
|
382
|
+
ConfigList = []
|
383
|
+
DeliveryChanList = []
|
384
|
+
config_deliv_channels_found = False
|
385
|
+
try:
|
386
|
+
# RegionList=Inventory_Modules.get_service_regions('config', 'all')
|
387
|
+
print(f"Checking account {childaccount} for Config Recorders and Delivery Channels in any region")
|
388
|
+
# TODO: Need to find a way to gracefully handle the error processing of opt-in regions.
|
389
|
+
# Until then - we're using a hard-coded listing of regions, instead of dynamically finding those.
|
390
|
+
for region in RegionList:
|
391
|
+
print(ERASE_LINE, f"Checking account {childaccount} in region {region} for Config Recorder", end="\r")
|
392
|
+
logging.info(f"Looking for Config Recorders in account {childaccount} from Region {region}")
|
393
|
+
ConfigRecorder = Inventory_Modules.find_config_recorders2(account_credentials, region)
|
394
|
+
logging.debug("Tried to capture Config Recorder")
|
395
|
+
if len(ConfigRecorder["ConfigurationRecorders"]) > 0:
|
396
|
+
ConfigList.append(
|
397
|
+
{
|
398
|
+
"Name": ConfigRecorder["ConfigurationRecorders"][0]["name"],
|
399
|
+
"roleARN": ConfigRecorder["ConfigurationRecorders"][0]["roleARN"],
|
400
|
+
"AccountID": childaccount,
|
401
|
+
"Region": region,
|
402
|
+
}
|
403
|
+
)
|
404
|
+
print(ERASE_LINE, f"Checking account {childaccount} in region {region} for Delivery Channel", end="\r")
|
405
|
+
DeliveryChannel = Inventory_Modules.find_delivery_channels2(account_credentials, region)
|
406
|
+
logging.debug("Tried to capture Delivery Channel")
|
407
|
+
if len(DeliveryChannel["DeliveryChannels"]) > 0:
|
408
|
+
DeliveryChanList.append(
|
409
|
+
{
|
410
|
+
"Name": DeliveryChannel["DeliveryChannels"][0]["name"],
|
411
|
+
"AccountID": childaccount,
|
412
|
+
"Region": region,
|
413
|
+
}
|
414
|
+
)
|
415
|
+
logging.error(
|
416
|
+
f"Checked account {childaccount} in {len(RegionList)} regions. Found {len(ConfigList) + len(DeliveryChanList)} issues with Config Recorders and Delivery Channels"
|
417
|
+
)
|
418
|
+
except ClientError as my_Error:
|
419
|
+
logging.warning("Failed to capture Config Recorder and Delivery Channels")
|
420
|
+
ProcessStatus[childaccount]["Step2"]["Success"] = False
|
421
|
+
print(my_Error)
|
422
|
+
|
423
|
+
for i in range(len(ConfigList)):
|
424
|
+
logging.error(
|
425
|
+
f"{Fore.RED}Found a config recorder for account %s in region %s",
|
426
|
+
ConfigList[i]["AccountID"],
|
427
|
+
ConfigList[i]["Region"] + Fore.RESET,
|
428
|
+
)
|
429
|
+
ProcessStatus[childaccount]["Step2"]["IssuesFound"] += 1
|
430
|
+
config_deliv_channels_found = True
|
431
|
+
if FixRun:
|
432
|
+
logging.warning(
|
433
|
+
"Deleting %s in account %s in region %s",
|
434
|
+
ConfigList[i]["Name"],
|
435
|
+
ConfigList[i]["AccountID"],
|
436
|
+
ConfigList[i]["Region"],
|
437
|
+
)
|
438
|
+
DelConfigRecorder = Inventory_Modules.del_config_recorder2(
|
439
|
+
account_credentials, ConfigList[i]["Region"], ConfigList[i]["Name"]
|
440
|
+
)
|
441
|
+
# We assume the process worked. We should probably NOT assume this.
|
442
|
+
ProcessStatus[childaccount]["Step2"]["IssuesFixed"] += 1
|
443
|
+
for i in range(len(DeliveryChanList)):
|
444
|
+
logging.error(
|
445
|
+
f"{Fore.RED}I found a delivery channel for account %s in region %s",
|
446
|
+
DeliveryChanList[i]["AccountID"],
|
447
|
+
DeliveryChanList[i]["Region"] + Fore.RESET,
|
448
|
+
)
|
449
|
+
ProcessStatus[childaccount]["Step2"]["IssuesFound"] += 1
|
450
|
+
config_deliv_channels_found = True
|
451
|
+
if FixRun:
|
452
|
+
logging.warning(
|
453
|
+
"Deleting %s in account %s in region %s",
|
454
|
+
DeliveryChanList[i]["Name"],
|
455
|
+
DeliveryChanList[i]["AccountID"],
|
456
|
+
DeliveryChanList[i]["Region"],
|
457
|
+
)
|
458
|
+
DelDeliveryChannel = Inventory_Modules.del_delivery_channel2(
|
459
|
+
account_credentials, ConfigList[i]["Region"], DeliveryChanList[i]["Name"]
|
460
|
+
)
|
461
|
+
# We assume the process worked. We should probably NOT assume this.
|
462
|
+
ProcessStatus[childaccount]["Step2"]["IssuesFixed"] += 1
|
463
|
+
if config_deliv_channels_found:
|
464
|
+
ProcessStatus[childaccount]["Step2"]["Success"] = False
|
465
|
+
else:
|
466
|
+
ProcessStatus[childaccount]["Step2"]["Success"] = True
|
467
|
+
|
468
|
+
if ProcessStatus[childaccount]["Step2"]["Success"]:
|
469
|
+
print(f"{ERASE_LINE + Fore.GREEN}** Step 2 completed with no issues{Fore.RESET}")
|
470
|
+
elif ProcessStatus[childaccount]["Step2"]["IssuesFound"] - ProcessStatus[childaccount]["Step2"]["IssuesFixed"] == 0:
|
471
|
+
print(
|
472
|
+
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}"
|
473
|
+
)
|
474
|
+
ProcessStatus[childaccount]["Step2"]["Success"] = True
|
475
|
+
elif ProcessStatus[childaccount]["Step2"]["IssuesFound"] > ProcessStatus[childaccount]["Step2"]["IssuesFixed"]:
|
476
|
+
print(
|
477
|
+
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}"
|
478
|
+
)
|
479
|
+
else:
|
480
|
+
print(f"{ERASE_LINE + Fore.RED}** Step 2 completed with blockers found{Fore.RESET}")
|
481
|
+
print()
|
482
|
+
|
483
|
+
# Step 3
|
484
|
+
# 3. The account must not have a Cloudtrail Trail name the same name as the LZ Trail ("AWS-Landing-Zone-BaselineCloudTrail")
|
485
|
+
CTtrails2 = []
|
486
|
+
trailname = None
|
487
|
+
ct_trail_found = False
|
488
|
+
try:
|
489
|
+
print(f"Checking account {childaccount} for a specially named CloudTrail in all regions")
|
490
|
+
for region in RegionList:
|
491
|
+
print(ERASE_LINE, f"Checking account {childaccount} in region {region} for CloudTrail trails", end="\r")
|
492
|
+
CTtrails = Inventory_Modules.find_cloudtrails2(
|
493
|
+
account_credentials, region, ["AWS-Landing-Zone-BaselineCloudTrail"]
|
494
|
+
)
|
495
|
+
if len(CTtrails) > 0:
|
496
|
+
logging.error(
|
497
|
+
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."
|
498
|
+
)
|
499
|
+
# CTtrails['trailList'][0]['region'] = region
|
500
|
+
CTtrails2.extend(CTtrails)
|
501
|
+
ct_trail_found = True
|
502
|
+
except ClientError as my_Error:
|
503
|
+
print(my_Error)
|
504
|
+
ProcessStatus[childaccount]["Step3"]["Success"] = False
|
505
|
+
|
506
|
+
for i in range(len(CTtrails2)):
|
507
|
+
logging.error(
|
508
|
+
f"{Fore.RED}Found a CloudTrail trail for account {childaccount} in region {CTtrails2[i]['HomeRegion']} named {CTtrails2[i]['Name']}{Fore.RESET}"
|
509
|
+
)
|
510
|
+
ProcessStatus[childaccount]["Step3"]["IssuesFound"] += 1
|
511
|
+
if FixRun:
|
512
|
+
try:
|
513
|
+
logging.error("CloudTrail trail deletion commencing...")
|
514
|
+
delresponse = Inventory_Modules.del_cloudtrails2(
|
515
|
+
account_credentials, CTtrails2[i]["HomeRegion"], CTtrails2[i]["TrailARN"]
|
516
|
+
)
|
517
|
+
ProcessStatus[childaccount]["Step3"]["IssuesFixed"] += 1
|
518
|
+
except ClientError as my_Error:
|
519
|
+
print(my_Error)
|
520
|
+
if ct_trail_found:
|
521
|
+
ProcessStatus[childaccount]["Step3"]["Success"] = False
|
522
|
+
else:
|
523
|
+
ProcessStatus[childaccount]["Step3"]["Success"] = True
|
524
|
+
|
525
|
+
if ProcessStatus[childaccount]["Step3"]["Success"]:
|
526
|
+
print(f"{ERASE_LINE + Fore.GREEN}** Step 3 completed with no issues{Fore.RESET}")
|
527
|
+
elif ProcessStatus[childaccount]["Step3"]["IssuesFound"] - ProcessStatus[childaccount]["Step3"]["IssuesFixed"] == 0:
|
528
|
+
print(
|
529
|
+
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}"
|
530
|
+
)
|
531
|
+
ProcessStatus[childaccount]["Step3"]["Success"] = True
|
532
|
+
elif ProcessStatus[childaccount]["Step3"]["IssuesFound"] > ProcessStatus[childaccount]["Step3"]["IssuesFixed"]:
|
533
|
+
print(
|
534
|
+
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}"
|
535
|
+
)
|
536
|
+
else:
|
537
|
+
print(f"{ERASE_LINE + Fore.RED}** Step 3 completed with blockers found{Fore.RESET}")
|
538
|
+
print()
|
539
|
+
|
540
|
+
# Step 4
|
541
|
+
# 4. The account must not have a pending guard duty invite. You can check from the Guard Duty Console
|
542
|
+
GDinvites2 = []
|
543
|
+
gdinvites_found = False
|
544
|
+
try:
|
545
|
+
print(f"Checking account {childaccount} for any GuardDuty invites")
|
546
|
+
for region in RegionList:
|
547
|
+
print(
|
548
|
+
f"{ERASE_LINE}Checking account {childaccount} in region {region} for {Fore.RED}GuardDuty{Fore.RESET}invitations",
|
549
|
+
end="\r",
|
550
|
+
)
|
551
|
+
GDinvites = Inventory_Modules.find_gd_invites2(account_credentials, region)
|
552
|
+
if len(GDinvites["Invitations"]) > 0:
|
553
|
+
gdinvites_found = True
|
554
|
+
for x in range(len(GDinvites["Invitations"])):
|
555
|
+
logging.warning("GD Invite: %s", str(GDinvites["Invitations"][x]))
|
556
|
+
logging.error(
|
557
|
+
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."
|
558
|
+
)
|
559
|
+
ProcessStatus[childaccount]["Step4"]["IssuesFound"] += 1
|
560
|
+
GDinvites2.append(
|
561
|
+
{
|
562
|
+
"AccountId": GDinvites["Invitations"][x]["AccountId"],
|
563
|
+
"InvitationId": GDinvites["Invitations"][x]["InvitationId"],
|
564
|
+
"Region": region,
|
565
|
+
}
|
566
|
+
)
|
567
|
+
except ClientError as my_Error:
|
568
|
+
print(my_Error)
|
569
|
+
ProcessStatus[childaccount]["Step4"]["Success"] = False
|
570
|
+
|
571
|
+
for i in range(len(GDinvites2)):
|
572
|
+
logging.error(
|
573
|
+
f"{Fore.RED}I found a GuardDuty invitation for account %s in region %s from account %s ",
|
574
|
+
childaccount,
|
575
|
+
GDinvites2[i]["Region"],
|
576
|
+
GDinvites2[i]["AccountId"] + Fore.RESET,
|
577
|
+
)
|
578
|
+
ProcessStatus[childaccount]["Step4"]["IssuesFound"] += 1
|
579
|
+
if FixRun:
|
580
|
+
for x in range(len(GDinvites2)):
|
581
|
+
try:
|
582
|
+
logging.warning("GuardDuty invite deletion commencing...")
|
583
|
+
delresponse = Inventory_Modules.delete_gd_invites2(
|
584
|
+
account_credentials, GDinvites2[x]["Region"], GDinvites2[x]["AccountId"]
|
585
|
+
)
|
586
|
+
ProcessStatus[childaccount]["Step4"]["IssuesFixed"] += 1
|
587
|
+
# We assume the process worked. We should probably NOT assume this.
|
588
|
+
except ClientError as my_Error:
|
589
|
+
print(my_Error)
|
590
|
+
if gdinvites_found:
|
591
|
+
ProcessStatus[childaccount]["Step4"]["Success"] = False
|
592
|
+
else:
|
593
|
+
ProcessStatus[childaccount]["Step4"]["Success"] = True
|
594
|
+
|
595
|
+
if ProcessStatus[childaccount]["Step4"]["Success"]:
|
596
|
+
print(f"{ERASE_LINE + Fore.GREEN}** Step 4 completed with no issues{Fore.RESET}")
|
597
|
+
elif ProcessStatus[childaccount]["Step4"]["IssuesFound"] - ProcessStatus[childaccount]["Step4"]["IssuesFixed"] == 0:
|
598
|
+
print(
|
599
|
+
f"{ERASE_LINE + Fore.GREEN}** Step 4 found {ProcessStatus[childaccount]['Step4']['IssuesFound']} guardduty invites, but they were deleted{Fore.RESET}"
|
600
|
+
)
|
601
|
+
ProcessStatus[childaccount]["Step4"]["Success"] = True
|
602
|
+
elif ProcessStatus[childaccount]["Step4"]["IssuesFound"] > ProcessStatus[childaccount]["Step4"]["IssuesFixed"]:
|
603
|
+
print(
|
604
|
+
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}"
|
605
|
+
)
|
606
|
+
else:
|
607
|
+
print(f"{ERASE_LINE + Fore.RED}** Step 4 completed with blockers found{Fore.RESET}")
|
608
|
+
print()
|
609
|
+
|
610
|
+
"""
|
611
|
+
# Step 4.5
|
612
|
+
# STS must be active in all regions. You can check from the Account Settings page in IAM.
|
613
|
+
We would have already verified this - since we've used STS to connect to each region already for the previous steps.
|
614
|
+
"""
|
615
|
+
# Step 5
|
616
|
+
"""
|
617
|
+
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.
|
618
|
+
- If the existing account is to be imported as a Core Account, modify the manifest.yaml file to use it.
|
619
|
+
- 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.
|
620
|
+
"""
|
621
|
+
print("Checking that the account is part of the AWS Organization.")
|
622
|
+
if childaccount in [d["AccountId"] for d in aws_account.ChildAccounts]:
|
623
|
+
ProcessStatus[childaccount]["Step5"]["Success"] = True
|
624
|
+
else:
|
625
|
+
print()
|
626
|
+
print(
|
627
|
+
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"
|
628
|
+
)
|
629
|
+
print("This is easiest done manually right now.")
|
630
|
+
ProcessStatus[childaccount]["Step5"]["Success"] = False
|
631
|
+
ProcessStatus[childaccount]["Step5"]["IssuesFound"] += 1
|
632
|
+
|
633
|
+
if ProcessStatus[childaccount]["Step5"]["Success"]:
|
634
|
+
print(f"{ERASE_LINE + Fore.GREEN}** Step 5 completed with no issues{Fore.RESET}")
|
635
|
+
elif ProcessStatus[childaccount]["Step5"]["IssuesFound"] - ProcessStatus[childaccount]["Step5"]["IssuesFixed"] == 0:
|
636
|
+
print(
|
637
|
+
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}"
|
638
|
+
)
|
639
|
+
ProcessStatus[childaccount]["Step5"]["Success"] = True
|
640
|
+
elif ProcessStatus[childaccount]["Step5"]["IssuesFound"] > ProcessStatus[childaccount]["Step5"]["IssuesFixed"]:
|
641
|
+
print(
|
642
|
+
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}"
|
643
|
+
)
|
644
|
+
else:
|
645
|
+
print(f"{ERASE_LINE + Fore.RED}** Step 5 completed with blockers found{Fore.RESET}")
|
646
|
+
print()
|
647
|
+
|
648
|
+
print(f"{Fore.CYAN}Account {childaccount} is complete. {accountsleft} more to go!!{Fore.RESET}")
|
649
|
+
|
650
|
+
"""
|
651
|
+
# Step 6
|
652
|
+
# 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.
|
653
|
+
So we'll need to verify that the parent OU of the account is the root of the organization.
|
654
|
+
"""
|
655
|
+
|
656
|
+
x = PrettyTable()
|
657
|
+
y = PrettyTable()
|
658
|
+
|
659
|
+
x.field_names = ["Account", "Issues Found", "Issues Fixed", "Ready?"]
|
660
|
+
# The following headers represent Step0 through Step5,
|
661
|
+
y.field_names = [
|
662
|
+
"Account",
|
663
|
+
"Account Access",
|
664
|
+
"Default VPCs",
|
665
|
+
"Recorders",
|
666
|
+
"CloudTrail",
|
667
|
+
"GuardDuty",
|
668
|
+
"Org Member",
|
669
|
+
"Ready?",
|
670
|
+
]
|
671
|
+
for item in ProcessStatus:
|
672
|
+
for _ in range(Steps):
|
673
|
+
Step = f"Step{str(_)}"
|
674
|
+
ProcessStatus[item]["IssuesFound"] += ProcessStatus[item][Step]["IssuesFound"]
|
675
|
+
ProcessStatus[item]["IssuesFound"] += ProcessStatus[item][Step]["IssuesFixed"]
|
676
|
+
x.add_row(
|
677
|
+
[
|
678
|
+
item,
|
679
|
+
ProcessStatus[item]["IssuesFound"],
|
680
|
+
ProcessStatus[item]["IssuesFixed"],
|
681
|
+
ProcessStatus[item]["ChildIsReady"],
|
682
|
+
]
|
683
|
+
)
|
684
|
+
y.add_row(
|
685
|
+
[
|
686
|
+
item,
|
687
|
+
ProcessStatus[item]["Step0"]["IssuesFound"] - ProcessStatus[item]["Step0"]["IssuesFixed"],
|
688
|
+
ProcessStatus[item]["Step1"]["IssuesFound"] - ProcessStatus[item]["Step1"]["IssuesFixed"],
|
689
|
+
ProcessStatus[item]["Step2"]["IssuesFound"] - ProcessStatus[item]["Step2"]["IssuesFixed"],
|
690
|
+
ProcessStatus[item]["Step3"]["IssuesFound"] - ProcessStatus[item]["Step3"]["IssuesFixed"],
|
691
|
+
ProcessStatus[item]["Step4"]["IssuesFound"] - ProcessStatus[item]["Step4"]["IssuesFixed"],
|
692
|
+
ProcessStatus[item]["Step5"]["IssuesFound"] - ProcessStatus[item]["Step5"]["IssuesFixed"],
|
693
|
+
ProcessStatus[item]["Step0"]["Success"]
|
694
|
+
and ProcessStatus[item]["Step1"]["Success"]
|
695
|
+
and ProcessStatus[item]["Step2"]["Success"]
|
696
|
+
and ProcessStatus[item]["Step3"]["Success"]
|
697
|
+
and ProcessStatus[item]["Step4"]["Success"]
|
698
|
+
and ProcessStatus[item]["Step5"]["Success"],
|
699
|
+
]
|
700
|
+
)
|
701
|
+
print(
|
702
|
+
"The following table represents the accounts looked at, and whether they are ready to be incorporated into an ALZ environment."
|
703
|
+
)
|
704
|
+
print(x)
|
705
|
+
print()
|
706
|
+
print(
|
707
|
+
"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."
|
708
|
+
)
|
709
|
+
print(y)
|
710
|
+
|
711
|
+
print("Thanks for using this script...")
|