runbooks 1.1.7__py3-none-any.whl → 1.1.10__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.
- runbooks/__init__.py +1 -1
- runbooks/__init___optimized.py +2 -1
- runbooks/_platform/__init__.py +1 -1
- runbooks/cfat/cli.py +4 -3
- runbooks/cfat/cloud_foundations_assessment.py +1 -2
- runbooks/cfat/tests/test_cli.py +4 -1
- runbooks/cli/commands/finops.py +68 -19
- runbooks/cli/commands/inventory.py +838 -14
- runbooks/cli/commands/operate.py +65 -4
- runbooks/cli/commands/vpc.py +1 -1
- runbooks/cloudops/cost_optimizer.py +1 -3
- runbooks/common/cli_decorators.py +6 -4
- runbooks/common/config_loader.py +787 -0
- runbooks/common/config_schema.py +280 -0
- runbooks/common/dry_run_framework.py +14 -2
- runbooks/common/mcp_integration.py +238 -0
- runbooks/finops/ebs_cost_optimizer.py +7 -4
- runbooks/finops/elastic_ip_optimizer.py +7 -4
- runbooks/finops/infrastructure/__init__.py +3 -2
- runbooks/finops/infrastructure/commands.py +7 -4
- runbooks/finops/infrastructure/load_balancer_optimizer.py +7 -4
- runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +7 -4
- runbooks/finops/nat_gateway_optimizer.py +7 -4
- runbooks/finops/tests/run_tests.py +1 -1
- runbooks/inventory/ArgumentsClass.py +2 -1
- runbooks/inventory/CLAUDE.md +41 -0
- runbooks/inventory/README.md +210 -2
- runbooks/inventory/Tests/test_Inventory_Modules.py +27 -10
- runbooks/inventory/Tests/test_cfn_describe_stacks.py +18 -7
- runbooks/inventory/Tests/test_ec2_describe_instances.py +30 -15
- runbooks/inventory/Tests/test_lambda_list_functions.py +17 -3
- runbooks/inventory/Tests/test_org_list_accounts.py +17 -4
- runbooks/inventory/account_class.py +0 -1
- runbooks/inventory/all_my_instances_wrapper.py +4 -8
- runbooks/inventory/aws_organization.png +0 -0
- runbooks/inventory/check_cloudtrail_compliance.py +4 -4
- runbooks/inventory/check_controltower_readiness.py +50 -47
- runbooks/inventory/check_landingzone_readiness.py +35 -31
- runbooks/inventory/cloud_foundations_integration.py +8 -3
- runbooks/inventory/collectors/aws_compute.py +59 -11
- runbooks/inventory/collectors/aws_management.py +39 -5
- runbooks/inventory/core/collector.py +1655 -159
- runbooks/inventory/core/concurrent_paginator.py +511 -0
- runbooks/inventory/discovery.md +15 -6
- runbooks/inventory/{draw_org_structure.py → draw_org.py} +55 -9
- runbooks/inventory/drift_detection_cli.py +8 -68
- runbooks/inventory/find_cfn_drift_detection.py +14 -4
- runbooks/inventory/find_cfn_orphaned_stacks.py +7 -5
- runbooks/inventory/find_cfn_stackset_drift.py +5 -5
- runbooks/inventory/find_ec2_security_groups.py +6 -3
- runbooks/inventory/find_landingzone_versions.py +5 -5
- runbooks/inventory/find_vpc_flow_logs.py +5 -5
- runbooks/inventory/inventory.sh +20 -7
- runbooks/inventory/inventory_mcp_cli.py +4 -0
- runbooks/inventory/inventory_modules.py +9 -7
- runbooks/inventory/list_cfn_stacks.py +18 -8
- runbooks/inventory/list_cfn_stackset_operation_results.py +2 -2
- runbooks/inventory/list_cfn_stackset_operations.py +32 -20
- runbooks/inventory/list_cfn_stacksets.py +7 -4
- runbooks/inventory/list_config_recorders_delivery_channels.py +4 -4
- runbooks/inventory/list_ds_directories.py +3 -3
- runbooks/inventory/list_ec2_availability_zones.py +7 -3
- runbooks/inventory/list_ec2_ebs_volumes.py +3 -3
- runbooks/inventory/list_ec2_instances.py +1 -1
- runbooks/inventory/list_ecs_clusters_and_tasks.py +8 -4
- runbooks/inventory/list_elbs_load_balancers.py +7 -3
- runbooks/inventory/list_enis_network_interfaces.py +3 -3
- runbooks/inventory/list_guardduty_detectors.py +9 -5
- runbooks/inventory/list_iam_policies.py +7 -3
- runbooks/inventory/list_iam_roles.py +3 -3
- runbooks/inventory/list_iam_saml_providers.py +8 -4
- runbooks/inventory/list_lambda_functions.py +8 -4
- runbooks/inventory/list_org_accounts.py +306 -276
- runbooks/inventory/list_org_accounts_users.py +45 -9
- runbooks/inventory/list_rds_db_instances.py +4 -4
- runbooks/inventory/list_route53_hosted_zones.py +3 -3
- runbooks/inventory/list_servicecatalog_provisioned_products.py +5 -5
- runbooks/inventory/list_sns_topics.py +4 -4
- runbooks/inventory/list_ssm_parameters.py +6 -3
- runbooks/inventory/list_vpc_subnets.py +8 -4
- runbooks/inventory/list_vpcs.py +15 -4
- runbooks/inventory/mcp_inventory_validator.py +771 -134
- runbooks/inventory/mcp_vpc_validator.py +6 -0
- runbooks/inventory/organizations_discovery.py +17 -3
- runbooks/inventory/organizations_utils.py +553 -0
- runbooks/inventory/output_formatters.py +422 -0
- runbooks/inventory/recover_cfn_stack_ids.py +5 -5
- runbooks/inventory/run_on_multi_accounts.py +3 -3
- runbooks/inventory/tag_coverage.py +481 -0
- runbooks/inventory/validation_utils.py +358 -0
- runbooks/inventory/verify_ec2_security_groups.py +18 -5
- runbooks/inventory/vpc_architecture_validator.py +7 -1
- runbooks/inventory/vpc_dependency_analyzer.py +6 -0
- runbooks/main_final.py +2 -2
- runbooks/main_ultra_minimal.py +2 -2
- runbooks/mcp/integration.py +6 -4
- runbooks/remediation/acm_remediation.py +2 -2
- runbooks/remediation/cloudtrail_remediation.py +2 -2
- runbooks/remediation/cognito_remediation.py +2 -2
- runbooks/remediation/dynamodb_remediation.py +2 -2
- runbooks/remediation/ec2_remediation.py +2 -2
- runbooks/remediation/kms_remediation.py +2 -2
- runbooks/remediation/lambda_remediation.py +2 -2
- runbooks/remediation/rds_remediation.py +2 -2
- runbooks/remediation/s3_remediation.py +1 -1
- runbooks/vpc/cloudtrail_audit_integration.py +1 -1
- {runbooks-1.1.7.dist-info → runbooks-1.1.10.dist-info}/METADATA +74 -4
- {runbooks-1.1.7.dist-info → runbooks-1.1.10.dist-info}/RECORD +112 -105
- runbooks/__init__.py.backup +0 -134
- {runbooks-1.1.7.dist-info → runbooks-1.1.10.dist-info}/WHEEL +0 -0
- {runbooks-1.1.7.dist-info → runbooks-1.1.10.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.7.dist-info → runbooks-1.1.10.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.7.dist-info → runbooks-1.1.10.dist-info}/top_level.txt +0 -0
@@ -32,20 +32,20 @@ from typing import List, Optional
|
|
32
32
|
from ..common.profile_utils import get_profile_for_operation, validate_profile_access
|
33
33
|
from ..common.rich_utils import console, print_error, print_info, print_success
|
34
34
|
from .mcp_inventory_validator import (
|
35
|
+
|
36
|
+
|
37
|
+
# Terminal control constants
|
35
38
|
create_inventory_mcp_validator,
|
36
39
|
generate_drift_report,
|
37
40
|
validate_inventory_results_with_mcp,
|
38
41
|
)
|
39
42
|
|
40
43
|
|
41
|
-
@click.group()
|
42
|
-
def drift():
|
43
|
-
"""Infrastructure drift detection and validation commands."""
|
44
|
-
pass
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
@click.
|
45
|
+
# Terminal control constants
|
46
|
+
ERASE_LINE = '\x1b[2K'
|
47
|
+
@click.command()
|
48
|
+
@click.option("--profile", "-p", help="AWS profile to use (overrides environment variables)")
|
49
49
|
@click.option("--profiles", help="Comma-separated list of AWS profiles for multi-account analysis")
|
50
50
|
@click.option(
|
51
51
|
"--terraform-dir",
|
@@ -60,7 +60,7 @@ def drift():
|
|
60
60
|
)
|
61
61
|
@click.option("--output-file", help="File path to save drift report (optional)")
|
62
62
|
@click.option("--threshold", type=float, default=99.5, help="Accuracy threshold for drift detection (default: 99.5%)")
|
63
|
-
def
|
63
|
+
def drift(
|
64
64
|
profile: Optional[str],
|
65
65
|
profiles: Optional[str],
|
66
66
|
terraform_dir: str,
|
@@ -152,66 +152,6 @@ def detect_drift(
|
|
152
152
|
raise click.Abort()
|
153
153
|
|
154
154
|
|
155
|
-
@drift.command("report")
|
156
|
-
@click.option("--profile", help="AWS profile to use for single-account analysis")
|
157
|
-
@click.option(
|
158
|
-
"--terraform-dir",
|
159
|
-
default="/Volumes/Working/1xOps/CloudOps-Runbooks/terraform-aws",
|
160
|
-
help="Path to terraform configuration directory",
|
161
|
-
)
|
162
|
-
@click.option(
|
163
|
-
"--format",
|
164
|
-
"report_format",
|
165
|
-
type=click.Choice(["json", "csv", "markdown"]),
|
166
|
-
default="json",
|
167
|
-
help="Report output format",
|
168
|
-
)
|
169
|
-
@click.option("--output", "output_file", required=True, help="Output file path for drift report")
|
170
|
-
def generate_report(profile: Optional[str], terraform_dir: str, report_format: str, output_file: str):
|
171
|
-
"""
|
172
|
-
Generate comprehensive infrastructure drift report.
|
173
|
-
|
174
|
-
Creates detailed drift analysis report with actionable recommendations
|
175
|
-
for infrastructure as code management and compliance.
|
176
|
-
"""
|
177
|
-
try:
|
178
|
-
# Use default profile if none specified
|
179
|
-
if not profile:
|
180
|
-
profile = get_profile_for_operation("operational", None)
|
181
|
-
|
182
|
-
print_info(f"Generating drift report for profile: {profile}")
|
183
|
-
|
184
|
-
# Validate profile
|
185
|
-
if not validate_profile_access(profile, "drift-reporting"):
|
186
|
-
print_error(f"Profile '{profile}' validation failed")
|
187
|
-
return
|
188
|
-
|
189
|
-
# Create mock inventory for demonstration
|
190
|
-
mock_inventory = _create_mock_inventory_data([profile])
|
191
|
-
|
192
|
-
# Generate comprehensive drift report
|
193
|
-
drift_report = generate_drift_report([profile], mock_inventory, terraform_dir)
|
194
|
-
|
195
|
-
# Export report
|
196
|
-
_export_drift_report(drift_report, output_file, report_format)
|
197
|
-
|
198
|
-
print_success(f"Drift report generated: {output_file}")
|
199
|
-
|
200
|
-
# Display summary
|
201
|
-
accounts_analyzed = drift_report.get("accounts_analyzed", 0)
|
202
|
-
overall_accuracy = drift_report.get("overall_accuracy", 0)
|
203
|
-
drift_detected = drift_report.get("drift_detected", False)
|
204
|
-
|
205
|
-
console.print(f"[dim]📊 Analysis Summary:[/]")
|
206
|
-
console.print(f"[dim] Accounts analyzed: {accounts_analyzed}[/]")
|
207
|
-
console.print(f"[dim] Overall accuracy: {overall_accuracy:.1f}%[/]")
|
208
|
-
console.print(f"[dim] Drift detected: {'Yes' if drift_detected else 'No'}[/]")
|
209
|
-
|
210
|
-
except Exception as e:
|
211
|
-
print_error(f"Report generation failed: {str(e)}")
|
212
|
-
raise click.Abort()
|
213
|
-
|
214
|
-
|
215
155
|
def _create_mock_inventory_data(profiles: List[str]) -> dict:
|
216
156
|
"""Create mock inventory data for demonstration purposes."""
|
217
157
|
mock_data = {}
|
@@ -66,13 +66,13 @@ Future Enhancements:
|
|
66
66
|
|
67
67
|
import logging
|
68
68
|
|
69
|
-
import Inventory_Modules
|
70
|
-
from account_class import aws_acct_access
|
71
|
-
from ArgumentsClass import CommonArguments
|
69
|
+
from runbooks.inventory import inventory_modules as Inventory_Modules
|
70
|
+
from runbooks.inventory.account_class import aws_acct_access
|
71
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
72
72
|
from botocore.exceptions import ClientError
|
73
73
|
from runbooks.common.rich_utils import console
|
74
|
+
from runbooks import __version__
|
74
75
|
|
75
|
-
__version__ = "2023.05.04"
|
76
76
|
|
77
77
|
# Configure comprehensive argument parsing for CloudFormation drift detection operations
|
78
78
|
parser = CommonArguments()
|
@@ -174,6 +174,16 @@ if aws_acct.AccountType == "Root":
|
|
174
174
|
"AccountStatus": aws_acct.AccountStatus, # Account operational status
|
175
175
|
}
|
176
176
|
]
|
177
|
+
else:
|
178
|
+
# Non-root account: Initialize ChildAccounts with single account entry
|
179
|
+
ChildAccounts = [
|
180
|
+
{
|
181
|
+
"MgmtAccount": aws_acct.acct_number, # Management account identifier
|
182
|
+
"AccountId": aws_acct.acct_number, # Account number for single account scope
|
183
|
+
"AccountEmail": aws_acct.MgmtEmail if hasattr(aws_acct, 'MgmtEmail') else "N/A", # Management email
|
184
|
+
"AccountStatus": aws_acct.AccountStatus if hasattr(aws_acct, 'AccountStatus') else "ACTIVE", # Account status
|
185
|
+
}
|
186
|
+
]
|
177
187
|
|
178
188
|
# Initialize drift detection operation tracking and regional scope configuration
|
179
189
|
NumStacksFound = 0 # Counter for tracking total stacks processed for drift detection
|
@@ -71,13 +71,13 @@ import sys
|
|
71
71
|
from os.path import split
|
72
72
|
from time import time
|
73
73
|
|
74
|
-
from account_class import aws_acct_access
|
75
|
-
from ArgumentsClass import CommonArguments
|
74
|
+
from runbooks.inventory.account_class import aws_acct_access
|
75
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
76
76
|
|
77
77
|
# import simplejson as json
|
78
78
|
from botocore.exceptions import ClientError
|
79
79
|
from runbooks.common.rich_utils import console
|
80
|
-
from
|
80
|
+
from runbooks.inventory.inventory_modules import (
|
81
81
|
display_results,
|
82
82
|
find_stack_instances3,
|
83
83
|
find_stacks2,
|
@@ -86,8 +86,11 @@ from Inventory_Modules import (
|
|
86
86
|
get_regions3,
|
87
87
|
print_timings,
|
88
88
|
)
|
89
|
+
from runbooks import __version__
|
90
|
+
|
91
|
+
# Terminal control constants
|
92
|
+
ERASE_LINE = '\x1b[2K'
|
89
93
|
|
90
|
-
__version__ = "2024.05.18"
|
91
94
|
begin_time = time()
|
92
95
|
|
93
96
|
|
@@ -650,7 +653,6 @@ if __name__ == "__main__":
|
|
650
653
|
logging.getLogger("s3transfer").setLevel(logging.CRITICAL)
|
651
654
|
logging.getLogger("urllib3").setLevel(logging.CRITICAL)
|
652
655
|
|
653
|
-
ERASE_LINE = "\x1b[2K"
|
654
656
|
begin_time = time()
|
655
657
|
|
656
658
|
# Setup credentials and regions (filtered by what they wanted to check)
|
@@ -66,15 +66,15 @@ from datetime import datetime
|
|
66
66
|
from os.path import split
|
67
67
|
from time import time
|
68
68
|
|
69
|
-
import Inventory_Modules
|
70
|
-
from account_class import aws_acct_access
|
71
|
-
from ArgumentsClass import CommonArguments
|
69
|
+
from runbooks.inventory import inventory_modules as Inventory_Modules
|
70
|
+
from runbooks.inventory.account_class import aws_acct_access
|
71
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
72
72
|
from botocore.exceptions import ClientError
|
73
73
|
from runbooks.common.rich_utils import console
|
74
|
-
from
|
74
|
+
from runbooks.inventory.inventory_modules import display_results
|
75
|
+
from runbooks import __version__
|
75
76
|
|
76
77
|
# Initialize colorama for cross-platform colored terminal output
|
77
|
-
__version__ = "2024.03.06"
|
78
78
|
begin_time = time() # Script execution timing for performance monitoring
|
79
79
|
sleep_interval = 5 # Default wait interval for drift detection operations
|
80
80
|
|
@@ -60,19 +60,22 @@ from queue import Queue
|
|
60
60
|
from threading import Thread
|
61
61
|
from time import time
|
62
62
|
|
63
|
-
from ArgumentsClass import CommonArguments
|
63
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
64
64
|
from botocore.exceptions import ClientError
|
65
65
|
from runbooks.common.rich_utils import console
|
66
|
-
from
|
66
|
+
from runbooks.inventory.inventory_modules import (
|
67
67
|
display_results,
|
68
68
|
find_references_to_security_groups2,
|
69
69
|
find_security_groups2,
|
70
70
|
get_all_credentials,
|
71
71
|
)
|
72
|
+
from runbooks import __version__
|
73
|
+
|
74
|
+
# Terminal control constants
|
75
|
+
ERASE_LINE = '\x1b[2K'
|
72
76
|
# Migrated to Rich.Progress - see rich_utils.py for enterprise UX standards
|
73
77
|
# from tqdm.auto import tqdm
|
74
78
|
|
75
|
-
__version__ = "2024.09.24"
|
76
79
|
begin_time = time()
|
77
80
|
|
78
81
|
|
@@ -73,14 +73,14 @@ License: MIT
|
|
73
73
|
import logging
|
74
74
|
|
75
75
|
import boto3
|
76
|
-
import Inventory_Modules
|
77
|
-
from ArgumentsClass import CommonArguments
|
76
|
+
from runbooks.inventory import inventory_modules as Inventory_Modules
|
77
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
78
78
|
from botocore.exceptions import ClientError, CredentialRetrievalError, InvalidConfigError
|
79
|
-
|
79
|
+
from runbooks.common.rich_utils import console
|
80
|
+
from runbooks import __version__
|
80
81
|
|
81
|
-
#
|
82
|
+
# colorama removed - migrated to Rich
|
82
83
|
|
83
|
-
__version__ = "2023.05.31"
|
84
84
|
|
85
85
|
# Configure comprehensive CLI argument parsing for Landing Zone discovery operations
|
86
86
|
parser = CommonArguments()
|
@@ -88,16 +88,16 @@ from time import sleep, time
|
|
88
88
|
from typing import Any, List
|
89
89
|
|
90
90
|
import boto3
|
91
|
-
import Inventory_Modules
|
92
|
-
from account_class import aws_acct_access
|
93
|
-
from ArgumentsClass import CommonArguments
|
91
|
+
from runbooks.inventory import inventory_modules as Inventory_Modules
|
92
|
+
from runbooks.inventory.account_class import aws_acct_access
|
93
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
94
94
|
from botocore.config import Config
|
95
95
|
from botocore.exceptions import ClientError
|
96
96
|
from runbooks.common.rich_utils import console
|
97
|
-
from
|
97
|
+
from runbooks.inventory.inventory_modules import RemoveCoreAccounts, display_results, get_all_credentials, get_regions3
|
98
|
+
from runbooks import __version__
|
98
99
|
|
99
100
|
# Initialize colorama for cross-platform colored terminal output
|
100
|
-
__version__ = "2024.03.10"
|
101
101
|
begin_time = time() # Script execution timing for performance monitoring
|
102
102
|
sleep_interval = 5 # Default wait interval for CloudWatch Logs query processing
|
103
103
|
|
runbooks/inventory/inventory.sh
CHANGED
@@ -66,7 +66,7 @@ fi
|
|
66
66
|
|
67
67
|
# Test execution settings
|
68
68
|
MAX_CONCURRENT_TESTS=5
|
69
|
-
TEST_TIMEOUT=
|
69
|
+
TEST_TIMEOUT=540 # 540 seconds per test (68 accounts × regions: list_cfn_stacks/list_vpcs timeout at 489s, need 540s with 10% headroom)
|
70
70
|
RETRY_ATTEMPTS=2
|
71
71
|
|
72
72
|
# Logging and output configuration
|
@@ -77,6 +77,9 @@ ERROR_ANALYSIS_FILE="error_analysis_$(date +%Y%m%d_%H%M%S).txt"
|
|
77
77
|
# Create test output directory
|
78
78
|
mkdir -p "$TEST_LOG_DIR"
|
79
79
|
|
80
|
+
# Initialize results temp file for IPC (counter persistence across subshells)
|
81
|
+
: > "$TEST_LOG_DIR/results.tmp"
|
82
|
+
|
80
83
|
# Test result tracking (using arrays compatible with older bash versions)
|
81
84
|
TEST_RESULTS_KEYS=()
|
82
85
|
TEST_RESULTS_VALUES=()
|
@@ -205,11 +208,11 @@ function log_test_result() {
|
|
205
208
|
# Determine test status
|
206
209
|
if [ "$exit_code" -eq 0 ]; then
|
207
210
|
status="PASSED"
|
208
|
-
|
211
|
+
echo "PASS:$script_name" >> "$TEST_LOG_DIR/results.tmp"
|
209
212
|
else
|
210
213
|
status="FAILED"
|
211
|
-
|
212
|
-
|
214
|
+
echo "FAIL:$script_name" >> "$TEST_LOG_DIR/results.tmp"
|
215
|
+
|
213
216
|
# Perform error analysis
|
214
217
|
analyze_test_errors "$script_name" "$log_file"
|
215
218
|
fi
|
@@ -339,9 +342,9 @@ function execute_test() {
|
|
339
342
|
echo "========================================" >> "$output_file"
|
340
343
|
|
341
344
|
# Execute test with timeout (macOS compatible)
|
342
|
-
#
|
345
|
+
# Use module execution pattern for package imports (python -m runbooks.inventory.script)
|
343
346
|
# Start the command in background and track its PID
|
344
|
-
(cd "$INVENTORY_DIR" &&
|
347
|
+
(cd "$INVENTORY_DIR" && uv run python -m runbooks.inventory.$(basename "$script_name" .py) $test_parameters) >> "$output_file" 2>&1 &
|
345
348
|
test_cmd_pid=$!
|
346
349
|
|
347
350
|
# Wait for process completion or timeout
|
@@ -393,7 +396,8 @@ scripts_to_not_test="Inventory_Modules.py cfn_recover_stack_ids.py cfn_lockdown_
|
|
393
396
|
account_class.py org_check_alz_readiness.py controltower_check_account_readiness.py s3_delete_objects.py cfn_enable_drift_detection.py \
|
394
397
|
org_describe_landingzone_versions.py cfn_move_stack_instances.py multi_account_runner.py iam_update_roles_cross_account.py ec2_vpc_utils.py \
|
395
398
|
cfn_recover_stack_ids.py setup.py aws_decorators.py cfn_list_stack_set_operation_results.py __pycache__ tests \
|
396
|
-
update_aws_actions.py update_iam_roles_cross_accounts.py run_on_multi_accounts.py
|
399
|
+
update_aws_actions.py update_iam_roles_cross_accounts.py run_on_multi_accounts.py vpc_architecture_validator.py vpc_dependency_analyzer.py mcp_vpc_validator.py \
|
400
|
+
check_controltower_readiness.py"
|
397
401
|
|
398
402
|
# Scripts that require interactive responses (cannot be tested autonomously)
|
399
403
|
# These scripts need manual input and are skipped in automated testing
|
@@ -588,6 +592,8 @@ echo "==========================================================================
|
|
588
592
|
echo "\nTEST EXECUTION SUMMARY"
|
589
593
|
echo "======================"
|
590
594
|
echo "Total Tests Executed: ${#arrScripts[@]}"
|
595
|
+
TESTS_PASSED=$(grep -c "^PASS:" "$TEST_LOG_DIR/results.tmp" 2>/dev/null || echo 0)
|
596
|
+
TESTS_FAILED=$(grep -c "^FAIL:" "$TEST_LOG_DIR/results.tmp" 2>/dev/null || echo 0)
|
591
597
|
echo "Tests Passed: $TESTS_PASSED"
|
592
598
|
echo "Tests Failed: $TESTS_FAILED"
|
593
599
|
echo "Tests Skipped: $TESTS_SKIPPED"
|
@@ -649,10 +655,17 @@ echo "==========================================================================
|
|
649
655
|
|
650
656
|
} | tee -a "$TEST_LOG_DIR/$SUMMARY_FILE"
|
651
657
|
|
658
|
+
# Read final counts from temp file for exit code logic
|
659
|
+
TESTS_PASSED=$(grep -c "^PASS:" "$TEST_LOG_DIR/results.tmp" 2>/dev/null || echo 0)
|
660
|
+
TESTS_FAILED=$(grep -c "^FAIL:" "$TEST_LOG_DIR/results.tmp" 2>/dev/null || echo 0)
|
661
|
+
|
652
662
|
# Set exit code based on test results
|
653
663
|
if [[ $TESTS_FAILED -gt 0 ]]; then
|
654
664
|
echo "\nWARNING: $TESTS_FAILED tests failed. Review logs for details."
|
655
665
|
exit 1
|
666
|
+
elif [[ $TESTS_PASSED -eq 0 && $TESTS_FAILED -eq 0 ]]; then
|
667
|
+
echo "\nERROR: No tests executed or counter error detected."
|
668
|
+
exit 1
|
656
669
|
else
|
657
670
|
echo "\nSUCCESS: All tests passed successfully."
|
658
671
|
exit 0
|
@@ -24,6 +24,10 @@ from ..common.rich_utils import console, print_error, print_info, print_success,
|
|
24
24
|
from .mcp_inventory_validator import create_inventory_mcp_validator
|
25
25
|
|
26
26
|
|
27
|
+
|
28
|
+
|
29
|
+
# Terminal control constants
|
30
|
+
ERASE_LINE = '\x1b[2K'
|
27
31
|
@click.command()
|
28
32
|
@click.option("--profile", help="AWS profile name (takes precedence over environment variables)")
|
29
33
|
@click.option(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import logging
|
2
|
+
from runbooks import __version__
|
2
3
|
|
3
|
-
__version__ = "2025.04.03"
|
4
4
|
|
5
5
|
"""
|
6
6
|
** Why are some functions "function" vs. "function2" vs. "function3"?**
|
@@ -457,8 +457,6 @@ def print_timings(fTiming: bool = False, fverbose: int = 50, fbegin_time=None, f
|
|
457
457
|
|
458
458
|
from runbooks.common.rich_utils import console
|
459
459
|
|
460
|
-
init()
|
461
|
-
|
462
460
|
if fTiming and fverbose < 50 and fbegin_time is not None:
|
463
461
|
print(f"[green]{fmessage}\nThis script has taken {time() - fbegin_time:.6f} seconds so far")
|
464
462
|
|
@@ -2652,8 +2650,12 @@ def find_account_policies2(
|
|
2652
2650
|
from botocore.exceptions import ClientError
|
2653
2651
|
|
2654
2652
|
# Handle both profile-based and credential-based authentication
|
2653
|
+
# Priority: Profile > ParentProfile > AccessKeyId > default credential chain
|
2655
2654
|
if "Profile" in ocredentials and ocredentials["Profile"]:
|
2656
2655
|
session_iam = boto3.Session(profile_name=ocredentials["Profile"], region_name=fRegion)
|
2656
|
+
elif "ParentProfile" in ocredentials and ocredentials["ParentProfile"]:
|
2657
|
+
# Use ParentProfile from get_all_credentials output
|
2658
|
+
session_iam = boto3.Session(profile_name=ocredentials["ParentProfile"], region_name=fRegion)
|
2657
2659
|
elif "AccessKeyId" in ocredentials and ocredentials["AccessKeyId"]:
|
2658
2660
|
session_iam = boto3.Session(
|
2659
2661
|
aws_access_key_id=ocredentials["AccessKeyId"],
|
@@ -5080,8 +5082,12 @@ def find_ssm_parameters2(ocredentials):
|
|
5080
5082
|
region = ocredentials["Region"]
|
5081
5083
|
logging.info(f"Finding ssm parameters for account {ocredentials['AccountNumber']} in Region {region}")
|
5082
5084
|
# Handle both profile-based and credential-based authentication
|
5085
|
+
# Priority: Profile > ParentProfile > AccessKeyId > default credential chain
|
5083
5086
|
if "Profile" in ocredentials and ocredentials["Profile"]:
|
5084
5087
|
session_ssm = boto3.Session(profile_name=ocredentials["Profile"], region_name=region)
|
5088
|
+
elif "ParentProfile" in ocredentials and ocredentials["ParentProfile"]:
|
5089
|
+
# Use ParentProfile from get_all_credentials output
|
5090
|
+
session_ssm = boto3.Session(profile_name=ocredentials["ParentProfile"], region_name=region)
|
5085
5091
|
elif "AccessKeyId" in ocredentials and ocredentials["AccessKeyId"]:
|
5086
5092
|
session_ssm = boto3.Session(
|
5087
5093
|
aws_access_key_id=ocredentials["AccessKeyId"],
|
@@ -5345,7 +5351,6 @@ def display_results(
|
|
5345
5351
|
|
5346
5352
|
from runbooks.common.rich_utils import console
|
5347
5353
|
|
5348
|
-
init()
|
5349
5354
|
"""
|
5350
5355
|
Note that this function simply formats the output of the data within the list provided
|
5351
5356
|
@param: results_list: This should be a list of dictionaries, matching to the fields in fdisplay_dict
|
@@ -5760,8 +5765,6 @@ def get_all_credentials(
|
|
5760
5765
|
|
5761
5766
|
# from time import time
|
5762
5767
|
from runbooks.common.rich_utils import console
|
5763
|
-
|
5764
|
-
init()
|
5765
5768
|
# ERASE_LINE = '\x1b[2K'
|
5766
5769
|
# begin_time = time()
|
5767
5770
|
print(f"[green]Timing is enabled") if fTiming else None
|
@@ -5853,7 +5856,6 @@ def get_credentials_for_accounts_in_org(
|
|
5853
5856
|
from runbooks.common.rich_utils import console
|
5854
5857
|
from runbooks.common.rich_utils import create_progress_bar
|
5855
5858
|
|
5856
|
-
init()
|
5857
5859
|
begin_time = time()
|
5858
5860
|
|
5859
5861
|
class AssembleCredentials(Thread):
|
@@ -2,12 +2,19 @@
|
|
2
2
|
"""
|
3
3
|
AWS CloudFormation Stack Inventory Collection
|
4
4
|
|
5
|
-
A comprehensive CloudFormation stack discovery and management tool for multi-account
|
6
|
-
AWS Organizations. Provides detailed stack inventory with advanced filtering capabilities
|
5
|
+
A comprehensive CloudFormation stack discovery and management tool for multi-account
|
6
|
+
AWS Organizations. Provides detailed stack inventory with advanced filtering capabilities
|
7
7
|
and optional stack deletion functionality.
|
8
8
|
|
9
9
|
**AWS API Mapping**: `cloudformation.describe_stacks()`, `cloudformation.list_stacks()`
|
10
10
|
|
11
|
+
.. TODO v1.1.11: Performance optimization for large-scale CloudFormation stack discovery
|
12
|
+
- Current: Timeouts at 540s for large AWS Organizations with 100+ accounts
|
13
|
+
- Root Cause: Sequential API calls with pagination bottlenecks
|
14
|
+
- Improvement: Implement concurrent pagination (40-80% speedup expected)
|
15
|
+
- Target: Complete discovery in <180s for 100+ accounts across 16 regions
|
16
|
+
- Reference: FinOps proven pattern (docs/development/finops-performance-optimization.md)
|
17
|
+
|
11
18
|
Features:
|
12
19
|
- Multi-account CloudFormation stack discovery
|
13
20
|
- Advanced status filtering (CREATE_COMPLETE, UPDATE_FAILED, etc.)
|
@@ -63,15 +70,19 @@ from os.path import split
|
|
63
70
|
from pprint import pprint
|
64
71
|
from time import time
|
65
72
|
|
66
|
-
import Inventory_Modules
|
67
|
-
from account_class import aws_acct_access
|
68
|
-
from ArgumentsClass import CommonArguments
|
73
|
+
from runbooks.inventory import inventory_modules as Inventory_Modules
|
74
|
+
from runbooks.inventory.account_class import aws_acct_access
|
75
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
69
76
|
from botocore.exceptions import ClientError
|
70
77
|
from runbooks.common.rich_utils import console
|
71
|
-
from
|
78
|
+
from runbooks.inventory.inventory_modules import display_results, get_all_credentials
|
79
|
+
from runbooks import __version__
|
80
|
+
|
81
|
+
|
72
82
|
|
73
83
|
|
74
|
-
|
84
|
+
# Terminal control constants
|
85
|
+
ERASE_LINE = '\x1b[2K'
|
75
86
|
|
76
87
|
###########################
|
77
88
|
|
@@ -529,7 +540,6 @@ if __name__ == "__main__":
|
|
529
540
|
begin_time = time()
|
530
541
|
|
531
542
|
##########################
|
532
|
-
ERASE_LINE = "\x1b[2K"
|
533
543
|
|
534
544
|
print()
|
535
545
|
# Setup the aws_acct object
|
@@ -75,10 +75,10 @@ Output:
|
|
75
75
|
import logging
|
76
76
|
import re
|
77
77
|
|
78
|
-
from ArgumentsClass import CommonArguments
|
78
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
79
79
|
from runbooks.common.rich_utils import console
|
80
|
+
from runbooks import __version__
|
80
81
|
|
81
|
-
__version__ = "2024.06.20"
|
82
82
|
|
83
83
|
# Configure CLI argument parsing for StackSet results analysis and correlation
|
84
84
|
parser = CommonArguments()
|
@@ -73,17 +73,21 @@ from queue import Queue
|
|
73
73
|
from threading import Thread
|
74
74
|
from time import time
|
75
75
|
|
76
|
-
import Inventory_Modules
|
77
|
-
from account_class import aws_acct_access
|
78
|
-
from ArgumentsClass import CommonArguments
|
76
|
+
from runbooks.inventory import inventory_modules as Inventory_Modules
|
77
|
+
from runbooks.inventory.account_class import aws_acct_access
|
78
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
79
79
|
from botocore.exceptions import ClientError
|
80
80
|
from runbooks.common.rich_utils import console
|
81
|
-
from
|
81
|
+
from runbooks.inventory.inventory_modules import display_results, find_stacksets3, get_regions3
|
82
|
+
from runbooks import __version__
|
83
|
+
|
82
84
|
# Migrated to Rich.Progress - see rich_utils.py for enterprise UX standards
|
83
85
|
# from tqdm.auto import tqdm
|
84
86
|
|
85
87
|
|
86
|
-
|
88
|
+
|
89
|
+
# Terminal control constants
|
90
|
+
ERASE_LINE = '\x1b[2K'
|
87
91
|
begin_time = time()
|
88
92
|
DefaultMaxWorkerThreads = 5
|
89
93
|
|
@@ -679,21 +683,29 @@ def find_last_operations(faws_acct: aws_acct_access, fStackSetNames: list):
|
|
679
683
|
)
|
680
684
|
|
681
685
|
for stacksetname in fStackSetNames:
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
686
|
+
try:
|
687
|
+
# Retrieve most recent operation for the current StackSet
|
688
|
+
StackSetOps = StackSetOps_client.list_stack_set_operations(
|
689
|
+
StackSetName=stacksetname, MaxResults=1, CallAs="SELF"
|
690
|
+
)["Summaries"]
|
691
|
+
|
692
|
+
# Extract and aggregate operation metadata for analysis
|
693
|
+
if StackSetOps: # Only process if operations exist
|
694
|
+
AllStackSetOps.append(
|
695
|
+
{
|
696
|
+
"StackSetName": stacksetname, # StackSet identifier for correlation
|
697
|
+
"Operation": StackSetOps[0]["Action"], # Operation type for lifecycle tracking
|
698
|
+
"LatestStatus": StackSetOps[0]["Status"], # Current operation status
|
699
|
+
"LatestDate": StackSetOps[0]["EndTimestamp"], # Completion timestamp
|
700
|
+
"Details": StackSetOps[0]["StatusDetails"]["FailedStackInstancesCount"], # Failure count for analysis
|
701
|
+
}
|
702
|
+
)
|
703
|
+
except StackSetOps_client.exceptions.StackSetNotFoundException:
|
704
|
+
logging.warning(f"StackSet {stacksetname} not found or inaccessible - skipping")
|
705
|
+
continue # Skip this StackSet, continue with others
|
706
|
+
except ClientError as e:
|
707
|
+
logging.error(f"Error querying StackSet {stacksetname}: {e}")
|
708
|
+
continue
|
697
709
|
|
698
710
|
# Update progress after processing each StackSet
|
699
711
|
progress.update(task, advance=1)
|
@@ -86,10 +86,10 @@ import sys
|
|
86
86
|
from os.path import split
|
87
87
|
from time import time
|
88
88
|
|
89
|
-
from account_class import aws_acct_access
|
90
|
-
from ArgumentsClass import CommonArguments
|
89
|
+
from runbooks.inventory.account_class import aws_acct_access
|
90
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
91
91
|
from runbooks.common.rich_utils import console
|
92
|
-
from
|
92
|
+
from runbooks.inventory.inventory_modules import (
|
93
93
|
RemoveCoreAccounts,
|
94
94
|
display_results,
|
95
95
|
find_stack_instances2,
|
@@ -97,9 +97,12 @@ from Inventory_Modules import (
|
|
97
97
|
get_credentials_for_accounts_in_org,
|
98
98
|
get_regions3,
|
99
99
|
)
|
100
|
+
from runbooks import __version__
|
101
|
+
|
102
|
+
# Terminal control constants
|
103
|
+
ERASE_LINE = '\x1b[2K'
|
100
104
|
|
101
105
|
|
102
|
-
__version__ = "2024.06.20"
|
103
106
|
begin_time = time()
|
104
107
|
|
105
108
|
#####################
|
@@ -90,15 +90,15 @@ from queue import Queue
|
|
90
90
|
from threading import Thread
|
91
91
|
from time import time
|
92
92
|
|
93
|
-
import Inventory_Modules
|
94
|
-
from ArgumentsClass import CommonArguments
|
93
|
+
from runbooks.inventory import inventory_modules as Inventory_Modules
|
94
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
95
95
|
from botocore.exceptions import ClientError
|
96
96
|
from runbooks.common.rich_utils import console
|
97
|
-
from
|
97
|
+
from runbooks.inventory.inventory_modules import del_config_recorder_or_delivery_channel2, display_results, get_all_credentials
|
98
|
+
from runbooks import __version__
|
98
99
|
# Migrated to Rich.Progress - see rich_utils.py for enterprise UX standards
|
99
100
|
# from tqdm.auto import tqdm
|
100
101
|
|
101
|
-
__version__ = "2024.05.31"
|
102
102
|
|
103
103
|
|
104
104
|
##################
|
@@ -58,14 +58,14 @@ import logging
|
|
58
58
|
import sys
|
59
59
|
from time import time
|
60
60
|
|
61
|
-
from ArgumentsClass import CommonArguments
|
61
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
62
62
|
from botocore.exceptions import ClientError
|
63
63
|
from runbooks.common.rich_utils import console
|
64
|
-
from
|
64
|
+
from runbooks.inventory.inventory_modules import display_results, find_directories2, get_all_credentials
|
65
|
+
from runbooks import __version__
|
65
66
|
# Migrated to Rich.Progress - see rich_utils.py for enterprise UX standards
|
66
67
|
# from tqdm.auto import tqdm
|
67
68
|
|
68
|
-
__version__ = "2024.05.31"
|
69
69
|
|
70
70
|
|
71
71
|
def parse_args(f_arguments):
|
@@ -66,11 +66,15 @@ import logging
|
|
66
66
|
import sys
|
67
67
|
from time import time
|
68
68
|
|
69
|
-
from ArgumentsClass import CommonArguments
|
69
|
+
from runbooks.inventory.ArgumentsClass import CommonArguments
|
70
70
|
from runbooks.common.rich_utils import console
|
71
|
-
from
|
71
|
+
from runbooks.inventory.inventory_modules import display_results, get_all_credentials, get_region_azs2
|
72
|
+
from runbooks import __version__
|
72
73
|
|
73
|
-
|
74
|
+
|
75
|
+
|
76
|
+
# Terminal control constants
|
77
|
+
ERASE_LINE = '\x1b[2K'
|
74
78
|
begin_time = time()
|
75
79
|
|
76
80
|
|