runbooks 1.0.0__py3-none-any.whl → 1.0.2__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 (99) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/WEIGHT_CONFIG_README.md +368 -0
  3. runbooks/cfat/app.ts +27 -19
  4. runbooks/cfat/assessment/runner.py +6 -5
  5. runbooks/cfat/tests/test_weight_configuration.ts +449 -0
  6. runbooks/cfat/weight_config.ts +574 -0
  7. runbooks/cloudops/models.py +20 -14
  8. runbooks/common/__init__.py +26 -9
  9. runbooks/common/aws_pricing.py +1070 -105
  10. runbooks/common/aws_pricing_api.py +276 -44
  11. runbooks/common/date_utils.py +115 -0
  12. runbooks/common/dry_run_examples.py +587 -0
  13. runbooks/common/dry_run_framework.py +520 -0
  14. runbooks/common/enhanced_exception_handler.py +10 -7
  15. runbooks/common/mcp_cost_explorer_integration.py +5 -4
  16. runbooks/common/memory_optimization.py +533 -0
  17. runbooks/common/performance_optimization_engine.py +1153 -0
  18. runbooks/common/profile_utils.py +86 -118
  19. runbooks/common/rich_utils.py +3 -3
  20. runbooks/common/sre_performance_suite.py +574 -0
  21. runbooks/finops/business_case_config.py +314 -0
  22. runbooks/finops/cost_processor.py +19 -4
  23. runbooks/finops/dashboard_runner.py +47 -28
  24. runbooks/finops/ebs_cost_optimizer.py +1 -1
  25. runbooks/finops/ebs_optimizer.py +56 -9
  26. runbooks/finops/embedded_mcp_validator.py +642 -36
  27. runbooks/finops/enhanced_trend_visualization.py +7 -2
  28. runbooks/finops/executive_export.py +789 -0
  29. runbooks/finops/finops_dashboard.py +6 -5
  30. runbooks/finops/finops_scenarios.py +34 -27
  31. runbooks/finops/iam_guidance.py +6 -1
  32. runbooks/finops/nat_gateway_optimizer.py +46 -27
  33. runbooks/finops/notebook_utils.py +1 -1
  34. runbooks/finops/schemas.py +73 -58
  35. runbooks/finops/single_dashboard.py +20 -4
  36. runbooks/finops/tests/test_integration.py +3 -1
  37. runbooks/finops/vpc_cleanup_exporter.py +2 -1
  38. runbooks/finops/vpc_cleanup_optimizer.py +22 -29
  39. runbooks/inventory/core/collector.py +51 -28
  40. runbooks/inventory/discovery.md +197 -247
  41. runbooks/inventory/inventory_modules.py +2 -2
  42. runbooks/inventory/list_ec2_instances.py +3 -3
  43. runbooks/inventory/models/account.py +5 -3
  44. runbooks/inventory/models/inventory.py +1 -1
  45. runbooks/inventory/models/resource.py +5 -3
  46. runbooks/inventory/organizations_discovery.py +102 -13
  47. runbooks/inventory/unified_validation_engine.py +2 -15
  48. runbooks/main.py +255 -92
  49. runbooks/operate/base.py +9 -6
  50. runbooks/operate/deployment_framework.py +5 -4
  51. runbooks/operate/deployment_validator.py +6 -5
  52. runbooks/operate/mcp_integration.py +6 -5
  53. runbooks/operate/networking_cost_heatmap.py +17 -13
  54. runbooks/operate/vpc_operations.py +82 -13
  55. runbooks/remediation/base.py +3 -1
  56. runbooks/remediation/commons.py +5 -5
  57. runbooks/remediation/commvault_ec2_analysis.py +66 -18
  58. runbooks/remediation/config/accounts_example.json +31 -0
  59. runbooks/remediation/multi_account.py +120 -7
  60. runbooks/remediation/remediation_cli.py +710 -0
  61. runbooks/remediation/universal_account_discovery.py +377 -0
  62. runbooks/remediation/workspaces_list.py +2 -2
  63. runbooks/security/compliance_automation_engine.py +99 -20
  64. runbooks/security/config/__init__.py +24 -0
  65. runbooks/security/config/compliance_config.py +255 -0
  66. runbooks/security/config/compliance_weights_example.json +22 -0
  67. runbooks/security/config_template_generator.py +500 -0
  68. runbooks/security/security_cli.py +377 -0
  69. runbooks/validation/cli.py +8 -7
  70. runbooks/validation/comprehensive_2way_validator.py +26 -15
  71. runbooks/validation/mcp_validator.py +62 -8
  72. runbooks/vpc/config.py +49 -15
  73. runbooks/vpc/cross_account_session.py +5 -1
  74. runbooks/vpc/heatmap_engine.py +438 -59
  75. runbooks/vpc/mcp_no_eni_validator.py +115 -36
  76. runbooks/vpc/performance_optimized_analyzer.py +546 -0
  77. runbooks/vpc/runbooks_adapter.py +33 -12
  78. runbooks/vpc/tests/conftest.py +4 -2
  79. runbooks/vpc/tests/test_cost_engine.py +3 -1
  80. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/METADATA +1 -1
  81. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/RECORD +85 -79
  82. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  83. runbooks/finops/runbooks.security.report_generator.log +0 -0
  84. runbooks/finops/runbooks.security.run_script.log +0 -0
  85. runbooks/finops/runbooks.security.security_export.log +0 -0
  86. runbooks/finops/tests/results_test_finops_dashboard.xml +0 -1
  87. runbooks/inventory/artifacts/scale-optimize-status.txt +0 -12
  88. runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
  89. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  90. runbooks/inventory/runbooks.security.run_script.log +0 -0
  91. runbooks/inventory/runbooks.security.security_export.log +0 -0
  92. runbooks/vpc/runbooks.inventory.organizations_discovery.log +0 -0
  93. runbooks/vpc/runbooks.security.report_generator.log +0 -0
  94. runbooks/vpc/runbooks.security.run_script.log +0 -0
  95. runbooks/vpc/runbooks.security.security_export.log +0 -0
  96. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/WHEEL +0 -0
  97. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/entry_points.txt +0 -0
  98. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/licenses/LICENSE +0 -0
  99. {runbooks-1.0.0.dist-info → runbooks-1.0.2.dist-info}/top_level.txt +0 -0
@@ -1,59 +1,51 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Profile Management Utilities for CloudOps Runbooks Platform
3
+ Universal AWS Profile Management for CloudOps Runbooks Platform
4
4
 
5
- This module provides centralized AWS profile management with enterprise-grade
6
- three-tier priority system extracted from proven FinOps success patterns.
5
+ This module provides truly universal AWS profile management that works with ANY AWS setup:
6
+ - Single account setups
7
+ - Multi-account setups
8
+ - Any profile naming convention
9
+ - No specific environment variable requirements
7
10
 
8
11
  Features:
9
- - Three-tier priority: User > Environment > Default
10
- - Multi-profile enterprise architecture support
11
- - Consistent session creation across all modules
12
- - Rich CLI integration for user feedback
13
- - Profile validation and error handling
12
+ - Universal compatibility: User --profile AWS_PROFILE → "default"
13
+ - Works with ANY AWS profile names (not just specific test profiles)
14
+ - No hardcoded environment variable assumptions
15
+ - Simple, reliable profile selection for all users
14
16
 
15
17
  Author: CloudOps Runbooks Team
16
- Version: 0.9.0
18
+ Version: 1.0.0 - Universal Compatibility
17
19
  """
18
20
 
19
21
  import os
20
22
  import time
21
- from typing import Dict, Optional
23
+ from typing import Dict, Optional, Union, List, Tuple
22
24
 
23
25
  import boto3
24
26
 
25
27
  from runbooks.common.rich_utils import console
26
28
 
27
- # Profile cache to reduce duplicate calls (enterprise performance optimization)
29
+ # Profile cache to reduce duplicate calls (performance optimization)
28
30
  _profile_cache = {}
29
31
  _cache_timestamp = None
30
32
  _cache_ttl = 300 # 5 minutes cache TTL
31
33
 
32
- # Enterprise AWS profile mappings with fallback defaults
33
- ENV_PROFILE_MAP = {
34
- "billing": os.getenv("BILLING_PROFILE"),
35
- "management": os.getenv("MANAGEMENT_PROFILE"),
36
- "operational": os.getenv("CENTRALISED_OPS_PROFILE"),
37
- }
38
34
 
39
- # Fallback defaults if environment variables are not set - NO hardcoded defaults
40
- DEFAULT_PROFILE = os.getenv("AWS_PROFILE") or "default" # "default" is AWS boto3 expected fallback
41
-
42
-
43
- def get_profile_for_operation(operation_type: str, user_specified_profile: Optional[str] = None) -> str:
35
+ def get_profile_for_operation(operation_type: str, user_specified_profile: Optional[Union[str, Tuple[str, ...], List[str]]] = None) -> str:
44
36
  """
45
- Get the appropriate AWS profile based on operation type using proven three-tier priority system.
37
+ Universal AWS profile selection that works with ANY AWS setup.
46
38
 
47
- PRIORITY ORDER (Enterprise Success Pattern):
39
+ SIMPLE PRIORITY ORDER (Universal Compatibility):
48
40
  1. User-specified profile (--profile parameter) - HIGHEST PRIORITY
49
- 2. Environment variables for specialized operations - FALLBACK ONLY
50
- 3. Default profile - LAST RESORT
41
+ 2. AWS_PROFILE environment variable - STANDARD AWS CONVENTION
42
+ 3. "default" profile - AWS STANDARD FALLBACK
51
43
 
52
- This pattern extracted from FinOps module achieving 99.9996% accuracy and 280% ROI.
44
+ Works with ANY profile names and ANY AWS setup - no specific environment variable requirements.
53
45
 
54
46
  Args:
55
- operation_type: Type of operation ('billing', 'management', 'operational')
56
- user_specified_profile: Profile specified by user via --profile parameter
47
+ operation_type: Type of operation (informational only, not used for profile selection)
48
+ user_specified_profile: Profile specified by user via --profile parameter (handles both str and tuple)
57
49
 
58
50
  Returns:
59
51
  str: Profile name to use for the operation
@@ -61,9 +53,16 @@ def get_profile_for_operation(operation_type: str, user_specified_profile: Optio
61
53
  Raises:
62
54
  SystemExit: If user-specified profile not found in AWS config
63
55
  """
56
+ # SAFETY NET: Handle tuple profiles (Click multiple=True parameter issue)
57
+ # This prevents errors like: Profile '('profile-name',)' not found
58
+ if isinstance(user_specified_profile, (tuple, list)) and user_specified_profile:
59
+ user_specified_profile = user_specified_profile[0] # Take first profile from tuple/list
60
+ elif isinstance(user_specified_profile, (tuple, list)) and not user_specified_profile:
61
+ user_specified_profile = None # Empty tuple/list becomes None
62
+
64
63
  global _profile_cache, _cache_timestamp
65
64
 
66
- # Check cache first to reduce duplicate calls (enterprise performance optimization)
65
+ # Check cache first to reduce duplicate calls (performance optimization)
67
66
  cache_key = f"{operation_type}:{user_specified_profile or 'None'}"
68
67
  current_time = time.time()
69
68
 
@@ -82,33 +81,26 @@ def get_profile_for_operation(operation_type: str, user_specified_profile: Optio
82
81
  # PRIORITY 1: User-specified profile ALWAYS takes precedence
83
82
  if user_specified_profile and user_specified_profile != "default":
84
83
  if user_specified_profile in available_profiles:
85
- console.log(f"[green]Using user-specified profile for {operation_type}: {user_specified_profile}[/]")
84
+ console.log(f"[green]Using user-specified profile: {user_specified_profile}[/]")
86
85
  # Cache the result to reduce duplicate calls
87
86
  _profile_cache[cache_key] = user_specified_profile
88
87
  return user_specified_profile
89
88
  else:
90
- console.log(f"[red]Error: User-specified profile '{user_specified_profile}' not found in AWS config[/]")
91
- # Don't fall back - user explicitly chose this profile
89
+ console.log(f"[red]Error: Profile '{user_specified_profile}' not found in AWS config[/]")
90
+ console.log(f"[yellow]Available profiles: {', '.join(available_profiles)}[/]")
92
91
  raise SystemExit(1)
93
92
 
94
- # PRIORITY 2: Environment variables (only when no user input)
95
- profile_map = {
96
- "billing": os.getenv("AWS_BILLING_PROFILE") or os.getenv("BILLING_PROFILE"),
97
- "management": os.getenv("AWS_MANAGEMENT_PROFILE") or os.getenv("MANAGEMENT_PROFILE"),
98
- "operational": os.getenv("AWS_CENTRALISED_OPS_PROFILE") or os.getenv("CENTRALISED_OPS_PROFILE"),
99
- "single_account": os.getenv("AWS_SINGLE_ACCOUNT_PROFILE") or os.getenv("SINGLE_AWS_PROFILE"),
100
- }
101
-
102
- env_profile = profile_map.get(operation_type)
103
- if env_profile and env_profile in available_profiles:
104
- console.log(f"[dim cyan]Using {operation_type} profile from environment: {env_profile}[/]")
93
+ # PRIORITY 2: AWS_PROFILE environment variable (standard AWS convention)
94
+ aws_profile = os.getenv("AWS_PROFILE")
95
+ if aws_profile and aws_profile in available_profiles:
96
+ console.log(f"[dim cyan]Using AWS_PROFILE environment variable: {aws_profile}[/]")
105
97
  # Cache the result to reduce duplicate calls
106
- _profile_cache[cache_key] = env_profile
107
- return env_profile
98
+ _profile_cache[cache_key] = aws_profile
99
+ return aws_profile
108
100
 
109
- # PRIORITY 3: Default profile (last resort)
110
- default_profile = user_specified_profile or "default"
111
- console.log(f"[yellow]No {operation_type} profile found, using default: {default_profile}[/]")
101
+ # PRIORITY 3: Default profile (AWS standard fallback)
102
+ default_profile = "default"
103
+ console.log(f"[yellow]Using default AWS profile: {default_profile}[/]")
112
104
  # Cache the result to reduce duplicate calls
113
105
  _profile_cache[cache_key] = default_profile
114
106
  return default_profile
@@ -116,11 +108,11 @@ def get_profile_for_operation(operation_type: str, user_specified_profile: Optio
116
108
 
117
109
  def resolve_profile_for_operation_silent(operation_type: str, user_specified_profile: Optional[str] = None) -> str:
118
110
  """
119
- Resolve AWS profile for operation type without logging (for display purposes).
120
- Uses the same logic as get_profile_for_operation but without console output.
111
+ Universal AWS profile resolution without logging (for display purposes).
112
+ Uses the same universal logic as get_profile_for_operation but without console output.
121
113
 
122
114
  Args:
123
- operation_type: Type of operation ('billing', 'management', 'operational')
115
+ operation_type: Type of operation (informational only, not used for profile selection)
124
116
  user_specified_profile: Profile specified by user via --profile parameter
125
117
 
126
118
  Returns:
@@ -139,78 +131,72 @@ def resolve_profile_for_operation_silent(operation_type: str, user_specified_pro
139
131
  # Don't fall back - user explicitly chose this profile
140
132
  raise SystemExit(1)
141
133
 
142
- # PRIORITY 2: Environment variables (only when no user input)
143
- profile_map = {
144
- "billing": os.getenv("AWS_BILLING_PROFILE") or os.getenv("BILLING_PROFILE"),
145
- "management": os.getenv("AWS_MANAGEMENT_PROFILE") or os.getenv("MANAGEMENT_PROFILE"),
146
- "operational": os.getenv("AWS_CENTRALISED_OPS_PROFILE") or os.getenv("CENTRALISED_OPS_PROFILE"),
147
- "single_account": os.getenv("AWS_SINGLE_ACCOUNT_PROFILE") or os.getenv("SINGLE_AWS_PROFILE"),
148
- }
149
-
150
- env_profile = profile_map.get(operation_type)
151
- if env_profile and env_profile in available_profiles:
152
- return env_profile
134
+ # PRIORITY 2: AWS_PROFILE environment variable (standard AWS convention)
135
+ aws_profile = os.getenv("AWS_PROFILE")
136
+ if aws_profile and aws_profile in available_profiles:
137
+ return aws_profile
153
138
 
154
- # PRIORITY 3: Default profile (last resort)
155
- return user_specified_profile or "default"
139
+ # PRIORITY 3: Default profile (AWS standard fallback)
140
+ return "default"
156
141
 
157
142
 
158
143
  def create_cost_session(profile: Optional[str] = None) -> boto3.Session:
159
144
  """
160
- Create a boto3 session specifically for cost operations.
161
- User-specified profile takes priority over BILLING_PROFILE environment variable.
145
+ Create a boto3 session for cost operations with universal profile support.
146
+ Works with ANY AWS profile configuration.
162
147
 
163
148
  Args:
164
149
  profile: User-specified profile (from --profile parameter)
165
150
 
166
151
  Returns:
167
- boto3.Session: Session configured for cost operations
152
+ boto3.Session: Session configured for AWS operations
168
153
  """
169
- cost_profile = get_profile_for_operation("billing", profile)
170
- return boto3.Session(profile_name=cost_profile)
154
+ selected_profile = get_profile_for_operation("cost", profile)
155
+ return boto3.Session(profile_name=selected_profile)
171
156
 
172
157
 
173
158
  def create_management_session(profile: Optional[str] = None) -> boto3.Session:
174
159
  """
175
- Create a boto3 session specifically for management operations.
176
- User-specified profile takes priority over MANAGEMENT_PROFILE environment variable.
160
+ Create a boto3 session for management operations with universal profile support.
161
+ Works with ANY AWS profile configuration.
177
162
 
178
163
  Args:
179
164
  profile: User-specified profile (from --profile parameter)
180
165
 
181
166
  Returns:
182
- boto3.Session: Session configured for management operations
167
+ boto3.Session: Session configured for AWS operations
183
168
  """
184
- mgmt_profile = get_profile_for_operation("management", profile)
185
- return boto3.Session(profile_name=mgmt_profile)
169
+ selected_profile = get_profile_for_operation("management", profile)
170
+ return boto3.Session(profile_name=selected_profile)
186
171
 
187
172
 
188
173
  def create_operational_session(profile: Optional[str] = None) -> boto3.Session:
189
174
  """
190
- Create a boto3 session specifically for operational tasks.
191
- User-specified profile takes priority over CENTRALISED_OPS_PROFILE environment variable.
175
+ Create a boto3 session for operational tasks with universal profile support.
176
+ Works with ANY AWS profile configuration.
192
177
 
193
178
  Args:
194
179
  profile: User-specified profile (from --profile parameter)
195
180
 
196
181
  Returns:
197
- boto3.Session: Session configured for operational tasks
182
+ boto3.Session: Session configured for AWS operations
198
183
  """
199
- ops_profile = get_profile_for_operation("operational", profile)
200
- return boto3.Session(profile_name=ops_profile)
184
+ selected_profile = get_profile_for_operation("operational", profile)
185
+ return boto3.Session(profile_name=selected_profile)
201
186
 
202
187
 
203
- def get_enterprise_profile_mapping() -> Dict[str, Optional[str]]:
188
+ def get_current_profile_info() -> Dict[str, Optional[str]]:
204
189
  """
205
- Get current enterprise profile mapping from environment variables.
190
+ Get current AWS profile information using universal approach.
191
+ Works with ANY AWS setup without hardcoded environment variable assumptions.
206
192
 
207
193
  Returns:
208
- Dict mapping operation types to their environment profile values
194
+ Dict with current profile information
209
195
  """
210
196
  return {
211
- "billing": os.getenv("BILLING_PROFILE"),
212
- "management": os.getenv("MANAGEMENT_PROFILE"),
213
- "operational": os.getenv("CENTRALISED_OPS_PROFILE"),
197
+ "aws_profile": os.getenv("AWS_PROFILE"),
198
+ "default_profile": "default",
199
+ "available_profiles": boto3.Session().available_profiles
214
200
  }
215
201
 
216
202
 
@@ -247,10 +233,10 @@ def validate_profile_access(profile_name: str, operation_type: str = "general")
247
233
 
248
234
  def get_available_profiles_for_validation() -> list:
249
235
  """
250
- Get available AWS profiles for validation - universal compatibility approach.
236
+ Get available AWS profiles for validation - truly universal approach.
251
237
 
252
- Returns all configured AWS profiles for validation without hardcoded assumptions.
253
- Supports any AWS setup: single account, multi-account, any profile naming convention.
238
+ Returns all configured AWS profiles for validation without ANY hardcoded assumptions.
239
+ Works with any AWS setup: single account, multi-account, any profile naming convention.
254
240
 
255
241
  Returns:
256
242
  list: Available AWS profile names for validation
@@ -259,38 +245,20 @@ def get_available_profiles_for_validation() -> list:
259
245
  # Get all available profiles from AWS CLI configuration
260
246
  available_profiles = boto3.Session().available_profiles
261
247
 
262
- # Filter out common system profiles that shouldn't be tested
263
- system_profiles = {'default', 'none', 'null', ''}
264
-
265
- # Return profiles for validation, including default if it's the only one
248
+ # Start with AWS_PROFILE if set
266
249
  validation_profiles = []
250
+ aws_profile = os.getenv("AWS_PROFILE")
251
+ if aws_profile and aws_profile in available_profiles:
252
+ validation_profiles.append(aws_profile)
267
253
 
268
- # Add environment variable profiles if they exist
269
- env_profiles = [
270
- os.getenv("AWS_BILLING_PROFILE"),
271
- os.getenv("AWS_MANAGEMENT_PROFILE"),
272
- os.getenv("AWS_CENTRALISED_OPS_PROFILE"),
273
- os.getenv("AWS_SINGLE_ACCOUNT_PROFILE"),
274
- os.getenv("BILLING_PROFILE"),
275
- os.getenv("MANAGEMENT_PROFILE"),
276
- os.getenv("CENTRALISED_OPS_PROFILE"),
277
- os.getenv("SINGLE_AWS_PROFILE"),
278
- ]
279
-
280
- # Add valid environment profiles
281
- for profile in env_profiles:
282
- if profile and profile in available_profiles and profile not in validation_profiles:
254
+ # Add all other available profiles (universal approach)
255
+ for profile in available_profiles:
256
+ if profile not in validation_profiles:
283
257
  validation_profiles.append(profile)
284
258
 
285
- # If no environment profiles found, use available profiles (universal approach)
259
+ # Ensure we have at least one profile to test
286
260
  if not validation_profiles:
287
- for profile in available_profiles:
288
- if profile not in system_profiles:
289
- validation_profiles.append(profile)
290
-
291
- # Always include 'default' if available and no other profiles found
292
- if not validation_profiles and 'default' in available_profiles:
293
- validation_profiles.append('default')
261
+ validation_profiles = ['default']
294
262
 
295
263
  return validation_profiles
296
264
 
@@ -303,10 +271,10 @@ def get_available_profiles_for_validation() -> list:
303
271
  __all__ = [
304
272
  "get_profile_for_operation",
305
273
  "resolve_profile_for_operation_silent",
306
- "create_cost_session",
274
+ "create_cost_session",
307
275
  "create_management_session",
308
276
  "create_operational_session",
309
- "get_enterprise_profile_mapping",
277
+ "get_current_profile_info",
310
278
  "validate_profile_access",
311
279
  "get_available_profiles_for_validation",
312
280
  ]
@@ -358,8 +358,8 @@ def create_display_profile_name(profile_name: str, max_length: int = 25, context
358
358
  meaningful information for identification. Full names remain available for AWS API calls.
359
359
 
360
360
  Examples:
361
- 'ams-admin-Billing-ReadOnlyAccess-909135376185' → 'ams-admin-Billing-9091...'
362
- 'ams-centralised-ops-ReadOnlyAccess-335083429030' → 'ams-centralised-ops-3350...'
361
+ 'your-admin-Billing-ReadOnlyAccess-123456789012' → 'your-admin-Billing-1234...'
362
+ 'your-centralised-ops-ReadOnlyAccess-987654321098' → 'your-centralised-ops-9876...'
363
363
  'short-profile' → 'short-profile' (no truncation needed)
364
364
 
365
365
  Args:
@@ -398,7 +398,7 @@ def create_display_profile_name(profile_name: str, max_length: int = 25, context
398
398
 
399
399
  # Strategy 1: Keep meaningful prefix + account ID suffix
400
400
  if len(parts) >= 4 and parts[-1].isdigit():
401
- # Enterprise pattern: ams-admin-Billing-ReadOnlyAccess-909135376185
401
+ # Enterprise pattern: your-admin-Billing-ReadOnlyAccess-123456789012
402
402
  account_id = parts[-1]
403
403
  prefix_parts = parts[:-2] # Skip permissions part for brevity
404
404