runbooks 0.7.9__py3-none-any.whl → 0.9.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.
Files changed (122) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/README.md +12 -1
  3. runbooks/cfat/__init__.py +1 -1
  4. runbooks/cfat/assessment/compliance.py +4 -1
  5. runbooks/cfat/assessment/runner.py +42 -34
  6. runbooks/cfat/models.py +1 -1
  7. runbooks/cloudops/__init__.py +123 -0
  8. runbooks/cloudops/base.py +385 -0
  9. runbooks/cloudops/cost_optimizer.py +811 -0
  10. runbooks/cloudops/infrastructure_optimizer.py +29 -0
  11. runbooks/cloudops/interfaces.py +828 -0
  12. runbooks/cloudops/lifecycle_manager.py +29 -0
  13. runbooks/cloudops/mcp_cost_validation.py +678 -0
  14. runbooks/cloudops/models.py +251 -0
  15. runbooks/cloudops/monitoring_automation.py +29 -0
  16. runbooks/cloudops/notebook_framework.py +676 -0
  17. runbooks/cloudops/security_enforcer.py +449 -0
  18. runbooks/common/__init__.py +152 -0
  19. runbooks/common/accuracy_validator.py +1039 -0
  20. runbooks/common/context_logger.py +440 -0
  21. runbooks/common/cross_module_integration.py +594 -0
  22. runbooks/common/enhanced_exception_handler.py +1108 -0
  23. runbooks/common/enterprise_audit_integration.py +634 -0
  24. runbooks/common/mcp_cost_explorer_integration.py +900 -0
  25. runbooks/common/mcp_integration.py +548 -0
  26. runbooks/common/performance_monitor.py +387 -0
  27. runbooks/common/profile_utils.py +216 -0
  28. runbooks/common/rich_utils.py +172 -1
  29. runbooks/feedback/user_feedback_collector.py +440 -0
  30. runbooks/finops/README.md +377 -458
  31. runbooks/finops/__init__.py +4 -21
  32. runbooks/finops/account_resolver.py +279 -0
  33. runbooks/finops/accuracy_cross_validator.py +638 -0
  34. runbooks/finops/aws_client.py +721 -36
  35. runbooks/finops/budget_integration.py +313 -0
  36. runbooks/finops/cli.py +59 -5
  37. runbooks/finops/cost_optimizer.py +1340 -0
  38. runbooks/finops/cost_processor.py +211 -37
  39. runbooks/finops/dashboard_router.py +900 -0
  40. runbooks/finops/dashboard_runner.py +990 -232
  41. runbooks/finops/embedded_mcp_validator.py +288 -0
  42. runbooks/finops/enhanced_dashboard_runner.py +8 -7
  43. runbooks/finops/enhanced_progress.py +327 -0
  44. runbooks/finops/enhanced_trend_visualization.py +423 -0
  45. runbooks/finops/finops_dashboard.py +184 -1829
  46. runbooks/finops/helpers.py +509 -196
  47. runbooks/finops/iam_guidance.py +400 -0
  48. runbooks/finops/markdown_exporter.py +466 -0
  49. runbooks/finops/multi_dashboard.py +1502 -0
  50. runbooks/finops/optimizer.py +15 -15
  51. runbooks/finops/profile_processor.py +2 -2
  52. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  53. runbooks/finops/runbooks.security.report_generator.log +0 -0
  54. runbooks/finops/runbooks.security.run_script.log +0 -0
  55. runbooks/finops/runbooks.security.security_export.log +0 -0
  56. runbooks/finops/schemas.py +589 -0
  57. runbooks/finops/service_mapping.py +195 -0
  58. runbooks/finops/single_dashboard.py +710 -0
  59. runbooks/finops/tests/test_reference_images_validation.py +1 -1
  60. runbooks/inventory/README.md +12 -1
  61. runbooks/inventory/core/collector.py +157 -29
  62. runbooks/inventory/list_ec2_instances.py +9 -6
  63. runbooks/inventory/list_ssm_parameters.py +10 -10
  64. runbooks/inventory/organizations_discovery.py +210 -164
  65. runbooks/inventory/rich_inventory_display.py +74 -107
  66. runbooks/inventory/run_on_multi_accounts.py +13 -13
  67. runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
  68. runbooks/inventory/runbooks.security.security_export.log +0 -0
  69. runbooks/main.py +1371 -240
  70. runbooks/metrics/dora_metrics_engine.py +711 -17
  71. runbooks/monitoring/performance_monitor.py +433 -0
  72. runbooks/operate/README.md +394 -0
  73. runbooks/operate/base.py +215 -47
  74. runbooks/operate/ec2_operations.py +435 -5
  75. runbooks/operate/iam_operations.py +598 -3
  76. runbooks/operate/privatelink_operations.py +1 -1
  77. runbooks/operate/rds_operations.py +508 -0
  78. runbooks/operate/s3_operations.py +508 -0
  79. runbooks/operate/vpc_endpoints.py +1 -1
  80. runbooks/remediation/README.md +489 -13
  81. runbooks/remediation/base.py +5 -3
  82. runbooks/remediation/commons.py +8 -4
  83. runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +506 -0
  84. runbooks/security/README.md +12 -1
  85. runbooks/security/__init__.py +265 -33
  86. runbooks/security/cloudops_automation_security_validator.py +1164 -0
  87. runbooks/security/compliance_automation.py +12 -10
  88. runbooks/security/compliance_automation_engine.py +1021 -0
  89. runbooks/security/enterprise_security_framework.py +930 -0
  90. runbooks/security/enterprise_security_policies.json +293 -0
  91. runbooks/security/executive_security_dashboard.py +1247 -0
  92. runbooks/security/integration_test_enterprise_security.py +879 -0
  93. runbooks/security/module_security_integrator.py +641 -0
  94. runbooks/security/multi_account_security_controls.py +2254 -0
  95. runbooks/security/real_time_security_monitor.py +1196 -0
  96. runbooks/security/report_generator.py +1 -1
  97. runbooks/security/run_script.py +4 -8
  98. runbooks/security/security_baseline_tester.py +39 -52
  99. runbooks/security/security_export.py +99 -120
  100. runbooks/sre/README.md +472 -0
  101. runbooks/sre/__init__.py +33 -0
  102. runbooks/sre/mcp_reliability_engine.py +1049 -0
  103. runbooks/sre/performance_optimization_engine.py +1032 -0
  104. runbooks/sre/production_monitoring_framework.py +584 -0
  105. runbooks/sre/reliability_monitoring_framework.py +1011 -0
  106. runbooks/validation/__init__.py +2 -2
  107. runbooks/validation/benchmark.py +154 -149
  108. runbooks/validation/cli.py +159 -147
  109. runbooks/validation/mcp_validator.py +291 -248
  110. runbooks/vpc/README.md +478 -0
  111. runbooks/vpc/__init__.py +2 -2
  112. runbooks/vpc/manager_interface.py +366 -351
  113. runbooks/vpc/networking_wrapper.py +68 -36
  114. runbooks/vpc/rich_formatters.py +22 -8
  115. runbooks-0.9.1.dist-info/METADATA +308 -0
  116. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/RECORD +120 -59
  117. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/entry_points.txt +1 -1
  118. runbooks/finops/cross_validation.py +0 -375
  119. runbooks-0.7.9.dist-info/METADATA +0 -636
  120. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/WHEEL +0 -0
  121. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/licenses/LICENSE +0 -0
  122. {runbooks-0.7.9.dist-info → runbooks-0.9.1.dist-info}/top_level.txt +0 -0
@@ -20,23 +20,23 @@ Version: 0.7.8
20
20
  from datetime import datetime
21
21
  from typing import Any, Dict, List, Optional
22
22
 
23
- from rich.console import Console
24
- from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
25
- from rich.table import Table
26
23
  from rich import box
24
+ from rich.console import Console
27
25
  from rich.panel import Panel
26
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
27
+ from rich.table import Table
28
28
  from rich.text import Text
29
29
  from rich.tree import Tree
30
30
 
31
31
  from runbooks.common.rich_utils import (
32
+ STATUS_INDICATORS,
32
33
  console,
33
- create_table,
34
+ create_panel,
34
35
  create_progress_bar,
36
+ create_table,
37
+ print_info,
35
38
  print_status,
36
39
  print_success,
37
- print_info,
38
- create_panel,
39
- STATUS_INDICATORS
40
40
  )
41
41
 
42
42
 
@@ -55,14 +55,10 @@ def display_inventory_header(operation: str, profile: str, accounts: int, region
55
55
 
56
56
  [yellow]Profile:[/yellow] {profile}
57
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')}
58
+ [yellow]Started:[/yellow] {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
59
59
  """
60
-
61
- console.print(create_panel(
62
- header_text.strip(),
63
- title="📊 Inventory Operation",
64
- border_style="cyan"
65
- ))
60
+
61
+ console.print(create_panel(header_text.strip(), title="📊 Inventory Operation", border_style="cyan"))
66
62
 
67
63
 
68
64
  def create_inventory_progress(total_operations: int, operation_name: str = "Scanning Resources") -> Progress:
@@ -84,14 +80,13 @@ def create_inventory_progress(total_operations: int, operation_name: str = "Scan
84
80
  TextColumn("•"),
85
81
  TextColumn("[blue]{task.completed}/{task.total} operations"),
86
82
  console=console,
87
- transient=False
83
+ transient=False,
88
84
  )
89
85
 
90
86
 
91
- def display_ec2_inventory_results(instances: List[Dict[str, Any]],
92
- accounts: int,
93
- regions: int,
94
- timing_info: Optional[Dict] = None) -> None:
87
+ def display_ec2_inventory_results(
88
+ instances: List[Dict[str, Any]], accounts: int, regions: int, timing_info: Optional[Dict] = None
89
+ ) -> None:
95
90
  """
96
91
  Display EC2 inventory results in a professional Rich table format.
97
92
 
@@ -103,27 +98,27 @@ def display_ec2_inventory_results(instances: List[Dict[str, Any]],
103
98
  """
104
99
  # Summary panel first
105
100
  total_instances = len(instances)
106
-
101
+
107
102
  # Count by state
108
103
  state_counts = {}
109
104
  for instance in instances:
110
105
  state = instance.get("State", {}).get("Name", "unknown")
111
106
  state_counts[state] = state_counts.get(state, 0) + 1
112
-
107
+
113
108
  # Create status breakdown
114
109
  status_text = ""
115
110
  for state, count in sorted(state_counts.items()):
116
111
  status_indicator = {
117
112
  "running": "🟢",
118
- "stopped": "🔴",
113
+ "stopped": "🔴",
119
114
  "stopping": "🟡",
120
115
  "starting": "🟡",
121
116
  "terminated": "⚫",
122
- "terminating": "🟡"
117
+ "terminating": "🟡",
123
118
  }.get(state, "⚪")
124
-
119
+
125
120
  status_text += f"{status_indicator} {state.title()}: {count}\n"
126
-
121
+
127
122
  summary_content = f"""
128
123
  [bold cyan]EC2 Inventory Summary[/bold cyan]
129
124
 
@@ -134,13 +129,9 @@ def display_ec2_inventory_results(instances: List[Dict[str, Any]],
134
129
  [bold yellow]Instance States:[/bold yellow]
135
130
  {status_text.strip()}
136
131
  """
137
-
138
- console.print(create_panel(
139
- summary_content.strip(),
140
- title="📊 Discovery Results",
141
- border_style="green"
142
- ))
143
-
132
+
133
+ console.print(create_panel(summary_content.strip(), title="📊 Discovery Results", border_style="green"))
134
+
144
135
  # Detailed results table if instances found
145
136
  if instances:
146
137
  # Group instances by account for better organization
@@ -150,7 +141,7 @@ def display_ec2_inventory_results(instances: List[Dict[str, Any]],
150
141
  if account_id not in instances_by_account:
151
142
  instances_by_account[account_id] = []
152
143
  instances_by_account[account_id].append(instance)
153
-
144
+
154
145
  # Create detailed table
155
146
  table = create_table(
156
147
  title="🖥️ EC2 Instance Details",
@@ -160,26 +151,26 @@ def display_ec2_inventory_results(instances: List[Dict[str, Any]],
160
151
  {"name": "Instance ID", "style": "magenta"},
161
152
  {"name": "Type", "style": "blue"},
162
153
  {"name": "State", "style": "green"},
163
- {"name": "Name", "style": "white"}
154
+ {"name": "Name", "style": "white"},
164
155
  ],
165
- box_style=box.ROUNDED
156
+ box_style=box.ROUNDED,
166
157
  )
167
-
158
+
168
159
  # Add rows (show first 50 instances to avoid overwhelming output)
169
160
  displayed_count = 0
170
161
  for account_id in sorted(instances_by_account.keys()):
171
162
  account_instances = instances_by_account[account_id][:10] # Max 10 per account
172
-
163
+
173
164
  for instance in account_instances:
174
165
  if displayed_count >= 50: # Overall limit
175
166
  break
176
-
167
+
177
168
  # Extract instance information
178
169
  instance_id = instance.get("InstanceId", "N/A")
179
170
  instance_type = instance.get("InstanceType", "N/A")
180
171
  state = instance.get("State", {}).get("Name", "unknown")
181
172
  region = instance.get("Region", "N/A")
182
-
173
+
183
174
  # Get instance name from tags
184
175
  instance_name = "N/A"
185
176
  tags = instance.get("Tags", [])
@@ -187,7 +178,7 @@ def display_ec2_inventory_results(instances: List[Dict[str, Any]],
187
178
  if tag.get("Key") == "Name":
188
179
  instance_name = tag.get("Value", "N/A")
189
180
  break
190
-
181
+
191
182
  # Style state with appropriate color
192
183
  state_styled = {
193
184
  "running": "[green]🟢 Running[/green]",
@@ -195,38 +186,37 @@ def display_ec2_inventory_results(instances: List[Dict[str, Any]],
195
186
  "stopping": "[yellow]🟡 Stopping[/yellow]",
196
187
  "starting": "[yellow]🟡 Starting[/yellow]",
197
188
  "terminated": "[dim]⚫ Terminated[/dim]",
198
- "terminating": "[yellow]🟡 Terminating[/yellow]"
189
+ "terminating": "[yellow]🟡 Terminating[/yellow]",
199
190
  }.get(state, f"[white]⚪ {state.title()}[/white]")
200
-
191
+
201
192
  table.add_row(
202
193
  account_id[:12], # Truncate account ID
203
194
  region,
204
195
  instance_id,
205
196
  instance_type,
206
197
  state_styled,
207
- instance_name[:20] if instance_name != "N/A" else "N/A" # Truncate long names
198
+ instance_name[:20] if instance_name != "N/A" else "N/A", # Truncate long names
208
199
  )
209
-
200
+
210
201
  displayed_count += 1
211
-
202
+
212
203
  console.print(table)
213
-
204
+
214
205
  # Show truncation message if needed
215
206
  if total_instances > 50:
216
207
  console.print(f"\n[dim]Showing first 50 instances. Total found: {total_instances}[/dim]")
217
-
208
+
218
209
  # Timing information
219
210
  if timing_info:
220
211
  execution_time = timing_info.get("total_time", 0)
221
212
  print_success(f"✅ Inventory scan completed in {execution_time:.2f} seconds")
222
-
213
+
223
214
  print_info("💡 Use --output json or --output csv to export complete results")
224
215
 
225
216
 
226
- def display_generic_inventory_results(resource_type: str,
227
- resources: List[Dict[str, Any]],
228
- accounts: int,
229
- regions: int) -> None:
217
+ def display_generic_inventory_results(
218
+ resource_type: str, resources: List[Dict[str, Any]], accounts: int, regions: int
219
+ ) -> None:
230
220
  """
231
221
  Display generic inventory results for any resource type.
232
222
 
@@ -237,7 +227,7 @@ def display_generic_inventory_results(resource_type: str,
237
227
  regions: Number of regions scanned
238
228
  """
239
229
  total_resources = len(resources)
240
-
230
+
241
231
  # Resource type icons
242
232
  resource_icons = {
243
233
  "rds": "🗄️",
@@ -247,11 +237,11 @@ def display_generic_inventory_results(resource_type: str,
247
237
  "iam": "👤",
248
238
  "cloudformation": "📚",
249
239
  "ssm": "🔑",
250
- "route53": "🌍"
240
+ "route53": "🌍",
251
241
  }
252
-
242
+
253
243
  icon = resource_icons.get(resource_type.lower(), "📦")
254
-
244
+
255
245
  summary_content = f"""
256
246
  [bold cyan]{resource_type.upper()} Inventory Summary[/bold cyan]
257
247
 
@@ -259,13 +249,9 @@ def display_generic_inventory_results(resource_type: str,
259
249
  [green]Accounts Scanned:[/green] {accounts}
260
250
  [green]Regions Scanned:[/green] {regions}
261
251
  """
262
-
263
- console.print(create_panel(
264
- summary_content.strip(),
265
- title=f"{icon} Discovery Results",
266
- border_style="green"
267
- ))
268
-
252
+
253
+ console.print(create_panel(summary_content.strip(), title=f"{icon} Discovery Results", border_style="green"))
254
+
269
255
  if total_resources > 0:
270
256
  print_success(f"✅ Found {total_resources} {resource_type} resources across {accounts} accounts")
271
257
  else:
@@ -281,24 +267,18 @@ def display_inventory_error(error_message: str, suggestions: Optional[List[str]]
281
267
  suggestions: Optional list of suggestions for resolution
282
268
  """
283
269
  error_content = f"[bold red]❌ Inventory Operation Failed[/bold red]\n\n{error_message}"
284
-
270
+
285
271
  if suggestions:
286
272
  error_content += "\n\n[yellow]💡 Suggestions:[/yellow]\n"
287
273
  for suggestion in suggestions:
288
274
  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:
275
+
276
+ console.print(create_panel(error_content.strip(), title="🚨 Error", border_style="red", padding=1))
277
+
278
+
279
+ def display_multi_resource_summary(
280
+ resource_counts: Dict[str, int], accounts: int, regions: int, execution_time: float
281
+ ) -> None:
302
282
  """
303
283
  Display summary for multi-resource inventory operations.
304
284
 
@@ -314,22 +294,18 @@ def display_multi_resource_summary(resource_counts: Dict[str, int],
314
294
  columns=[
315
295
  {"name": "Resource Type", "style": "cyan"},
316
296
  {"name": "Count", "style": "green", "justify": "right"},
317
- {"name": "Status", "style": "yellow"}
318
- ]
297
+ {"name": "Status", "style": "yellow"},
298
+ ],
319
299
  )
320
-
300
+
321
301
  total_resources = 0
322
302
  for resource_type, count in sorted(resource_counts.items()):
323
303
  status = "✅ Found" if count > 0 else "⚪ None"
324
- table.add_row(
325
- resource_type.title(),
326
- str(count),
327
- status
328
- )
304
+ table.add_row(resource_type.title(), str(count), status)
329
305
  total_resources += count
330
-
306
+
331
307
  console.print(table)
332
-
308
+
333
309
  # Overall summary
334
310
  summary_text = f"""
335
311
  [bold green]Overall Summary[/bold green]
@@ -340,12 +316,8 @@ def display_multi_resource_summary(resource_counts: Dict[str, int],
340
316
  [cyan]Execution Time:[/cyan] {execution_time:.2f} seconds
341
317
  [cyan]Performance:[/cyan] {(accounts * regions) / execution_time:.1f} operations/second
342
318
  """
343
-
344
- console.print(create_panel(
345
- summary_text.strip(),
346
- title="🎯 Inventory Complete",
347
- border_style="bright_blue"
348
- ))
319
+
320
+ console.print(create_panel(summary_text.strip(), title="🎯 Inventory Complete", border_style="bright_blue"))
349
321
 
350
322
 
351
323
  def display_account_tree(accounts_data: Dict[str, Dict]) -> None:
@@ -356,38 +328,33 @@ def display_account_tree(accounts_data: Dict[str, Dict]) -> None:
356
328
  accounts_data: Nested dictionary of account -> resource data
357
329
  """
358
330
  tree = Tree("🏢 [bold cyan]AWS Organization Structure[/bold cyan]")
359
-
331
+
360
332
  for account_id, account_data in accounts_data.items():
361
333
  account_name = account_data.get("account_name", "Unknown")
362
334
  account_branch = tree.add(f"📊 [yellow]Account: {account_id}[/yellow] ({account_name})")
363
-
335
+
364
336
  # Add regions
365
337
  regions = account_data.get("regions", {})
366
338
  for region, region_data in regions.items():
367
339
  region_branch = account_branch.add(f"🌍 [green]Region: {region}[/green]")
368
-
340
+
369
341
  # Add resource counts
370
342
  for resource_type, count in region_data.get("resource_counts", {}).items():
371
343
  if count > 0:
372
- icon = {
373
- "ec2": "🖥️",
374
- "rds": "🗄️",
375
- "s3": "🪣",
376
- "lambda": "⚡"
377
- }.get(resource_type, "📦")
378
-
344
+ icon = {"ec2": "🖥️", "rds": "🗄️", "s3": "🪣", "lambda": "⚡"}.get(resource_type, "📦")
345
+
379
346
  region_branch.add(f"{icon} {resource_type.upper()}: [bold]{count}[/bold] resources")
380
-
347
+
381
348
  console.print(tree)
382
349
 
383
350
 
384
351
  # Export public functions
385
352
  __all__ = [
386
353
  "display_inventory_header",
387
- "create_inventory_progress",
354
+ "create_inventory_progress",
388
355
  "display_ec2_inventory_results",
389
356
  "display_generic_inventory_results",
390
357
  "display_inventory_error",
391
358
  "display_multi_resource_summary",
392
- "display_account_tree"
393
- ]
359
+ "display_account_tree",
360
+ ]
@@ -192,7 +192,9 @@ for account_num in Accounts:
192
192
  )
193
193
  response = check_account_access(aws_acct, account_num, pAccessRole)
194
194
  if response["Success"]:
195
- console.print(f"[green]✅ Account {account_num} was successfully connected via role {pAccessRole} from {aws_acct.acct_number}[/green]")
195
+ console.print(
196
+ f"[green]✅ Account {account_num} was successfully connected via role {pAccessRole} from {aws_acct.acct_number}[/green]"
197
+ )
196
198
  """
197
199
  Put more commands here... Or you can write functions that represent your commands and call them from here.
198
200
  """
@@ -201,11 +203,13 @@ for account_num in Accounts:
201
203
  username = "Paul"
202
204
  user_response = participant_user(tgt_aws_access, username=username)
203
205
  else:
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
- ))
206
+ console.print(
207
+ Panel(
208
+ f"Access Role {pAccessRole} failed to connect to {account_num} from {aws_acct.acct_number}",
209
+ title=f"[red]❌ Connection Error: {response['ErrorMessage']}[/red]",
210
+ border_style="red",
211
+ )
212
+ )
209
213
 
210
214
  # Display access keys with Rich formatting
211
215
  credentials_info = [
@@ -213,14 +217,10 @@ for account_num in Accounts:
213
217
  f"[cyan]User:[/cyan] {user_response['User']} (created/confirmed in account {user_response['AccountId']})",
214
218
  f"[cyan]Password:[/cyan] {user_response['Password']}",
215
219
  f"[cyan]Access Key ID:[/cyan] {user_response['AccessKeyId']}",
216
- f"[cyan]Secret Access Key:[/cyan] {user_response['SecretAccessKey']}"
220
+ f"[cyan]Secret Access Key:[/cyan] {user_response['SecretAccessKey']}",
217
221
  ]
218
-
219
- console.print(Panel(
220
- "\n".join(credentials_info),
221
- title="[blue]🔑 Account Credentials[/blue]",
222
- border_style="blue"
223
- ))
222
+
223
+ console.print(Panel("\n".join(credentials_info), title="[blue]🔑 Account Credentials[/blue]", border_style="blue"))
224
224
 
225
225
  console.print("")
226
226
  console.print("[green]✅ Thanks for using this script...[/green]")