runbooks 0.7.6__py3-none-any.whl → 0.7.9__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/base.py +5 -1
- runbooks/cfat/__init__.py +8 -4
- runbooks/cfat/assessment/collectors.py +171 -14
- runbooks/cfat/assessment/compliance.py +871 -0
- runbooks/cfat/assessment/runner.py +122 -11
- runbooks/cfat/models.py +6 -2
- runbooks/common/logger.py +14 -0
- runbooks/common/rich_utils.py +451 -0
- runbooks/enterprise/__init__.py +68 -0
- runbooks/enterprise/error_handling.py +411 -0
- runbooks/enterprise/logging.py +439 -0
- runbooks/enterprise/multi_tenant.py +583 -0
- runbooks/finops/README.md +468 -241
- runbooks/finops/__init__.py +39 -3
- runbooks/finops/cli.py +83 -18
- runbooks/finops/cross_validation.py +375 -0
- runbooks/finops/dashboard_runner.py +812 -164
- runbooks/finops/enhanced_dashboard_runner.py +525 -0
- runbooks/finops/finops_dashboard.py +1892 -0
- runbooks/finops/helpers.py +485 -51
- runbooks/finops/optimizer.py +823 -0
- runbooks/finops/tests/__init__.py +19 -0
- runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
- runbooks/finops/tests/run_comprehensive_tests.py +421 -0
- runbooks/finops/tests/run_tests.py +305 -0
- runbooks/finops/tests/test_finops_dashboard.py +705 -0
- runbooks/finops/tests/test_integration.py +477 -0
- runbooks/finops/tests/test_performance.py +380 -0
- runbooks/finops/tests/test_performance_benchmarks.py +500 -0
- runbooks/finops/tests/test_reference_images_validation.py +867 -0
- runbooks/finops/tests/test_single_account_features.py +715 -0
- runbooks/finops/tests/validate_test_suite.py +220 -0
- runbooks/finops/types.py +1 -1
- runbooks/hitl/enhanced_workflow_engine.py +725 -0
- runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
- runbooks/inventory/collectors/aws_comprehensive.py +442 -0
- runbooks/inventory/collectors/enterprise_scale.py +281 -0
- runbooks/inventory/core/collector.py +172 -13
- runbooks/inventory/discovery.md +1 -1
- runbooks/inventory/list_ec2_instances.py +18 -20
- runbooks/inventory/list_ssm_parameters.py +31 -3
- runbooks/inventory/organizations_discovery.py +1269 -0
- runbooks/inventory/rich_inventory_display.py +393 -0
- runbooks/inventory/run_on_multi_accounts.py +35 -19
- runbooks/inventory/runbooks.security.report_generator.log +0 -0
- runbooks/inventory/runbooks.security.run_script.log +0 -0
- runbooks/inventory/vpc_flow_analyzer.py +1030 -0
- runbooks/main.py +2215 -119
- runbooks/metrics/dora_metrics_engine.py +599 -0
- runbooks/operate/__init__.py +2 -2
- runbooks/operate/base.py +122 -10
- runbooks/operate/deployment_framework.py +1032 -0
- runbooks/operate/deployment_validator.py +853 -0
- runbooks/operate/dynamodb_operations.py +10 -6
- runbooks/operate/ec2_operations.py +319 -11
- runbooks/operate/executive_dashboard.py +779 -0
- runbooks/operate/mcp_integration.py +750 -0
- runbooks/operate/nat_gateway_operations.py +1120 -0
- runbooks/operate/networking_cost_heatmap.py +685 -0
- runbooks/operate/privatelink_operations.py +940 -0
- runbooks/operate/s3_operations.py +10 -6
- runbooks/operate/vpc_endpoints.py +644 -0
- runbooks/operate/vpc_operations.py +1038 -0
- runbooks/remediation/__init__.py +2 -2
- runbooks/remediation/acm_remediation.py +1 -1
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/cloudtrail_remediation.py +1 -1
- runbooks/remediation/cognito_remediation.py +1 -1
- runbooks/remediation/dynamodb_remediation.py +1 -1
- runbooks/remediation/ec2_remediation.py +1 -1
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
- runbooks/remediation/kms_enable_key_rotation.py +1 -1
- runbooks/remediation/kms_remediation.py +1 -1
- runbooks/remediation/lambda_remediation.py +1 -1
- runbooks/remediation/multi_account.py +1 -1
- runbooks/remediation/rds_remediation.py +1 -1
- runbooks/remediation/s3_block_public_access.py +1 -1
- runbooks/remediation/s3_enable_access_logging.py +1 -1
- runbooks/remediation/s3_encryption.py +1 -1
- runbooks/remediation/s3_remediation.py +1 -1
- runbooks/remediation/vpc_remediation.py +475 -0
- runbooks/security/__init__.py +3 -1
- runbooks/security/compliance_automation.py +632 -0
- runbooks/security/report_generator.py +10 -0
- runbooks/security/run_script.py +31 -5
- runbooks/security/security_baseline_tester.py +169 -30
- runbooks/security/security_export.py +477 -0
- runbooks/validation/__init__.py +10 -0
- runbooks/validation/benchmark.py +484 -0
- runbooks/validation/cli.py +356 -0
- runbooks/validation/mcp_validator.py +768 -0
- runbooks/vpc/__init__.py +38 -0
- runbooks/vpc/config.py +212 -0
- runbooks/vpc/cost_engine.py +347 -0
- runbooks/vpc/heatmap_engine.py +605 -0
- runbooks/vpc/manager_interface.py +634 -0
- runbooks/vpc/networking_wrapper.py +1260 -0
- runbooks/vpc/rich_formatters.py +679 -0
- runbooks/vpc/tests/__init__.py +5 -0
- runbooks/vpc/tests/conftest.py +356 -0
- runbooks/vpc/tests/test_cli_integration.py +530 -0
- runbooks/vpc/tests/test_config.py +458 -0
- runbooks/vpc/tests/test_cost_engine.py +479 -0
- runbooks/vpc/tests/test_networking_wrapper.py +512 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/METADATA +40 -12
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/RECORD +111 -50
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/WHEEL +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,393 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Rich Inventory Display - Enhanced inventory presentation with Rich library
|
4
|
+
|
5
|
+
This module provides enterprise-grade inventory display functionality using
|
6
|
+
the Rich library for beautiful, consistent CLI output that works in both
|
7
|
+
terminal and Jupyter environments.
|
8
|
+
|
9
|
+
Features:
|
10
|
+
- Rich progress bars for long-running operations
|
11
|
+
- Professional table formatting for results
|
12
|
+
- Status indicators and color coding
|
13
|
+
- Performance timing with visual feedback
|
14
|
+
- Consistent branding with CloudOps theme
|
15
|
+
|
16
|
+
Author: CloudOps Runbooks Team
|
17
|
+
Version: 0.7.8
|
18
|
+
"""
|
19
|
+
|
20
|
+
from datetime import datetime
|
21
|
+
from typing import Any, Dict, List, Optional
|
22
|
+
|
23
|
+
from rich.console import Console
|
24
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
|
25
|
+
from rich.table import Table
|
26
|
+
from rich import box
|
27
|
+
from rich.panel import Panel
|
28
|
+
from rich.text import Text
|
29
|
+
from rich.tree import Tree
|
30
|
+
|
31
|
+
from runbooks.common.rich_utils import (
|
32
|
+
console,
|
33
|
+
create_table,
|
34
|
+
create_progress_bar,
|
35
|
+
print_status,
|
36
|
+
print_success,
|
37
|
+
print_info,
|
38
|
+
create_panel,
|
39
|
+
STATUS_INDICATORS
|
40
|
+
)
|
41
|
+
|
42
|
+
|
43
|
+
def display_inventory_header(operation: str, profile: str, accounts: int, regions: int) -> None:
|
44
|
+
"""
|
45
|
+
Display inventory operation header with operation context.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
operation: Type of inventory operation (EC2, RDS, S3, etc.)
|
49
|
+
profile: AWS profile being used
|
50
|
+
accounts: Number of accounts to scan
|
51
|
+
regions: Number of regions to scan
|
52
|
+
"""
|
53
|
+
header_text = f"""
|
54
|
+
[bold cyan]🔍 AWS {operation} Inventory Discovery[/bold cyan]
|
55
|
+
|
56
|
+
[yellow]Profile:[/yellow] {profile}
|
57
|
+
[yellow]Scope:[/yellow] {accounts} accounts × {regions} regions = {accounts * regions} total operations
|
58
|
+
[yellow]Started:[/yellow] {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
59
|
+
"""
|
60
|
+
|
61
|
+
console.print(create_panel(
|
62
|
+
header_text.strip(),
|
63
|
+
title="📊 Inventory Operation",
|
64
|
+
border_style="cyan"
|
65
|
+
))
|
66
|
+
|
67
|
+
|
68
|
+
def create_inventory_progress(total_operations: int, operation_name: str = "Scanning Resources") -> Progress:
|
69
|
+
"""
|
70
|
+
Create a Rich progress bar for inventory operations.
|
71
|
+
|
72
|
+
Args:
|
73
|
+
total_operations: Total number of operations to perform
|
74
|
+
operation_name: Name of the operation being performed
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
Progress instance for tracking
|
78
|
+
"""
|
79
|
+
return Progress(
|
80
|
+
SpinnerColumn(spinner_name="dots", style="cyan"),
|
81
|
+
TextColumn("[progress.description]{task.description}"),
|
82
|
+
BarColumn(bar_width=40, style="cyan", complete_style="green"),
|
83
|
+
TaskProgressColumn(),
|
84
|
+
TextColumn("•"),
|
85
|
+
TextColumn("[blue]{task.completed}/{task.total} operations"),
|
86
|
+
console=console,
|
87
|
+
transient=False
|
88
|
+
)
|
89
|
+
|
90
|
+
|
91
|
+
def display_ec2_inventory_results(instances: List[Dict[str, Any]],
|
92
|
+
accounts: int,
|
93
|
+
regions: int,
|
94
|
+
timing_info: Optional[Dict] = None) -> None:
|
95
|
+
"""
|
96
|
+
Display EC2 inventory results in a professional Rich table format.
|
97
|
+
|
98
|
+
Args:
|
99
|
+
instances: List of EC2 instance data
|
100
|
+
accounts: Number of accounts scanned
|
101
|
+
regions: Number of regions scanned
|
102
|
+
timing_info: Optional timing information
|
103
|
+
"""
|
104
|
+
# Summary panel first
|
105
|
+
total_instances = len(instances)
|
106
|
+
|
107
|
+
# Count by state
|
108
|
+
state_counts = {}
|
109
|
+
for instance in instances:
|
110
|
+
state = instance.get("State", {}).get("Name", "unknown")
|
111
|
+
state_counts[state] = state_counts.get(state, 0) + 1
|
112
|
+
|
113
|
+
# Create status breakdown
|
114
|
+
status_text = ""
|
115
|
+
for state, count in sorted(state_counts.items()):
|
116
|
+
status_indicator = {
|
117
|
+
"running": "🟢",
|
118
|
+
"stopped": "🔴",
|
119
|
+
"stopping": "🟡",
|
120
|
+
"starting": "🟡",
|
121
|
+
"terminated": "⚫",
|
122
|
+
"terminating": "🟡"
|
123
|
+
}.get(state, "⚪")
|
124
|
+
|
125
|
+
status_text += f"{status_indicator} {state.title()}: {count}\n"
|
126
|
+
|
127
|
+
summary_content = f"""
|
128
|
+
[bold cyan]EC2 Inventory Summary[/bold cyan]
|
129
|
+
|
130
|
+
[green]Total Instances Found:[/green] {total_instances}
|
131
|
+
[green]Accounts Scanned:[/green] {accounts}
|
132
|
+
[green]Regions Scanned:[/green] {regions}
|
133
|
+
|
134
|
+
[bold yellow]Instance States:[/bold yellow]
|
135
|
+
{status_text.strip()}
|
136
|
+
"""
|
137
|
+
|
138
|
+
console.print(create_panel(
|
139
|
+
summary_content.strip(),
|
140
|
+
title="📊 Discovery Results",
|
141
|
+
border_style="green"
|
142
|
+
))
|
143
|
+
|
144
|
+
# Detailed results table if instances found
|
145
|
+
if instances:
|
146
|
+
# Group instances by account for better organization
|
147
|
+
instances_by_account = {}
|
148
|
+
for instance in instances:
|
149
|
+
account_id = instance.get("AccountId", "Unknown")
|
150
|
+
if account_id not in instances_by_account:
|
151
|
+
instances_by_account[account_id] = []
|
152
|
+
instances_by_account[account_id].append(instance)
|
153
|
+
|
154
|
+
# Create detailed table
|
155
|
+
table = create_table(
|
156
|
+
title="🖥️ EC2 Instance Details",
|
157
|
+
columns=[
|
158
|
+
{"name": "Account", "style": "cyan"},
|
159
|
+
{"name": "Region", "style": "yellow"},
|
160
|
+
{"name": "Instance ID", "style": "magenta"},
|
161
|
+
{"name": "Type", "style": "blue"},
|
162
|
+
{"name": "State", "style": "green"},
|
163
|
+
{"name": "Name", "style": "white"}
|
164
|
+
],
|
165
|
+
box_style=box.ROUNDED
|
166
|
+
)
|
167
|
+
|
168
|
+
# Add rows (show first 50 instances to avoid overwhelming output)
|
169
|
+
displayed_count = 0
|
170
|
+
for account_id in sorted(instances_by_account.keys()):
|
171
|
+
account_instances = instances_by_account[account_id][:10] # Max 10 per account
|
172
|
+
|
173
|
+
for instance in account_instances:
|
174
|
+
if displayed_count >= 50: # Overall limit
|
175
|
+
break
|
176
|
+
|
177
|
+
# Extract instance information
|
178
|
+
instance_id = instance.get("InstanceId", "N/A")
|
179
|
+
instance_type = instance.get("InstanceType", "N/A")
|
180
|
+
state = instance.get("State", {}).get("Name", "unknown")
|
181
|
+
region = instance.get("Region", "N/A")
|
182
|
+
|
183
|
+
# Get instance name from tags
|
184
|
+
instance_name = "N/A"
|
185
|
+
tags = instance.get("Tags", [])
|
186
|
+
for tag in tags:
|
187
|
+
if tag.get("Key") == "Name":
|
188
|
+
instance_name = tag.get("Value", "N/A")
|
189
|
+
break
|
190
|
+
|
191
|
+
# Style state with appropriate color
|
192
|
+
state_styled = {
|
193
|
+
"running": "[green]🟢 Running[/green]",
|
194
|
+
"stopped": "[red]🔴 Stopped[/red]",
|
195
|
+
"stopping": "[yellow]🟡 Stopping[/yellow]",
|
196
|
+
"starting": "[yellow]🟡 Starting[/yellow]",
|
197
|
+
"terminated": "[dim]⚫ Terminated[/dim]",
|
198
|
+
"terminating": "[yellow]🟡 Terminating[/yellow]"
|
199
|
+
}.get(state, f"[white]⚪ {state.title()}[/white]")
|
200
|
+
|
201
|
+
table.add_row(
|
202
|
+
account_id[:12], # Truncate account ID
|
203
|
+
region,
|
204
|
+
instance_id,
|
205
|
+
instance_type,
|
206
|
+
state_styled,
|
207
|
+
instance_name[:20] if instance_name != "N/A" else "N/A" # Truncate long names
|
208
|
+
)
|
209
|
+
|
210
|
+
displayed_count += 1
|
211
|
+
|
212
|
+
console.print(table)
|
213
|
+
|
214
|
+
# Show truncation message if needed
|
215
|
+
if total_instances > 50:
|
216
|
+
console.print(f"\n[dim]Showing first 50 instances. Total found: {total_instances}[/dim]")
|
217
|
+
|
218
|
+
# Timing information
|
219
|
+
if timing_info:
|
220
|
+
execution_time = timing_info.get("total_time", 0)
|
221
|
+
print_success(f"✅ Inventory scan completed in {execution_time:.2f} seconds")
|
222
|
+
|
223
|
+
print_info("💡 Use --output json or --output csv to export complete results")
|
224
|
+
|
225
|
+
|
226
|
+
def display_generic_inventory_results(resource_type: str,
|
227
|
+
resources: List[Dict[str, Any]],
|
228
|
+
accounts: int,
|
229
|
+
regions: int) -> None:
|
230
|
+
"""
|
231
|
+
Display generic inventory results for any resource type.
|
232
|
+
|
233
|
+
Args:
|
234
|
+
resource_type: Type of AWS resource (RDS, S3, Lambda, etc.)
|
235
|
+
resources: List of resource data
|
236
|
+
accounts: Number of accounts scanned
|
237
|
+
regions: Number of regions scanned
|
238
|
+
"""
|
239
|
+
total_resources = len(resources)
|
240
|
+
|
241
|
+
# Resource type icons
|
242
|
+
resource_icons = {
|
243
|
+
"rds": "🗄️",
|
244
|
+
"s3": "🪣",
|
245
|
+
"lambda": "⚡",
|
246
|
+
"vpc": "🌐",
|
247
|
+
"iam": "👤",
|
248
|
+
"cloudformation": "📚",
|
249
|
+
"ssm": "🔑",
|
250
|
+
"route53": "🌍"
|
251
|
+
}
|
252
|
+
|
253
|
+
icon = resource_icons.get(resource_type.lower(), "📦")
|
254
|
+
|
255
|
+
summary_content = f"""
|
256
|
+
[bold cyan]{resource_type.upper()} Inventory Summary[/bold cyan]
|
257
|
+
|
258
|
+
[green]Total Resources Found:[/green] {total_resources}
|
259
|
+
[green]Accounts Scanned:[/green] {accounts}
|
260
|
+
[green]Regions Scanned:[/green] {regions}
|
261
|
+
"""
|
262
|
+
|
263
|
+
console.print(create_panel(
|
264
|
+
summary_content.strip(),
|
265
|
+
title=f"{icon} Discovery Results",
|
266
|
+
border_style="green"
|
267
|
+
))
|
268
|
+
|
269
|
+
if total_resources > 0:
|
270
|
+
print_success(f"✅ Found {total_resources} {resource_type} resources across {accounts} accounts")
|
271
|
+
else:
|
272
|
+
print_info(f"ℹ️ No {resource_type} resources found in the specified scope")
|
273
|
+
|
274
|
+
|
275
|
+
def display_inventory_error(error_message: str, suggestions: Optional[List[str]] = None) -> None:
|
276
|
+
"""
|
277
|
+
Display inventory operation error with helpful suggestions.
|
278
|
+
|
279
|
+
Args:
|
280
|
+
error_message: Error message to display
|
281
|
+
suggestions: Optional list of suggestions for resolution
|
282
|
+
"""
|
283
|
+
error_content = f"[bold red]❌ Inventory Operation Failed[/bold red]\n\n{error_message}"
|
284
|
+
|
285
|
+
if suggestions:
|
286
|
+
error_content += "\n\n[yellow]💡 Suggestions:[/yellow]\n"
|
287
|
+
for suggestion in suggestions:
|
288
|
+
error_content += f" • {suggestion}\n"
|
289
|
+
|
290
|
+
console.print(create_panel(
|
291
|
+
error_content.strip(),
|
292
|
+
title="🚨 Error",
|
293
|
+
border_style="red",
|
294
|
+
padding=1
|
295
|
+
))
|
296
|
+
|
297
|
+
|
298
|
+
def display_multi_resource_summary(resource_counts: Dict[str, int],
|
299
|
+
accounts: int,
|
300
|
+
regions: int,
|
301
|
+
execution_time: float) -> None:
|
302
|
+
"""
|
303
|
+
Display summary for multi-resource inventory operations.
|
304
|
+
|
305
|
+
Args:
|
306
|
+
resource_counts: Dictionary of resource type to count
|
307
|
+
accounts: Number of accounts scanned
|
308
|
+
regions: Number of regions scanned
|
309
|
+
execution_time: Total execution time in seconds
|
310
|
+
"""
|
311
|
+
# Create summary table
|
312
|
+
table = create_table(
|
313
|
+
title="📊 Multi-Resource Inventory Summary",
|
314
|
+
columns=[
|
315
|
+
{"name": "Resource Type", "style": "cyan"},
|
316
|
+
{"name": "Count", "style": "green", "justify": "right"},
|
317
|
+
{"name": "Status", "style": "yellow"}
|
318
|
+
]
|
319
|
+
)
|
320
|
+
|
321
|
+
total_resources = 0
|
322
|
+
for resource_type, count in sorted(resource_counts.items()):
|
323
|
+
status = "✅ Found" if count > 0 else "⚪ None"
|
324
|
+
table.add_row(
|
325
|
+
resource_type.title(),
|
326
|
+
str(count),
|
327
|
+
status
|
328
|
+
)
|
329
|
+
total_resources += count
|
330
|
+
|
331
|
+
console.print(table)
|
332
|
+
|
333
|
+
# Overall summary
|
334
|
+
summary_text = f"""
|
335
|
+
[bold green]Overall Summary[/bold green]
|
336
|
+
|
337
|
+
[cyan]Total Resources:[/cyan] {total_resources}
|
338
|
+
[cyan]Accounts Scanned:[/cyan] {accounts}
|
339
|
+
[cyan]Regions Scanned:[/cyan] {regions}
|
340
|
+
[cyan]Execution Time:[/cyan] {execution_time:.2f} seconds
|
341
|
+
[cyan]Performance:[/cyan] {(accounts * regions) / execution_time:.1f} operations/second
|
342
|
+
"""
|
343
|
+
|
344
|
+
console.print(create_panel(
|
345
|
+
summary_text.strip(),
|
346
|
+
title="🎯 Inventory Complete",
|
347
|
+
border_style="bright_blue"
|
348
|
+
))
|
349
|
+
|
350
|
+
|
351
|
+
def display_account_tree(accounts_data: Dict[str, Dict]) -> None:
|
352
|
+
"""
|
353
|
+
Display account and resource hierarchy as a Rich tree.
|
354
|
+
|
355
|
+
Args:
|
356
|
+
accounts_data: Nested dictionary of account -> resource data
|
357
|
+
"""
|
358
|
+
tree = Tree("🏢 [bold cyan]AWS Organization Structure[/bold cyan]")
|
359
|
+
|
360
|
+
for account_id, account_data in accounts_data.items():
|
361
|
+
account_name = account_data.get("account_name", "Unknown")
|
362
|
+
account_branch = tree.add(f"📊 [yellow]Account: {account_id}[/yellow] ({account_name})")
|
363
|
+
|
364
|
+
# Add regions
|
365
|
+
regions = account_data.get("regions", {})
|
366
|
+
for region, region_data in regions.items():
|
367
|
+
region_branch = account_branch.add(f"🌍 [green]Region: {region}[/green]")
|
368
|
+
|
369
|
+
# Add resource counts
|
370
|
+
for resource_type, count in region_data.get("resource_counts", {}).items():
|
371
|
+
if count > 0:
|
372
|
+
icon = {
|
373
|
+
"ec2": "🖥️",
|
374
|
+
"rds": "🗄️",
|
375
|
+
"s3": "🪣",
|
376
|
+
"lambda": "⚡"
|
377
|
+
}.get(resource_type, "📦")
|
378
|
+
|
379
|
+
region_branch.add(f"{icon} {resource_type.upper()}: [bold]{count}[/bold] resources")
|
380
|
+
|
381
|
+
console.print(tree)
|
382
|
+
|
383
|
+
|
384
|
+
# Export public functions
|
385
|
+
__all__ = [
|
386
|
+
"display_inventory_header",
|
387
|
+
"create_inventory_progress",
|
388
|
+
"display_ec2_inventory_results",
|
389
|
+
"display_generic_inventory_results",
|
390
|
+
"display_inventory_error",
|
391
|
+
"display_multi_resource_summary",
|
392
|
+
"display_account_tree"
|
393
|
+
]
|
@@ -8,6 +8,11 @@ from account_class import aws_acct_access
|
|
8
8
|
from ArgumentsClass import CommonArguments
|
9
9
|
from botocore.exceptions import ClientError
|
10
10
|
from colorama import Fore, init
|
11
|
+
from rich.console import Console
|
12
|
+
from rich.panel import Panel
|
13
|
+
|
14
|
+
# Initialize Rich console
|
15
|
+
console = Console()
|
11
16
|
|
12
17
|
init()
|
13
18
|
__version__ = "2023.05.04"
|
@@ -57,23 +62,23 @@ def check_account_access(faws_acct, faccount_num, fAccessRole=None):
|
|
57
62
|
return_response = {"Credentials": credentials, "Success": True, "ErrorMessage": ""}
|
58
63
|
return return_response
|
59
64
|
except ClientError as my_Error:
|
60
|
-
print(f"Client Error: {my_Error}")
|
65
|
+
console.print(f"[red]❌ Client Error: {my_Error}[/red]")
|
61
66
|
return_response = {"Success": False, "ErrorMessage": "Client Error"}
|
62
67
|
return return_response
|
63
68
|
except sts_client.exceptions.MalformedPolicyDocumentException as my_Error:
|
64
|
-
print(f"MalformedPolicy: {my_Error}")
|
69
|
+
console.print(f"[red]❌ MalformedPolicy: {my_Error}[/red]")
|
65
70
|
return_response = {"Success": False, "ErrorMessage": "Malformed Policy"}
|
66
71
|
return return_response
|
67
72
|
except sts_client.exceptions.PackedPolicyTooLargeException as my_Error:
|
68
|
-
print(f"Policy is too large: {my_Error}")
|
73
|
+
console.print(f"[red]❌ Policy is too large: {my_Error}[/red]")
|
69
74
|
return_response = {"Success": False, "ErrorMessage": "Policy is too large"}
|
70
75
|
return return_response
|
71
76
|
except sts_client.exceptions.RegionDisabledException as my_Error:
|
72
|
-
print(f"Region is disabled: {my_Error}")
|
77
|
+
console.print(f"[red]❌ Region is disabled: {my_Error}[/red]")
|
73
78
|
return_response = {"Success": False, "ErrorMessage": "Region Disabled"}
|
74
79
|
return return_response
|
75
80
|
except sts_client.exceptions.ExpiredTokenException as my_Error:
|
76
|
-
print(f"Expired Token: {my_Error}")
|
81
|
+
console.print(f"[red]❌ Expired Token: {my_Error}[/red]")
|
77
82
|
return_response = {"Success": False, "ErrorMessage": "Expired Token"}
|
78
83
|
return return_response
|
79
84
|
|
@@ -187,7 +192,7 @@ for account_num in Accounts:
|
|
187
192
|
)
|
188
193
|
response = check_account_access(aws_acct, account_num, pAccessRole)
|
189
194
|
if response["Success"]:
|
190
|
-
print(f"Account {account_num} was successfully connected via role {pAccessRole} from {aws_acct.acct_number}")
|
195
|
+
console.print(f"[green]✅ Account {account_num} was successfully connected via role {pAccessRole} from {aws_acct.acct_number}[/green]")
|
191
196
|
"""
|
192
197
|
Put more commands here... Or you can write functions that represent your commands and call them from here.
|
193
198
|
"""
|
@@ -196,16 +201,27 @@ for account_num in Accounts:
|
|
196
201
|
username = "Paul"
|
197
202
|
user_response = participant_user(tgt_aws_access, username=username)
|
198
203
|
else:
|
199
|
-
print(
|
200
|
-
f"Access Role {pAccessRole} failed to connect to {account_num} from {aws_acct.acct_number}
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
204
|
+
console.print(Panel(
|
205
|
+
f"Access Role {pAccessRole} failed to connect to {account_num} from {aws_acct.acct_number}",
|
206
|
+
title=f"[red]❌ Connection Error: {response['ErrorMessage']}[/red]",
|
207
|
+
border_style="red"
|
208
|
+
))
|
209
|
+
|
210
|
+
# Display access keys with Rich formatting
|
211
|
+
credentials_info = [
|
212
|
+
f"[cyan]Account:[/cyan] {tgt_aws_access.acct_number}",
|
213
|
+
f"[cyan]User:[/cyan] {user_response['User']} (created/confirmed in account {user_response['AccountId']})",
|
214
|
+
f"[cyan]Password:[/cyan] {user_response['Password']}",
|
215
|
+
f"[cyan]Access Key ID:[/cyan] {user_response['AccessKeyId']}",
|
216
|
+
f"[cyan]Secret Access Key:[/cyan] {user_response['SecretAccessKey']}"
|
217
|
+
]
|
218
|
+
|
219
|
+
console.print(Panel(
|
220
|
+
"\n".join(credentials_info),
|
221
|
+
title="[blue]🔑 Account Credentials[/blue]",
|
222
|
+
border_style="blue"
|
223
|
+
))
|
224
|
+
|
225
|
+
console.print("")
|
226
|
+
console.print("[green]✅ Thanks for using this script...[/green]")
|
227
|
+
console.print("")
|
File without changes
|
File without changes
|