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,719 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
AWS CloudFormation Orphaned Stack Detection and Analysis Script
|
5
|
+
|
6
|
+
This enterprise-grade script provides comprehensive detection and analysis of orphaned CloudFormation
|
7
|
+
stacks across multi-account AWS Organizations environments. Designed for infrastructure teams managing
|
8
|
+
CloudFormation StackSets at scale, offering automated orphaned stack identification, cross-account
|
9
|
+
stack reconciliation, and organizational governance support for infrastructure cleanup and compliance.
|
10
|
+
|
11
|
+
Key Features:
|
12
|
+
- Multi-account, multi-region CloudFormation stack and StackSet reconciliation
|
13
|
+
- Orphaned stack instance detection comparing StackSet definitions with deployed stacks
|
14
|
+
- Fragment-based filtering for targeted orphaned stack analysis
|
15
|
+
- Comprehensive cross-account stack inventory and comparison analysis
|
16
|
+
- Enterprise governance support with account exclusion and targeted analysis
|
17
|
+
- Multi-threaded processing for efficient large-scale stack discovery
|
18
|
+
|
19
|
+
Orphaned Stack Detection:
|
20
|
+
- Cross-references StackSet instances in management account with actual stacks in member accounts
|
21
|
+
- Identifies StackSet instances that exist in management account but not in target accounts
|
22
|
+
- Discovers stacks in member accounts that are not managed by any StackSet
|
23
|
+
- Provides detailed mismatch analysis for infrastructure cleanup and governance
|
24
|
+
- Supports fragment-based filtering for targeted orphaned stack identification
|
25
|
+
|
26
|
+
Authentication & Access:
|
27
|
+
- AWS Organizations support for centralized StackSet and stack management
|
28
|
+
- Cross-account role assumption for organizational stack visibility
|
29
|
+
- Regional validation and multi-region support for comprehensive coverage
|
30
|
+
- Profile-based authentication with comprehensive credential management
|
31
|
+
|
32
|
+
Performance & Scalability:
|
33
|
+
- Multi-threaded stack discovery for efficient large-scale processing
|
34
|
+
- Progress tracking and performance metrics for operational visibility
|
35
|
+
- Efficient credential management for multi-account stack enumeration
|
36
|
+
- Memory-efficient processing for extensive StackSet and stack inventories
|
37
|
+
|
38
|
+
Enterprise Use Cases:
|
39
|
+
- Infrastructure cleanup and orphaned resource identification
|
40
|
+
- StackSet governance and stack instance lifecycle management
|
41
|
+
- Organizational compliance monitoring and drift detection
|
42
|
+
- Infrastructure migration and consolidation planning
|
43
|
+
- Automated cleanup recommendation generation
|
44
|
+
|
45
|
+
Security & Compliance:
|
46
|
+
- Account exclusion capabilities preventing analysis on sensitive accounts
|
47
|
+
- Comprehensive audit logging for stack analysis and discovery operations
|
48
|
+
- Regional access validation and opt-in status verification
|
49
|
+
- Safe credential handling with automatic session management
|
50
|
+
|
51
|
+
Historical Context:
|
52
|
+
This script was originally created to support the "move_stack_instances.py" script by providing
|
53
|
+
recovery capabilities for stack instances that might be lost during StackSet migration operations.
|
54
|
+
While the move script now has built-in recovery, this script remains valuable for identifying
|
55
|
+
and analyzing orphaned stack instances that may occur during normal StackSet operations.
|
56
|
+
|
57
|
+
Dependencies:
|
58
|
+
- boto3: AWS SDK for CloudFormation and StackSet API access
|
59
|
+
- colorama: Terminal output formatting and colored display
|
60
|
+
- Custom modules: Inventory_Modules, ArgumentsClass, account_class for enterprise operations
|
61
|
+
|
62
|
+
Output Format:
|
63
|
+
- Comprehensive tabular analysis of orphaned stack instances
|
64
|
+
- Separate reporting for parent StackSet instances and child stacks
|
65
|
+
- Performance timing metrics and operational summaries
|
66
|
+
- Optional file export for further analysis and reporting
|
67
|
+
"""
|
68
|
+
|
69
|
+
import logging
|
70
|
+
import sys
|
71
|
+
from os.path import split
|
72
|
+
from time import time
|
73
|
+
|
74
|
+
from account_class import aws_acct_access
|
75
|
+
from ArgumentsClass import CommonArguments
|
76
|
+
|
77
|
+
# import simplejson as json
|
78
|
+
from botocore.exceptions import ClientError
|
79
|
+
from colorama import Fore, init
|
80
|
+
from Inventory_Modules import (
|
81
|
+
display_results,
|
82
|
+
find_stack_instances3,
|
83
|
+
find_stacks2,
|
84
|
+
find_stacksets3,
|
85
|
+
get_credentials_for_accounts_in_org,
|
86
|
+
get_regions3,
|
87
|
+
print_timings,
|
88
|
+
)
|
89
|
+
|
90
|
+
init()
|
91
|
+
__version__ = "2024.05.18"
|
92
|
+
ERASE_LINE = "\x1b[2K"
|
93
|
+
begin_time = time()
|
94
|
+
|
95
|
+
|
96
|
+
##################
|
97
|
+
# Functions
|
98
|
+
##################
|
99
|
+
|
100
|
+
|
101
|
+
def parse_args(fargs):
|
102
|
+
"""
|
103
|
+
Parse and validate command-line arguments for CloudFormation orphaned stack detection operations.
|
104
|
+
|
105
|
+
Configures comprehensive argument parsing for orphaned stack analysis across multi-account AWS
|
106
|
+
Organizations environments. Supports enterprise-grade CloudFormation management with profile
|
107
|
+
configuration, regional targeting, fragment-based filtering, and operational controls for
|
108
|
+
large-scale stack reconciliation and orphaned resource identification.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
fargs (list): Command-line argument list for orphaned stack detection configuration
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
argparse.Namespace: Parsed argument namespace containing:
|
115
|
+
- Profile: AWS profile for organizational CloudFormation access
|
116
|
+
- Region: Home region for StackSet management and control operations
|
117
|
+
- Fragments: Stack name fragments for targeted orphaned stack filtering
|
118
|
+
- Exact: Boolean flag for exact fragment matching vs substring matching
|
119
|
+
- Accounts: Specific account list for targeted orphaned stack analysis
|
120
|
+
- SkipAccounts: Account exclusion list for selective stack enumeration
|
121
|
+
- SkipProfiles: Profile exclusion list for selective discovery operations
|
122
|
+
- AccessRoles: Custom IAM roles for cross-account StackSet access
|
123
|
+
- Filename: Output file path for orphaned stack analysis results
|
124
|
+
- Time: Performance timing flag for operational metrics and optimization
|
125
|
+
- loglevel: Logging verbosity for operational visibility and troubleshooting
|
126
|
+
- SearchRegionList: Target regions for comprehensive stack instance analysis
|
127
|
+
|
128
|
+
CLI Argument Categories:
|
129
|
+
- Single profile support for centralized StackSet management and analysis
|
130
|
+
- Single home region for StackSet control plane operations
|
131
|
+
- Fragment-based filtering for targeted orphaned stack identification
|
132
|
+
- Extended arguments including account filtering and performance timing
|
133
|
+
- Role-based access controls for cross-account StackSet operations
|
134
|
+
- File export capabilities for analysis results and reporting
|
135
|
+
- Multi-region search support for comprehensive stack instance coverage
|
136
|
+
|
137
|
+
Enterprise Features:
|
138
|
+
- Organizational profile management for centralized StackSet governance
|
139
|
+
- Regional filtering for geo-distributed StackSet architectures
|
140
|
+
- Account inclusion/exclusion for selective orphaned stack analysis
|
141
|
+
- Performance monitoring with timing metrics for operational optimization
|
142
|
+
- Comprehensive logging controls for audit and compliance requirements
|
143
|
+
- Custom role support for enhanced security and access control
|
144
|
+
|
145
|
+
Orphaned Stack Detection Features:
|
146
|
+
- Home region specification for StackSet control plane access
|
147
|
+
- Multi-region search capabilities for comprehensive stack instance analysis
|
148
|
+
- Fragment-based filtering for targeted orphaned stack identification
|
149
|
+
- Account-specific analysis for focused cleanup and governance operations
|
150
|
+
|
151
|
+
Usage Examples:
|
152
|
+
- Basic analysis: --profile mgmt-profile --region us-east-1
|
153
|
+
- Fragment filtering: --fragment "MyApp" --exact for targeted analysis
|
154
|
+
- Multi-region search: --SearchRegions us-east-1 us-west-2 eu-west-1
|
155
|
+
- Account targeting: --accounts 123456789012 234567890123 for specific analysis
|
156
|
+
- Performance timing: --timing for operational metrics and optimization analysis
|
157
|
+
"""
|
158
|
+
script_path, script_name = split(sys.argv[0]) # Extract script name for argument grouping
|
159
|
+
|
160
|
+
# Configure comprehensive argument parsing for orphaned stack detection
|
161
|
+
parser = CommonArguments()
|
162
|
+
parser.singleprofile() # Single AWS profile for centralized StackSet management
|
163
|
+
parser.singleregion() # Single home region for StackSet control operations
|
164
|
+
parser.fragment() # Fragment-based filtering for targeted stack identification
|
165
|
+
parser.extendedargs() # Extended arguments including account filtering and timing
|
166
|
+
parser.rolestouse() # Custom IAM roles for cross-account StackSet access
|
167
|
+
parser.save_to_file() # File export capabilities for analysis results
|
168
|
+
parser.verbosity() # Logging verbosity controls for operational visibility
|
169
|
+
parser.timing() # Performance timing metrics for operational optimization
|
170
|
+
parser.version(__version__)
|
171
|
+
|
172
|
+
# Script-specific arguments for orphaned stack detection operations
|
173
|
+
local = parser.my_parser.add_argument_group(script_name, "Parameters specific to this script")
|
174
|
+
local.add_argument(
|
175
|
+
"-R",
|
176
|
+
"--SearchRegions",
|
177
|
+
help="Target regions for comprehensive stack instance analysis and orphaned stack detection. "
|
178
|
+
"Supports multi-region search capabilities for thorough StackSet reconciliation across "
|
179
|
+
"geo-distributed AWS infrastructure deployments.",
|
180
|
+
default=["all"], # Default to all regions for comprehensive analysis
|
181
|
+
nargs="*", # Multiple regions supported for targeted analysis
|
182
|
+
metavar="region-name",
|
183
|
+
dest="SearchRegionList",
|
184
|
+
)
|
185
|
+
|
186
|
+
return parser.my_parser.parse_args(fargs)
|
187
|
+
|
188
|
+
|
189
|
+
def setup_auth_and_regions(fProfile: str, f_AccountList: list, f_Region: str, f_args) -> (aws_acct_access, list, list):
|
190
|
+
"""
|
191
|
+
Initialize AWS authentication and configure regional scope for orphaned stack detection operations.
|
192
|
+
|
193
|
+
Establishes comprehensive AWS Organizations access and validates regional scope for CloudFormation
|
194
|
+
orphaned stack analysis across multi-account environments. Performs input validation, credential
|
195
|
+
initialization, account enumeration, and operational parameter display for enterprise-grade
|
196
|
+
StackSet governance and infrastructure cleanup operations.
|
197
|
+
|
198
|
+
Args:
|
199
|
+
fProfile (str): AWS profile name for organizational CloudFormation access
|
200
|
+
None uses default credentials or profile configuration
|
201
|
+
f_AccountList (list): Specific account list for targeted orphaned stack analysis
|
202
|
+
None includes all organizational accounts for comprehensive coverage
|
203
|
+
f_Region (str): Home region for StackSet control plane operations and management
|
204
|
+
Must be a single, valid AWS region for StackSet operations
|
205
|
+
f_args: Parsed command-line arguments containing:
|
206
|
+
- SearchRegionList: Target regions for stack instance search operations
|
207
|
+
- SkipAccounts: Account exclusion list for selective analysis
|
208
|
+
- Fragments: Stack name fragments for targeted filtering
|
209
|
+
- Exact: Boolean flag for exact fragment matching
|
210
|
+
- Accounts: Account inclusion list for focused analysis
|
211
|
+
|
212
|
+
Returns:
|
213
|
+
tuple: Three-element tuple containing:
|
214
|
+
- aws_acct_access: Initialized AWS account access object with organizational context
|
215
|
+
- AccountList: List of account IDs for orphaned stack analysis scope
|
216
|
+
- RegionList: List of regions for comprehensive stack instance search operations
|
217
|
+
|
218
|
+
Authentication & Validation:
|
219
|
+
- Profile validation ensuring single profile specification for centralized management
|
220
|
+
- AWS Organizations connectivity validation with comprehensive error handling
|
221
|
+
- Regional validation ensuring home region is available and properly configured
|
222
|
+
- Account list filtering supporting both inclusion and exclusion patterns
|
223
|
+
|
224
|
+
Organizational Context:
|
225
|
+
- Account enumeration across AWS Organizations for comprehensive analysis scope
|
226
|
+
- Management account identification and organizational hierarchy establishment
|
227
|
+
- Child account filtering based on account inclusion/exclusion specifications
|
228
|
+
- Account status validation ensuring active accounts for analysis operations
|
229
|
+
|
230
|
+
Operational Display:
|
231
|
+
- Comprehensive parameter summary for operational visibility and confirmation
|
232
|
+
- Fragment filtering specifications with exact vs substring matching display
|
233
|
+
- Account scope display showing targeted vs comprehensive analysis mode
|
234
|
+
- Regional scope display for multi-region stack instance search operations
|
235
|
+
|
236
|
+
Error Handling:
|
237
|
+
- Profile validation with informative error messages and graceful exit
|
238
|
+
- AWS connectivity error handling with detailed diagnostic information
|
239
|
+
- Regional validation preventing invalid region specifications
|
240
|
+
- Comprehensive input validation with clear error messaging
|
241
|
+
|
242
|
+
Enterprise Features:
|
243
|
+
- AWS Organizations support for centralized StackSet governance
|
244
|
+
- Account filtering capabilities for selective orphaned stack analysis
|
245
|
+
- Multi-region support for comprehensive stack instance coverage
|
246
|
+
- Operational parameter display for audit trails and confirmation
|
247
|
+
"""
|
248
|
+
# Perform comprehensive input validation for profile specification
|
249
|
+
if isinstance(fProfile, str) or fProfile is None:
|
250
|
+
pass # Valid profile specification (string or None for default)
|
251
|
+
else: # Invalid profile type (list, integer, etc.) - should be caught at argparse level
|
252
|
+
print(
|
253
|
+
f"{Fore.RED}You specified an invalid profile name. This script only allows for one profile at a time. Please try again.{Fore.RESET}"
|
254
|
+
)
|
255
|
+
sys.exit(7)
|
256
|
+
|
257
|
+
# Initialize AWS account access with comprehensive error handling
|
258
|
+
try:
|
259
|
+
aws_acct = aws_acct_access(fProfile)
|
260
|
+
except ConnectionError as my_Error:
|
261
|
+
logging.error(f"Exiting due to error: {my_Error}")
|
262
|
+
sys.exit(8)
|
263
|
+
|
264
|
+
# Configure regional scope for orphaned stack detection operations
|
265
|
+
AllRegions = get_regions3(aws_acct) # All available regions for validation
|
266
|
+
RegionList = get_regions3(aws_acct, f_args.SearchRegionList) # Target regions for stack instance search
|
267
|
+
|
268
|
+
# Validate home region specification ensuring single region for StackSet control plane
|
269
|
+
if f_Region.lower() not in AllRegions:
|
270
|
+
print(
|
271
|
+
f"{Fore.RED}You specified '{f_Region}' as the region, but this script only works with a single region.\n"
|
272
|
+
f"Please run the command again and specify only a single, valid region{Fore.RESET}"
|
273
|
+
)
|
274
|
+
sys.exit(9)
|
275
|
+
|
276
|
+
print()
|
277
|
+
|
278
|
+
# Configure account scope for orphaned stack analysis with organizational context
|
279
|
+
ChildAccounts = [] # List of account dictionaries: AccountId, AccountEmail, AccountStatus, MgmtAccount
|
280
|
+
|
281
|
+
if f_AccountList is None:
|
282
|
+
# Include all organizational accounts for comprehensive orphaned stack analysis
|
283
|
+
ChildAccounts = aws_acct.ChildAccounts
|
284
|
+
else:
|
285
|
+
# Filter accounts based on user-specified inclusion list for targeted analysis
|
286
|
+
for account in aws_acct.ChildAccounts:
|
287
|
+
if account["AccountId"] in f_AccountList:
|
288
|
+
ChildAccounts.append(
|
289
|
+
{
|
290
|
+
"AccountId": account["AccountId"], # Account identifier for stack analysis
|
291
|
+
"AccountEmail": account["AccountEmail"], # Account contact for audit context
|
292
|
+
"AccountStatus": account["AccountStatus"], # Account operational status
|
293
|
+
"MgmtAccount": account["MgmtAccount"], # Management account reference
|
294
|
+
}
|
295
|
+
)
|
296
|
+
|
297
|
+
# Extract account IDs for efficient processing and operational context
|
298
|
+
AccountList = [account["AccountId"] for account in ChildAccounts]
|
299
|
+
|
300
|
+
# Display comprehensive operational parameters for confirmation and audit trails
|
301
|
+
print(f"You asked me to find orphaned stacksets that match the following:")
|
302
|
+
print(f"\t\tIn the {aws_acct.AccountType} account {aws_acct.acct_number}")
|
303
|
+
print(f"\t\tIn this home Region: {f_Region}")
|
304
|
+
|
305
|
+
# Display search region configuration if specified
|
306
|
+
print(
|
307
|
+
f"\t\tFor stackset instances whose region matches this region fragment: {f_args.SearchRegionList}"
|
308
|
+
) if f_args.SearchRegionList is not None else ""
|
309
|
+
|
310
|
+
# Display account exclusion list if specified
|
311
|
+
print(
|
312
|
+
f"While skipping these accounts:\n{Fore.RED}{f_args.SkipAccounts}{Fore.RESET}"
|
313
|
+
) if f_args.SkipAccounts is not None else ""
|
314
|
+
|
315
|
+
# Display fragment filtering configuration with exact vs substring matching
|
316
|
+
if f_args.Exact:
|
317
|
+
print(f"\t\tFor stacksets that {Fore.RED}exactly match{Fore.RESET}: {f_args.Fragments}")
|
318
|
+
else:
|
319
|
+
print(
|
320
|
+
f"\t\tFor stacksets that contain th{'is fragment' if len(f_args.Fragments) == 1 else 'ese fragments'}: {f_args.Fragments}"
|
321
|
+
)
|
322
|
+
|
323
|
+
# Display account scope configuration for targeted vs comprehensive analysis
|
324
|
+
if f_args.Accounts is None:
|
325
|
+
print(f"\t\tFor stack instances across all accounts")
|
326
|
+
else:
|
327
|
+
print(
|
328
|
+
f"\t\tSpecifically to find th{'ese' if len(f_args.Accounts) > 1 else 'is'} account number{'s' if len(f_args.Accounts) > 1 else ''}: {f_args.Accounts}"
|
329
|
+
)
|
330
|
+
print()
|
331
|
+
|
332
|
+
return aws_acct, AccountList, RegionList
|
333
|
+
|
334
|
+
|
335
|
+
def find_stacks_within_child_accounts(fall_credentials, fFragmentlist: list = None, threads: int = 25):
|
336
|
+
"""
|
337
|
+
Discover and enumerate CloudFormation stacks across multiple organizational accounts using multi-threading.
|
338
|
+
|
339
|
+
Performs comprehensive CloudFormation stack discovery using multi-threaded processing to efficiently
|
340
|
+
inventory stacks across large-scale AWS Organizations environments. Designed for orphaned stack
|
341
|
+
detection by providing detailed stack metadata extraction from member accounts for reconciliation
|
342
|
+
with StackSet instances in the management account.
|
343
|
+
|
344
|
+
Args:
|
345
|
+
fall_credentials (list): List of credential dictionaries for cross-account stack discovery containing:
|
346
|
+
- AccountId: AWS account number for CloudFormation stack access
|
347
|
+
- Region: Target AWS region for stack enumeration
|
348
|
+
- Success: Boolean indicating credential validity and access status
|
349
|
+
- AccountNumber: Alternative account identifier format
|
350
|
+
fFragmentlist (list): Stack name fragments for targeted search and filtering
|
351
|
+
None defaults to ["all"] for comprehensive stack discovery
|
352
|
+
threads (int): Maximum number of worker threads for concurrent processing
|
353
|
+
Default 25, automatically limited by credential count for optimization
|
354
|
+
|
355
|
+
Returns:
|
356
|
+
list: Comprehensive list of stack dictionaries containing:
|
357
|
+
- StackName: CloudFormation stack name identifier
|
358
|
+
- StackId: Unique AWS CloudFormation stack identifier
|
359
|
+
- StackStatus: Current operational status
|
360
|
+
- AccountId: AWS account containing the stack
|
361
|
+
- Region: AWS region where stack is deployed
|
362
|
+
- Additional stack metadata for orphaned stack analysis
|
363
|
+
|
364
|
+
Multi-threaded Architecture:
|
365
|
+
- Queue-based worker pattern for concurrent stack discovery
|
366
|
+
- Configurable worker thread pool with automatic scaling
|
367
|
+
- Thread-safe stack aggregation for comprehensive result collection
|
368
|
+
- Graceful error handling for authorization and access issues
|
369
|
+
|
370
|
+
Stack Discovery Features:
|
371
|
+
- Multi-account, multi-region CloudFormation stack enumeration
|
372
|
+
- Fragment-based filtering for targeted stack identification
|
373
|
+
- Comprehensive stack metadata extraction for governance analysis
|
374
|
+
- Cross-account stack visibility for organizational inventory
|
375
|
+
- Regional stack distribution analysis for operational planning
|
376
|
+
|
377
|
+
Performance Optimization:
|
378
|
+
- Concurrent processing for efficient large-scale stack discovery
|
379
|
+
- Worker thread scaling based on credential count for optimal performance
|
380
|
+
- Queue management for efficient work distribution and completion tracking
|
381
|
+
- Memory-efficient processing for extensive stack inventories
|
382
|
+
|
383
|
+
Enterprise Infrastructure Governance:
|
384
|
+
- Organizational stack visibility across accounts and regions
|
385
|
+
- Stack metadata aggregation for compliance and audit tracking
|
386
|
+
- Fragment-based search capabilities for targeted stack management
|
387
|
+
- Comprehensive error handling for operational resilience and troubleshooting
|
388
|
+
|
389
|
+
Error Handling:
|
390
|
+
- Authorization failure detection with graceful degradation
|
391
|
+
- AWS API error management with comprehensive logging
|
392
|
+
- Credential validation and failure tracking for multi-account operations
|
393
|
+
- Regional access validation preventing unauthorized stack enumeration
|
394
|
+
"""
|
395
|
+
from queue import Queue
|
396
|
+
from threading import Thread
|
397
|
+
|
398
|
+
class FindStacks(Thread):
|
399
|
+
"""
|
400
|
+
Multi-threaded worker class for concurrent CloudFormation stack discovery.
|
401
|
+
|
402
|
+
Implements thread-safe stack discovery using queue-based work distribution for
|
403
|
+
efficient processing of CloudFormation stacks across organizational accounts
|
404
|
+
and regions.
|
405
|
+
"""
|
406
|
+
|
407
|
+
def __init__(self, fqueue: Queue):
|
408
|
+
Thread.__init__(self)
|
409
|
+
self.queue = fqueue
|
410
|
+
|
411
|
+
def run(self):
|
412
|
+
while True:
|
413
|
+
# Extract work item from queue with account credentials and processing context
|
414
|
+
c_credential, c_fragmentlist = self.queue.get()
|
415
|
+
|
416
|
+
# Discover CloudFormation stacks with comprehensive error handling
|
417
|
+
try:
|
418
|
+
if c_credential["Success"]:
|
419
|
+
# Execute stack discovery using validated credentials
|
420
|
+
account_and_region_stacks = find_stacks2(c_credential, c_credential["Region"], c_fragmentlist)
|
421
|
+
AllFoundStacks.extend(account_and_region_stacks)
|
422
|
+
else:
|
423
|
+
# Skip failed credentials with informative logging
|
424
|
+
logging.info(
|
425
|
+
f"Skipping {c_credential['AccountNumber']} in {c_credential['Region']} as we failed to successfully access"
|
426
|
+
)
|
427
|
+
except Exception as my_Error:
|
428
|
+
# Handle comprehensive stack discovery errors with detailed logging
|
429
|
+
logging.error(
|
430
|
+
f"Error accessing account {c_credential['AccountId']} in region {c_credential['Region']} "
|
431
|
+
f"Skipping this account"
|
432
|
+
)
|
433
|
+
logging.info(f"Actual Error: {my_Error}")
|
434
|
+
finally:
|
435
|
+
# Mark queue item as completed for work distribution tracking
|
436
|
+
self.queue.task_done()
|
437
|
+
|
438
|
+
# Initialize multi-threaded processing infrastructure for stack discovery
|
439
|
+
checkqueue = Queue() # Queue for work distribution across worker threads
|
440
|
+
|
441
|
+
# Configure fragment filtering with default comprehensive discovery
|
442
|
+
if fFragmentlist is None:
|
443
|
+
fFragmentlist = ["all"] # Default to all stacks for comprehensive analysis
|
444
|
+
|
445
|
+
# Note: Account and skip account filtering is handled by upstream credential management
|
446
|
+
# This function receives pre-filtered credentials based on user specifications
|
447
|
+
|
448
|
+
# Configure optimal worker thread count based on credential count and system limits
|
449
|
+
WorkerThreads = min(len(fall_credentials), threads)
|
450
|
+
|
451
|
+
# Initialize aggregated results collection for discovered stacks
|
452
|
+
AllFoundStacks = []
|
453
|
+
|
454
|
+
# Create and start worker thread pool for concurrent stack discovery
|
455
|
+
for x in range(WorkerThreads):
|
456
|
+
worker = FindStacks(checkqueue)
|
457
|
+
# Daemon threads allow main thread exit even if workers are still processing
|
458
|
+
worker.daemon = True
|
459
|
+
worker.start()
|
460
|
+
|
461
|
+
# Queue stack discovery work items for worker thread processing
|
462
|
+
for credential in fall_credentials:
|
463
|
+
logging.info(f"Queueing account {credential['AccountId']} and {credential['Region']}")
|
464
|
+
try:
|
465
|
+
# Queue account and region combination for worker thread processing
|
466
|
+
# Note: Tuple structure is critical for proper parameter expansion in worker threads
|
467
|
+
checkqueue.put((credential, fFragmentlist))
|
468
|
+
except ClientError as my_Error:
|
469
|
+
# Handle authorization failures with informative error messaging
|
470
|
+
if "AuthFailure" in str(my_Error):
|
471
|
+
logging.error(
|
472
|
+
f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']}"
|
473
|
+
)
|
474
|
+
logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
|
475
|
+
pass
|
476
|
+
|
477
|
+
# Wait for all worker threads to complete stack discovery processing
|
478
|
+
checkqueue.join()
|
479
|
+
return AllFoundStacks
|
480
|
+
|
481
|
+
|
482
|
+
def reconcile_between_parent_stacksets_and_children_stacks(f_parent_stack_instances: list, f_child_stacks: list):
|
483
|
+
"""
|
484
|
+
Perform comprehensive reconciliation analysis between StackSet instances and deployed stacks.
|
485
|
+
|
486
|
+
Executes detailed cross-referencing between CloudFormation StackSet instances managed in the
|
487
|
+
management account and actual CloudFormation stacks deployed in member accounts to identify
|
488
|
+
orphaned resources, missing deployments, and infrastructure governance issues requiring cleanup
|
489
|
+
or remediation across organizational AWS environments.
|
490
|
+
|
491
|
+
Args:
|
492
|
+
f_parent_stack_instances (list): List of StackSet instance dictionaries from management account containing:
|
493
|
+
- StackId: Unique CloudFormation stack identifier for matching
|
494
|
+
- Account: Target account for StackSet instance deployment
|
495
|
+
- Region: Target region for StackSet instance deployment
|
496
|
+
- StackSetId: Parent StackSet identifier for organizational context
|
497
|
+
- Status: Current StackSet instance operational status
|
498
|
+
- StatusReason: Detailed status reason for troubleshooting
|
499
|
+
f_child_stacks (list): List of actual stack dictionaries from member accounts containing:
|
500
|
+
- StackId: Unique CloudFormation stack identifier for matching
|
501
|
+
- StackName: CloudFormation stack name (StackSet- prefix indicates StackSet managed)
|
502
|
+
- AccountNumber: Account containing the deployed stack
|
503
|
+
- Region: Region where stack is deployed
|
504
|
+
- StackStatus: Current stack operational status
|
505
|
+
|
506
|
+
Processing Logic:
|
507
|
+
1. Cross-reference StackSet instances with deployed stacks using StackId matching
|
508
|
+
2. Mark matched instances to identify properly managed StackSet deployments
|
509
|
+
3. Identify parent StackSet instances without corresponding child stacks (missing deployments)
|
510
|
+
4. Identify child stacks without corresponding parent StackSet instances (orphaned stacks)
|
511
|
+
5. Filter child stacks to include only StackSet-managed stacks (StackSet- prefix)
|
512
|
+
6. Generate comprehensive reports for infrastructure cleanup and governance
|
513
|
+
|
514
|
+
Orphaned Stack Detection:
|
515
|
+
- Parent instances not in child stacks indicate failed or missing StackSet deployments
|
516
|
+
- Child stacks not in parent instances indicate orphaned StackSet-managed resources
|
517
|
+
- Cross-account stack reconciliation ensuring organizational infrastructure alignment
|
518
|
+
- Performance timing for large-scale reconciliation operations
|
519
|
+
|
520
|
+
Reporting & Display:
|
521
|
+
- Separate tabular reports for parent and child orphaned resources
|
522
|
+
- Sortable display with account, region, and resource identifiers
|
523
|
+
- Optional file export for further analysis and remediation planning
|
524
|
+
- Comprehensive operational metrics and reconciliation statistics
|
525
|
+
|
526
|
+
Enterprise Governance:
|
527
|
+
- Infrastructure cleanup recommendations based on orphaned resource identification
|
528
|
+
- StackSet lifecycle management insights for operational optimization
|
529
|
+
- Organizational compliance monitoring through resource reconciliation
|
530
|
+
- Automated remediation planning through detailed orphaned resource analysis
|
531
|
+
|
532
|
+
Performance Monitoring:
|
533
|
+
- Comparison metrics tracking reconciliation operation efficiency
|
534
|
+
- Timing analysis for large-scale infrastructure reconciliation operations
|
535
|
+
- Memory-efficient processing for extensive StackSet and stack inventories
|
536
|
+
- Operational visibility through detailed progress and result logging
|
537
|
+
"""
|
538
|
+
# Initialize reconciliation tracking metrics for operational monitoring
|
539
|
+
child_comparisons = 0 # Counter for child stack comparison operations
|
540
|
+
parent_comparisons = 0 # Counter for parent StackSet instance comparisons
|
541
|
+
i = 0 # Match counter for successful reconciliation tracking
|
542
|
+
|
543
|
+
# Execute comprehensive cross-referencing between parent StackSet instances and child stacks
|
544
|
+
for ParentInstance in f_parent_stack_instances:
|
545
|
+
parent_comparisons += 1 # Track parent instance processing for metrics
|
546
|
+
|
547
|
+
# Compare current parent instance against all child stacks for matching
|
548
|
+
for Childinstance in f_child_stacks:
|
549
|
+
child_comparisons += 1 # Track child stack comparison operations
|
550
|
+
|
551
|
+
# Perform StackId-based matching to identify properly managed StackSet deployments
|
552
|
+
if "StackId" in ParentInstance.keys() and Childinstance["StackId"] == ParentInstance["StackId"]:
|
553
|
+
i += 1 # Increment successful match counter
|
554
|
+
|
555
|
+
# Log successful matches with timing for performance analysis
|
556
|
+
logging.debug(f"**** Match {i}!! **** - {time() - begin_time:.6f}")
|
557
|
+
logging.debug(f"Childinstance: {Childinstance['StackId']}")
|
558
|
+
logging.debug(f"ParentInstance: {ParentInstance['StackId']}")
|
559
|
+
|
560
|
+
# Mark both instances as matched for filtering operations
|
561
|
+
Childinstance["Matches"] = ParentInstance["StackId"] # Child stack matches parent
|
562
|
+
ParentInstance["Matches"] = Childinstance["StackId"] # Parent instance matches child
|
563
|
+
else:
|
564
|
+
continue # No match found, proceed to next comparison
|
565
|
+
|
566
|
+
# Display reconciliation performance metrics for operational visibility
|
567
|
+
print_timings(
|
568
|
+
pTiming,
|
569
|
+
verbose,
|
570
|
+
begin_time,
|
571
|
+
f"We compared {len(AllChildStackInstances)} child stacks against {len(AllParentStackInstancesInStackSets)} parent stack instances",
|
572
|
+
)
|
573
|
+
|
574
|
+
# Identify StackSet instances in management account without corresponding deployed stacks
|
575
|
+
Parent_Instances_Not_In_Children_Stacks = [
|
576
|
+
x for x in AllParentStackInstancesInStackSets if "Matches" not in x.keys()
|
577
|
+
]
|
578
|
+
|
579
|
+
# Identify StackSet-managed stacks in member accounts without corresponding parent instances
|
580
|
+
# Filter for StackSet-managed stacks only (StackSet- prefix) to exclude regular account stacks
|
581
|
+
Child_Instances_Not_In_Parent_Stacks = [
|
582
|
+
x for x in AllChildStackInstances if "Matches" not in x.keys() and (x["StackName"].find("StackSet-") > -1)
|
583
|
+
]
|
584
|
+
|
585
|
+
# Display comprehensive reconciliation results for operational analysis
|
586
|
+
print()
|
587
|
+
print(
|
588
|
+
f"We found {len(Parent_Instances_Not_In_Children_Stacks)} parent stack instances that are not in the child stacks"
|
589
|
+
)
|
590
|
+
print(f"We found {len(Child_Instances_Not_In_Parent_Stacks)} child stacks that are not in the parent stacksets")
|
591
|
+
print()
|
592
|
+
|
593
|
+
# Generate detailed tabular reports for orphaned resource analysis (if verbose logging enabled)
|
594
|
+
if verbose < 50:
|
595
|
+
# Configure display formatting for parent StackSet instances without child stacks
|
596
|
+
parent_display_dict = {
|
597
|
+
"Account": {"DisplayOrder": 1, "Heading": "Acct Number"}, # Target account for deployment
|
598
|
+
"Region": {"DisplayOrder": 2, "Heading": "Region"}, # Target region for deployment
|
599
|
+
"StackSetId": {"DisplayOrder": 3, "Heading": "StackSet Id"}, # Parent StackSet identifier
|
600
|
+
"Status": {"DisplayOrder": 4, "Heading": "Status"}, # StackSet instance status
|
601
|
+
"StatusReason": {"DisplayOrder": 5, "Heading": "Possible Reason"}, # Detailed status reason
|
602
|
+
}
|
603
|
+
|
604
|
+
# Display and export parent instances without corresponding child stacks
|
605
|
+
print(f"Stack Instances in the Root Account that don't appear in the Children")
|
606
|
+
sorted_Parent_Instances_Not_In_Children_Stacks = sorted(
|
607
|
+
Parent_Instances_Not_In_Children_Stacks, key=lambda k: (k["Account"], k["Region"], k["StackSetId"])
|
608
|
+
)
|
609
|
+
display_results(
|
610
|
+
sorted_Parent_Instances_Not_In_Children_Stacks, parent_display_dict, None, f"{pFilename}-Parent"
|
611
|
+
)
|
612
|
+
|
613
|
+
# Configure display formatting for child stacks without parent StackSet instances
|
614
|
+
child_display_dict = {
|
615
|
+
"AccountNumber": {"DisplayOrder": 1, "Heading": "Acct Number"}, # Account containing orphaned stack
|
616
|
+
"Region": {"DisplayOrder": 2, "Heading": "Region"}, # Region containing orphaned stack
|
617
|
+
"StackName": {"DisplayOrder": 3, "Heading": "Stack Name"}, # Orphaned stack name
|
618
|
+
"StackStatus": {"DisplayOrder": 4, "Heading": "Status"}, # Current stack status
|
619
|
+
}
|
620
|
+
|
621
|
+
# Display and export child stacks without corresponding parent StackSet instances
|
622
|
+
print(f"Stacks in the Children accounts that don't appear in the Root Stacksets")
|
623
|
+
sorted_Child_Instances_Not_In_Parent_Stacks = sorted(
|
624
|
+
Child_Instances_Not_In_Parent_Stacks, key=lambda k: (k["AccountNumber"], k["Region"], k["StackName"])
|
625
|
+
)
|
626
|
+
display_results(sorted_Child_Instances_Not_In_Parent_Stacks, child_display_dict, None, f"{pFilename}-Child")
|
627
|
+
|
628
|
+
|
629
|
+
##################
|
630
|
+
# Main
|
631
|
+
##################
|
632
|
+
|
633
|
+
if __name__ == "__main__":
|
634
|
+
args = parse_args(sys.argv[1:])
|
635
|
+
pProfile = args.Profile
|
636
|
+
pRegion = args.Region
|
637
|
+
pSearchRegionList = args.SearchRegionList
|
638
|
+
pAccounts = args.Accounts
|
639
|
+
pSkipAccounts = args.SkipAccounts
|
640
|
+
pSkipProfiles = args.SkipProfiles
|
641
|
+
pFilename = args.Filename
|
642
|
+
pRootOnly = False # It doesn't make any sense to think that this script would be used for only the root account
|
643
|
+
pExact = args.Exact
|
644
|
+
pRoles = args.AccessRoles
|
645
|
+
verbose = args.loglevel
|
646
|
+
pTiming = args.Time
|
647
|
+
pFragments = args.Fragments
|
648
|
+
# Setup logging levels
|
649
|
+
logging.basicConfig(level=verbose, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
|
650
|
+
logging.getLogger("boto3").setLevel(logging.CRITICAL)
|
651
|
+
logging.getLogger("botocore").setLevel(logging.CRITICAL)
|
652
|
+
logging.getLogger("s3transfer").setLevel(logging.CRITICAL)
|
653
|
+
logging.getLogger("urllib3").setLevel(logging.CRITICAL)
|
654
|
+
|
655
|
+
ERASE_LINE = "\x1b[2K"
|
656
|
+
begin_time = time()
|
657
|
+
|
658
|
+
# Setup credentials and regions (filtered by what they wanted to check)
|
659
|
+
aws_acct, AccountList, RegionList = setup_auth_and_regions(pProfile, pAccounts, pRegion, args)
|
660
|
+
# Determine the accounts we're checking
|
661
|
+
print_timings(pTiming, verbose, begin_time, "Just setup account and region list")
|
662
|
+
AllCredentials = get_credentials_for_accounts_in_org(
|
663
|
+
aws_acct, pSkipAccounts, pRootOnly, AccountList, pProfile, RegionList, pRoles, pTiming
|
664
|
+
)
|
665
|
+
print_timings(
|
666
|
+
pTiming,
|
667
|
+
verbose,
|
668
|
+
begin_time,
|
669
|
+
f"Finished getting {len(AllCredentials)} credentials for all accounts and regions in spec...",
|
670
|
+
)
|
671
|
+
|
672
|
+
# Connect to every account, and in every region specified, to find all stacks
|
673
|
+
print(
|
674
|
+
f"Now finding all stacks across {'all' if pAccounts is None else (len(pAccounts) * len(RegionList))} accounts and regions under the {aws_acct.AccountType} account {aws_acct.acct_number}"
|
675
|
+
)
|
676
|
+
AllChildStackInstances = find_stacks_within_child_accounts(AllCredentials, pFragments)
|
677
|
+
print_timings(
|
678
|
+
pTiming, verbose, begin_time, f"Just finished getting {len(AllChildStackInstances)} children stack instances"
|
679
|
+
)
|
680
|
+
# and then compare them with the stackset instances managed within the Root account, and find anything that doesn't match
|
681
|
+
|
682
|
+
# This is the list of stacksets in the root account
|
683
|
+
AllParentStackSets = find_stacksets3(aws_acct, pRegion, pFragments, pExact)
|
684
|
+
print_timings(
|
685
|
+
pTiming, verbose, begin_time, f"Just finished getting {len(AllParentStackSets['StackSets'])} parent stack sets"
|
686
|
+
)
|
687
|
+
print(f"Now getting all the stack instances for all {len(AllParentStackSets)} stacksets")
|
688
|
+
# This will be the listing of the stack_instances in each of the stacksets in the root account
|
689
|
+
AllParentStackInstancesInStackSets = []
|
690
|
+
for stackset_name, stackset_attributes in AllParentStackSets["StackSets"].items():
|
691
|
+
StackInstancesInStackSets = find_stack_instances3(
|
692
|
+
aws_acct, pRegion, stackset_name, faccountlist=AccountList, fregionlist=RegionList
|
693
|
+
)
|
694
|
+
# TODO: Filter out skipped / closed accounts within the stacksets
|
695
|
+
AllParentStackInstancesInStackSets.extend(StackInstancesInStackSets)
|
696
|
+
print_timings(
|
697
|
+
pTiming,
|
698
|
+
verbose,
|
699
|
+
begin_time,
|
700
|
+
f"Just finished getting {len(AllParentStackInstancesInStackSets)} parent stack instances",
|
701
|
+
)
|
702
|
+
# Then compare the stack_instances in the root account with the stack_instances in the child accounts to see if anything is missing
|
703
|
+
print(f"We found {len(AllChildStackInstances)} stack instances in the {len(AccountList)} child accounts")
|
704
|
+
print(
|
705
|
+
f"We found {len(AllParentStackInstancesInStackSets)} stack instances in the {len(AllParentStackSets['StackSets'])} stacksets in the root account"
|
706
|
+
)
|
707
|
+
print(f"Now cross-referencing these to find if there are any orphaned stacks...")
|
708
|
+
# Find the stacks that are in the root account but not in the child accounts
|
709
|
+
# And find any stack instances in the children accounts that are not in the root account
|
710
|
+
# And display them to the screen...
|
711
|
+
reconcile_between_parent_stacksets_and_children_stacks(AllParentStackInstancesInStackSets, AllChildStackInstances)
|
712
|
+
|
713
|
+
if pTiming:
|
714
|
+
print(ERASE_LINE)
|
715
|
+
print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
|
716
|
+
|
717
|
+
print()
|
718
|
+
print("Thanks for using this script...")
|
719
|
+
print()
|