runbooks 1.1.3__py3-none-any.whl → 1.1.5__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 (247) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/WEIGHT_CONFIG_README.md +1 -1
  8. runbooks/cfat/assessment/compliance.py +8 -8
  9. runbooks/cfat/assessment/runner.py +1 -0
  10. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  11. runbooks/cfat/models.py +6 -2
  12. runbooks/cfat/tests/__init__.py +6 -1
  13. runbooks/cli/__init__.py +13 -0
  14. runbooks/cli/commands/cfat.py +274 -0
  15. runbooks/cli/commands/finops.py +1164 -0
  16. runbooks/cli/commands/inventory.py +379 -0
  17. runbooks/cli/commands/operate.py +239 -0
  18. runbooks/cli/commands/security.py +248 -0
  19. runbooks/cli/commands/validation.py +825 -0
  20. runbooks/cli/commands/vpc.py +310 -0
  21. runbooks/cli/registry.py +107 -0
  22. runbooks/cloudops/__init__.py +23 -30
  23. runbooks/cloudops/base.py +96 -107
  24. runbooks/cloudops/cost_optimizer.py +549 -547
  25. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  26. runbooks/cloudops/interfaces.py +226 -227
  27. runbooks/cloudops/lifecycle_manager.py +5 -4
  28. runbooks/cloudops/mcp_cost_validation.py +252 -235
  29. runbooks/cloudops/models.py +78 -53
  30. runbooks/cloudops/monitoring_automation.py +5 -4
  31. runbooks/cloudops/notebook_framework.py +179 -215
  32. runbooks/cloudops/security_enforcer.py +125 -159
  33. runbooks/common/accuracy_validator.py +11 -0
  34. runbooks/common/aws_pricing.py +349 -326
  35. runbooks/common/aws_pricing_api.py +211 -212
  36. runbooks/common/aws_profile_manager.py +341 -0
  37. runbooks/common/aws_utils.py +75 -80
  38. runbooks/common/business_logic.py +127 -105
  39. runbooks/common/cli_decorators.py +36 -60
  40. runbooks/common/comprehensive_cost_explorer_integration.py +456 -464
  41. runbooks/common/cross_account_manager.py +198 -205
  42. runbooks/common/date_utils.py +27 -39
  43. runbooks/common/decorators.py +235 -0
  44. runbooks/common/dry_run_examples.py +173 -208
  45. runbooks/common/dry_run_framework.py +157 -155
  46. runbooks/common/enhanced_exception_handler.py +15 -4
  47. runbooks/common/enhanced_logging_example.py +50 -64
  48. runbooks/common/enhanced_logging_integration_example.py +65 -37
  49. runbooks/common/env_utils.py +16 -16
  50. runbooks/common/error_handling.py +40 -38
  51. runbooks/common/lazy_loader.py +41 -23
  52. runbooks/common/logging_integration_helper.py +79 -86
  53. runbooks/common/mcp_cost_explorer_integration.py +478 -495
  54. runbooks/common/mcp_integration.py +63 -74
  55. runbooks/common/memory_optimization.py +140 -118
  56. runbooks/common/module_cli_base.py +37 -58
  57. runbooks/common/organizations_client.py +176 -194
  58. runbooks/common/patterns.py +204 -0
  59. runbooks/common/performance_monitoring.py +67 -71
  60. runbooks/common/performance_optimization_engine.py +283 -274
  61. runbooks/common/profile_utils.py +248 -39
  62. runbooks/common/rich_utils.py +643 -92
  63. runbooks/common/sre_performance_suite.py +177 -186
  64. runbooks/enterprise/__init__.py +1 -1
  65. runbooks/enterprise/logging.py +144 -106
  66. runbooks/enterprise/security.py +187 -204
  67. runbooks/enterprise/validation.py +43 -56
  68. runbooks/finops/__init__.py +29 -33
  69. runbooks/finops/account_resolver.py +1 -1
  70. runbooks/finops/advanced_optimization_engine.py +980 -0
  71. runbooks/finops/automation_core.py +268 -231
  72. runbooks/finops/business_case_config.py +184 -179
  73. runbooks/finops/cli.py +660 -139
  74. runbooks/finops/commvault_ec2_analysis.py +157 -164
  75. runbooks/finops/compute_cost_optimizer.py +336 -320
  76. runbooks/finops/config.py +20 -20
  77. runbooks/finops/cost_optimizer.py +488 -622
  78. runbooks/finops/cost_processor.py +332 -214
  79. runbooks/finops/dashboard_runner.py +1006 -172
  80. runbooks/finops/ebs_cost_optimizer.py +991 -657
  81. runbooks/finops/elastic_ip_optimizer.py +317 -257
  82. runbooks/finops/enhanced_mcp_integration.py +340 -0
  83. runbooks/finops/enhanced_progress.py +40 -37
  84. runbooks/finops/enhanced_trend_visualization.py +3 -2
  85. runbooks/finops/enterprise_wrappers.py +230 -292
  86. runbooks/finops/executive_export.py +203 -160
  87. runbooks/finops/helpers.py +130 -288
  88. runbooks/finops/iam_guidance.py +1 -1
  89. runbooks/finops/infrastructure/__init__.py +80 -0
  90. runbooks/finops/infrastructure/commands.py +506 -0
  91. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  92. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  93. runbooks/finops/markdown_exporter.py +338 -175
  94. runbooks/finops/mcp_validator.py +1952 -0
  95. runbooks/finops/nat_gateway_optimizer.py +1513 -482
  96. runbooks/finops/network_cost_optimizer.py +657 -587
  97. runbooks/finops/notebook_utils.py +226 -188
  98. runbooks/finops/optimization_engine.py +1136 -0
  99. runbooks/finops/optimizer.py +25 -29
  100. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  101. runbooks/finops/reservation_optimizer.py +427 -363
  102. runbooks/finops/scenario_cli_integration.py +77 -78
  103. runbooks/finops/scenarios.py +1278 -439
  104. runbooks/finops/schemas.py +218 -182
  105. runbooks/finops/snapshot_manager.py +2289 -0
  106. runbooks/finops/tests/test_finops_dashboard.py +3 -3
  107. runbooks/finops/tests/test_reference_images_validation.py +2 -2
  108. runbooks/finops/tests/test_single_account_features.py +17 -17
  109. runbooks/finops/tests/validate_test_suite.py +1 -1
  110. runbooks/finops/types.py +3 -3
  111. runbooks/finops/validation_framework.py +263 -269
  112. runbooks/finops/vpc_cleanup_exporter.py +191 -146
  113. runbooks/finops/vpc_cleanup_optimizer.py +593 -575
  114. runbooks/finops/workspaces_analyzer.py +171 -182
  115. runbooks/hitl/enhanced_workflow_engine.py +1 -1
  116. runbooks/integration/__init__.py +89 -0
  117. runbooks/integration/mcp_integration.py +1920 -0
  118. runbooks/inventory/CLAUDE.md +816 -0
  119. runbooks/inventory/README.md +3 -3
  120. runbooks/inventory/Tests/common_test_data.py +30 -30
  121. runbooks/inventory/__init__.py +2 -2
  122. runbooks/inventory/cloud_foundations_integration.py +144 -149
  123. runbooks/inventory/collectors/aws_comprehensive.py +28 -11
  124. runbooks/inventory/collectors/aws_networking.py +111 -101
  125. runbooks/inventory/collectors/base.py +4 -0
  126. runbooks/inventory/core/collector.py +495 -313
  127. runbooks/inventory/discovery.md +2 -2
  128. runbooks/inventory/drift_detection_cli.py +69 -96
  129. runbooks/inventory/find_ec2_security_groups.py +1 -1
  130. runbooks/inventory/inventory_mcp_cli.py +48 -46
  131. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  132. runbooks/inventory/mcp_inventory_validator.py +549 -465
  133. runbooks/inventory/mcp_vpc_validator.py +359 -442
  134. runbooks/inventory/organizations_discovery.py +56 -52
  135. runbooks/inventory/rich_inventory_display.py +33 -32
  136. runbooks/inventory/unified_validation_engine.py +278 -251
  137. runbooks/inventory/vpc_analyzer.py +733 -696
  138. runbooks/inventory/vpc_architecture_validator.py +293 -348
  139. runbooks/inventory/vpc_dependency_analyzer.py +382 -378
  140. runbooks/inventory/vpc_flow_analyzer.py +3 -3
  141. runbooks/main.py +152 -9147
  142. runbooks/main_final.py +91 -60
  143. runbooks/main_minimal.py +22 -10
  144. runbooks/main_optimized.py +131 -100
  145. runbooks/main_ultra_minimal.py +7 -2
  146. runbooks/mcp/__init__.py +36 -0
  147. runbooks/mcp/integration.py +679 -0
  148. runbooks/metrics/dora_metrics_engine.py +2 -2
  149. runbooks/monitoring/performance_monitor.py +9 -4
  150. runbooks/operate/dynamodb_operations.py +3 -1
  151. runbooks/operate/ec2_operations.py +145 -137
  152. runbooks/operate/iam_operations.py +146 -152
  153. runbooks/operate/mcp_integration.py +1 -1
  154. runbooks/operate/networking_cost_heatmap.py +33 -10
  155. runbooks/operate/privatelink_operations.py +1 -1
  156. runbooks/operate/rds_operations.py +223 -254
  157. runbooks/operate/s3_operations.py +107 -118
  158. runbooks/operate/vpc_endpoints.py +1 -1
  159. runbooks/operate/vpc_operations.py +648 -618
  160. runbooks/remediation/base.py +1 -1
  161. runbooks/remediation/commons.py +10 -7
  162. runbooks/remediation/commvault_ec2_analysis.py +71 -67
  163. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  164. runbooks/remediation/multi_account.py +24 -21
  165. runbooks/remediation/rds_snapshot_list.py +91 -65
  166. runbooks/remediation/remediation_cli.py +92 -146
  167. runbooks/remediation/universal_account_discovery.py +83 -79
  168. runbooks/remediation/workspaces_list.py +49 -44
  169. runbooks/security/__init__.py +19 -0
  170. runbooks/security/assessment_runner.py +1150 -0
  171. runbooks/security/baseline_checker.py +812 -0
  172. runbooks/security/cloudops_automation_security_validator.py +509 -535
  173. runbooks/security/compliance_automation_engine.py +17 -17
  174. runbooks/security/config/__init__.py +2 -2
  175. runbooks/security/config/compliance_config.py +50 -50
  176. runbooks/security/config_template_generator.py +63 -76
  177. runbooks/security/enterprise_security_framework.py +1 -1
  178. runbooks/security/executive_security_dashboard.py +519 -508
  179. runbooks/security/integration_test_enterprise_security.py +5 -3
  180. runbooks/security/multi_account_security_controls.py +959 -1210
  181. runbooks/security/real_time_security_monitor.py +422 -444
  182. runbooks/security/run_script.py +1 -1
  183. runbooks/security/security_baseline_tester.py +1 -1
  184. runbooks/security/security_cli.py +143 -112
  185. runbooks/security/test_2way_validation.py +439 -0
  186. runbooks/security/two_way_validation_framework.py +852 -0
  187. runbooks/sre/mcp_reliability_engine.py +6 -6
  188. runbooks/sre/production_monitoring_framework.py +167 -177
  189. runbooks/tdd/__init__.py +15 -0
  190. runbooks/tdd/cli.py +1071 -0
  191. runbooks/utils/__init__.py +14 -17
  192. runbooks/utils/logger.py +7 -2
  193. runbooks/utils/version_validator.py +51 -48
  194. runbooks/validation/__init__.py +6 -6
  195. runbooks/validation/cli.py +9 -3
  196. runbooks/validation/comprehensive_2way_validator.py +754 -708
  197. runbooks/validation/mcp_validator.py +906 -228
  198. runbooks/validation/terraform_citations_validator.py +104 -115
  199. runbooks/validation/terraform_drift_detector.py +447 -451
  200. runbooks/vpc/README.md +617 -0
  201. runbooks/vpc/__init__.py +8 -1
  202. runbooks/vpc/analyzer.py +577 -0
  203. runbooks/vpc/cleanup_wrapper.py +476 -413
  204. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  205. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  206. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  207. runbooks/vpc/config.py +92 -97
  208. runbooks/vpc/cost_engine.py +411 -148
  209. runbooks/vpc/cost_explorer_integration.py +553 -0
  210. runbooks/vpc/cross_account_session.py +101 -106
  211. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  212. runbooks/vpc/eni_gate_validator.py +961 -0
  213. runbooks/vpc/heatmap_engine.py +190 -162
  214. runbooks/vpc/mcp_no_eni_validator.py +681 -640
  215. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  216. runbooks/vpc/networking_wrapper.py +15 -8
  217. runbooks/vpc/pdca_remediation_planner.py +528 -0
  218. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  219. runbooks/vpc/runbooks_adapter.py +1167 -241
  220. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  221. runbooks/vpc/test_data_loader.py +358 -0
  222. runbooks/vpc/tests/conftest.py +314 -4
  223. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  224. runbooks/vpc/tests/test_cost_engine.py +0 -2
  225. runbooks/vpc/topology_generator.py +326 -0
  226. runbooks/vpc/unified_scenarios.py +1302 -1129
  227. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  228. runbooks-1.1.5.dist-info/METADATA +328 -0
  229. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/RECORD +233 -200
  230. runbooks/finops/README.md +0 -414
  231. runbooks/finops/accuracy_cross_validator.py +0 -647
  232. runbooks/finops/business_cases.py +0 -950
  233. runbooks/finops/dashboard_router.py +0 -922
  234. runbooks/finops/ebs_optimizer.py +0 -956
  235. runbooks/finops/embedded_mcp_validator.py +0 -1629
  236. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  237. runbooks/finops/finops_dashboard.py +0 -584
  238. runbooks/finops/finops_scenarios.py +0 -1218
  239. runbooks/finops/legacy_migration.py +0 -730
  240. runbooks/finops/multi_dashboard.py +0 -1519
  241. runbooks/finops/single_dashboard.py +0 -1113
  242. runbooks/finops/unlimited_scenarios.py +0 -393
  243. runbooks-1.1.3.dist-info/METADATA +0 -799
  244. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
  245. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
  246. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
  247. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -737,16 +737,13 @@ class S3Operations(BaseOperation):
737
737
  return [result]
738
738
 
739
739
  def find_buckets_without_lifecycle(
740
- self,
741
- context: OperationContext,
742
- region: Optional[str] = None,
743
- bucket_names: Optional[List[str]] = None
740
+ self, context: OperationContext, region: Optional[str] = None, bucket_names: Optional[List[str]] = None
744
741
  ) -> List[OperationResult]:
745
742
  """
746
743
  Find S3 buckets without lifecycle policies.
747
-
744
+
748
745
  Enhanced from unSkript notebook: AWS_Add_Lifecycle_Policy_To_S3_Buckets.ipynb
749
- Identifies buckets that do not have any configured lifecycle rules for
746
+ Identifies buckets that do not have any configured lifecycle rules for
750
747
  managing object lifecycle, valuable for optimizing storage costs.
751
748
 
752
749
  Args:
@@ -757,15 +754,13 @@ class S3Operations(BaseOperation):
757
754
  Returns:
758
755
  List of operation results with buckets without lifecycle policies
759
756
  """
760
- result = self.create_operation_result(
761
- context, "find_buckets_without_lifecycle", "s3:bucket", "lifecycle-audit"
762
- )
757
+ result = self.create_operation_result(context, "find_buckets_without_lifecycle", "s3:bucket", "lifecycle-audit")
763
758
 
764
759
  try:
765
760
  console.print(f"[bold blue]🔍 Scanning for S3 buckets without lifecycle policies...[/bold blue]")
766
-
761
+
767
762
  buckets_without_policy = []
768
-
763
+
769
764
  # Get list of regions to search
770
765
  search_regions = []
771
766
  if region:
@@ -786,7 +781,7 @@ class S3Operations(BaseOperation):
786
781
  for reg in search_regions:
787
782
  try:
788
783
  s3_client = self.get_client("s3", reg)
789
-
784
+
790
785
  # Get buckets to check
791
786
  if bucket_names:
792
787
  # Check specific buckets
@@ -801,17 +796,19 @@ class S3Operations(BaseOperation):
801
796
  try:
802
797
  # Get bucket location to ensure it's in the current region
803
798
  try:
804
- bucket_location = self.execute_aws_call(s3_client, "get_bucket_location", Bucket=bucket_name)
799
+ bucket_location = self.execute_aws_call(
800
+ s3_client, "get_bucket_location", Bucket=bucket_name
801
+ )
805
802
  bucket_region = bucket_location.get("LocationConstraint")
806
-
803
+
807
804
  # us-east-1 returns None for LocationConstraint
808
805
  if bucket_region is None:
809
806
  bucket_region = "us-east-1"
810
-
807
+
811
808
  # Skip if bucket is not in current region (when checking all regions)
812
809
  if not bucket_names and bucket_region != reg:
813
810
  continue
814
-
811
+
815
812
  except ClientError as e:
816
813
  if e.response["Error"]["Code"] in ["NoSuchBucket", "AccessDenied"]:
817
814
  continue
@@ -822,25 +819,29 @@ class S3Operations(BaseOperation):
822
819
  lifecycle_response = self.execute_aws_call(
823
820
  s3_client, "get_bucket_lifecycle_configuration", Bucket=bucket_name
824
821
  )
825
-
822
+
826
823
  # If we get here, bucket has lifecycle rules
827
824
  rules = lifecycle_response.get("Rules", [])
828
825
  if not rules:
829
826
  # Empty rules list means no active lifecycle
830
- buckets_without_policy.append({
831
- "bucket_name": bucket_name,
832
- "region": bucket_region,
833
- "issue": "Empty lifecycle configuration"
834
- })
835
-
827
+ buckets_without_policy.append(
828
+ {
829
+ "bucket_name": bucket_name,
830
+ "region": bucket_region,
831
+ "issue": "Empty lifecycle configuration",
832
+ }
833
+ )
834
+
836
835
  except ClientError as e:
837
836
  if e.response["Error"]["Code"] == "NoSuchLifecycleConfiguration":
838
837
  # No lifecycle configuration found
839
- buckets_without_policy.append({
840
- "bucket_name": bucket_name,
841
- "region": bucket_region,
842
- "issue": "No lifecycle configuration"
843
- })
838
+ buckets_without_policy.append(
839
+ {
840
+ "bucket_name": bucket_name,
841
+ "region": bucket_region,
842
+ "issue": "No lifecycle configuration",
843
+ }
844
+ )
844
845
  else:
845
846
  logger.warning(f"Could not check lifecycle for bucket {bucket_name}: {e}")
846
847
 
@@ -854,27 +855,26 @@ class S3Operations(BaseOperation):
854
855
 
855
856
  # Format results with Rich console output
856
857
  if buckets_without_policy:
857
- console.print(f"[bold yellow]⚠️ Found {len(buckets_without_policy)} bucket(s) without lifecycle policies:[/bold yellow]")
858
-
858
+ console.print(
859
+ f"[bold yellow]⚠️ Found {len(buckets_without_policy)} bucket(s) without lifecycle policies:[/bold yellow]"
860
+ )
861
+
859
862
  from rich.table import Table
863
+
860
864
  table = Table(show_header=True, header_style="bold magenta")
861
865
  table.add_column("Bucket Name", style="cyan")
862
866
  table.add_column("Region", style="green")
863
867
  table.add_column("Issue", style="yellow")
864
-
868
+
865
869
  for bucket_info in buckets_without_policy:
866
- table.add_row(
867
- bucket_info["bucket_name"],
868
- bucket_info["region"],
869
- bucket_info["issue"]
870
- )
871
-
870
+ table.add_row(bucket_info["bucket_name"], bucket_info["region"], bucket_info["issue"])
871
+
872
872
  console.print(table)
873
-
873
+
874
874
  result.response_data = {
875
875
  "buckets_without_lifecycle": buckets_without_policy,
876
876
  "total_count": len(buckets_without_policy),
877
- "regions_scanned": search_regions
877
+ "regions_scanned": search_regions,
878
878
  }
879
879
  result.mark_completed(OperationStatus.SUCCESS)
880
880
  else:
@@ -883,7 +883,7 @@ class S3Operations(BaseOperation):
883
883
  "buckets_without_lifecycle": [],
884
884
  "total_count": 0,
885
885
  "regions_scanned": search_regions,
886
- "message": "All buckets have lifecycle policies"
886
+ "message": "All buckets have lifecycle policies",
887
887
  }
888
888
  result.mark_completed(OperationStatus.SUCCESS)
889
889
 
@@ -906,7 +906,7 @@ class S3Operations(BaseOperation):
906
906
  ) -> List[OperationResult]:
907
907
  """
908
908
  Add lifecycle policies to multiple S3 buckets in bulk.
909
-
909
+
910
910
  Enhanced from unSkript notebook: AWS_Add_Lifecycle_Policy_To_S3_Buckets.ipynb
911
911
  Applies optimized lifecycle configuration for cost management.
912
912
 
@@ -923,32 +923,30 @@ class S3Operations(BaseOperation):
923
923
  List of operation results for each bucket processed
924
924
  """
925
925
  results = []
926
-
926
+
927
927
  console.print(f"[bold blue]📋 Adding lifecycle policies to {len(bucket_list)} bucket(s)...[/bold blue]")
928
-
928
+
929
929
  if context.dry_run:
930
930
  console.print("[yellow]🧪 DRY-RUN MODE: No actual changes will be made[/yellow]")
931
931
 
932
932
  for i, bucket_info in enumerate(bucket_list, 1):
933
933
  bucket_name = bucket_info.get("bucket_name")
934
934
  bucket_region = bucket_info.get("region")
935
-
935
+
936
936
  if not bucket_name or not bucket_region:
937
937
  logger.error(f"Invalid bucket info: {bucket_info}")
938
938
  continue
939
-
939
+
940
940
  console.print(f"[cyan]({i}/{len(bucket_list)}) Processing bucket: {bucket_name}[/cyan]")
941
941
 
942
- result = self.create_operation_result(
943
- context, "add_lifecycle_policy", "s3:bucket", bucket_name
944
- )
942
+ result = self.create_operation_result(context, "add_lifecycle_policy", "s3:bucket", bucket_name)
945
943
 
946
944
  try:
947
945
  s3_client = self.get_client("s3", bucket_region)
948
946
 
949
947
  # Build lifecycle configuration
950
948
  lifecycle_rules = []
951
-
949
+
952
950
  # Main lifecycle rule
953
951
  rule = {
954
952
  "ID": f"lifecycle-rule-{int(datetime.now().timestamp())}",
@@ -964,17 +962,11 @@ class S3Operations(BaseOperation):
964
962
  # Add storage class transitions
965
963
  transitions = []
966
964
  if transition_ia_days and transition_ia_days < expiration_days:
967
- transitions.append({
968
- "Days": transition_ia_days,
969
- "StorageClass": "STANDARD_IA"
970
- })
971
-
965
+ transitions.append({"Days": transition_ia_days, "StorageClass": "STANDARD_IA"})
966
+
972
967
  if transition_glacier_days and transition_glacier_days < expiration_days:
973
- transitions.append({
974
- "Days": transition_glacier_days,
975
- "StorageClass": "GLACIER"
976
- })
977
-
968
+ transitions.append({"Days": transition_glacier_days, "StorageClass": "GLACIER"})
969
+
978
970
  if transitions:
979
971
  rule["Transitions"] = transitions
980
972
 
@@ -988,7 +980,7 @@ class S3Operations(BaseOperation):
988
980
  "bucket_name": bucket_name,
989
981
  "region": bucket_region,
990
982
  "lifecycle_config": lifecycle_config,
991
- "dry_run": True
983
+ "dry_run": True,
992
984
  }
993
985
  result.mark_completed(OperationStatus.DRY_RUN)
994
986
  else:
@@ -997,16 +989,16 @@ class S3Operations(BaseOperation):
997
989
  s3_client,
998
990
  "put_bucket_lifecycle_configuration",
999
991
  Bucket=bucket_name,
1000
- LifecycleConfiguration=lifecycle_config
992
+ LifecycleConfiguration=lifecycle_config,
1001
993
  )
1002
994
 
1003
995
  console.print(f"[green] ✅ Successfully applied lifecycle policy to {bucket_name}[/green]")
1004
-
996
+
1005
997
  result.response_data = {
1006
998
  "bucket_name": bucket_name,
1007
999
  "region": bucket_region,
1008
1000
  "lifecycle_config": lifecycle_config,
1009
- "aws_response": response
1001
+ "aws_response": response,
1010
1002
  }
1011
1003
  result.mark_completed(OperationStatus.SUCCESS)
1012
1004
 
@@ -1026,7 +1018,7 @@ class S3Operations(BaseOperation):
1026
1018
  # Summary report
1027
1019
  successful = len([r for r in results if r.success])
1028
1020
  failed = len(results) - successful
1029
-
1021
+
1030
1022
  if context.dry_run:
1031
1023
  console.print(f"[yellow]🧪 DRY-RUN SUMMARY: Would process {len(results)} bucket(s)[/yellow]")
1032
1024
  else:
@@ -1037,9 +1029,7 @@ class S3Operations(BaseOperation):
1037
1029
 
1038
1030
  return results
1039
1031
 
1040
- def get_bucket_lifecycle(
1041
- self, context: OperationContext, bucket_name: str
1042
- ) -> List[OperationResult]:
1032
+ def get_bucket_lifecycle(self, context: OperationContext, bucket_name: str) -> List[OperationResult]:
1043
1033
  """
1044
1034
  Get current lifecycle configuration for an S3 bucket.
1045
1035
 
@@ -1051,56 +1041,49 @@ class S3Operations(BaseOperation):
1051
1041
  List of operation results with current lifecycle configuration
1052
1042
  """
1053
1043
  s3_client = self.get_client("s3")
1054
-
1055
- result = self.create_operation_result(
1056
- context, "get_bucket_lifecycle", "s3:bucket", bucket_name
1057
- )
1044
+
1045
+ result = self.create_operation_result(context, "get_bucket_lifecycle", "s3:bucket", bucket_name)
1058
1046
 
1059
1047
  try:
1060
1048
  console.print(f"[blue]🔍 Checking lifecycle configuration for bucket: {bucket_name}[/blue]")
1061
-
1049
+
1062
1050
  try:
1063
- response = self.execute_aws_call(
1064
- s3_client, "get_bucket_lifecycle_configuration", Bucket=bucket_name
1065
- )
1066
-
1051
+ response = self.execute_aws_call(s3_client, "get_bucket_lifecycle_configuration", Bucket=bucket_name)
1052
+
1067
1053
  rules = response.get("Rules", [])
1068
1054
  console.print(f"[green]✅ Found {len(rules)} lifecycle rule(s) for bucket {bucket_name}[/green]")
1069
-
1055
+
1070
1056
  # Display rules in a formatted table
1071
1057
  if rules:
1072
1058
  from rich.table import Table
1059
+
1073
1060
  table = Table(show_header=True, header_style="bold magenta")
1074
1061
  table.add_column("Rule ID", style="cyan")
1075
1062
  table.add_column("Status", style="green")
1076
1063
  table.add_column("Prefix", style="yellow")
1077
1064
  table.add_column("Expiration", style="red")
1078
-
1065
+
1079
1066
  for rule in rules:
1080
1067
  rule_id = rule.get("ID", "N/A")
1081
1068
  status = rule.get("Status", "N/A")
1082
-
1069
+
1083
1070
  # Handle different filter formats
1084
1071
  filter_info = rule.get("Filter", {})
1085
1072
  if isinstance(filter_info, dict):
1086
1073
  prefix = filter_info.get("Prefix", "")
1087
1074
  else:
1088
1075
  prefix = ""
1089
-
1076
+
1090
1077
  expiration = rule.get("Expiration", {})
1091
1078
  exp_days = expiration.get("Days", "N/A")
1092
-
1079
+
1093
1080
  table.add_row(rule_id, status, prefix or "All objects", str(exp_days))
1094
-
1081
+
1095
1082
  console.print(table)
1096
-
1097
- result.response_data = {
1098
- "bucket_name": bucket_name,
1099
- "lifecycle_rules": rules,
1100
- "rules_count": len(rules)
1101
- }
1083
+
1084
+ result.response_data = {"bucket_name": bucket_name, "lifecycle_rules": rules, "rules_count": len(rules)}
1102
1085
  result.mark_completed(OperationStatus.SUCCESS)
1103
-
1086
+
1104
1087
  except ClientError as e:
1105
1088
  if e.response["Error"]["Code"] == "NoSuchLifecycleConfiguration":
1106
1089
  console.print(f"[yellow]⚠️ No lifecycle configuration found for bucket {bucket_name}[/yellow]")
@@ -1108,7 +1091,7 @@ class S3Operations(BaseOperation):
1108
1091
  "bucket_name": bucket_name,
1109
1092
  "lifecycle_rules": [],
1110
1093
  "rules_count": 0,
1111
- "message": "No lifecycle configuration"
1094
+ "message": "No lifecycle configuration",
1112
1095
  }
1113
1096
  result.mark_completed(OperationStatus.SUCCESS)
1114
1097
  else:
@@ -1126,9 +1109,9 @@ class S3Operations(BaseOperation):
1126
1109
  ) -> List[OperationResult]:
1127
1110
  """
1128
1111
  Analyze lifecycle compliance across S3 buckets and provide cost optimization recommendations.
1129
-
1112
+
1130
1113
  Args:
1131
- context: Operation context
1114
+ context: Operation context
1132
1115
  region: AWS region to analyze (if None, analyzes all regions)
1133
1116
 
1134
1117
  Returns:
@@ -1139,41 +1122,47 @@ class S3Operations(BaseOperation):
1139
1122
  )
1140
1123
 
1141
1124
  try:
1142
- console.print("[bold blue]📊 Analyzing S3 lifecycle compliance and cost optimization opportunities...[/bold blue]")
1143
-
1125
+ console.print(
1126
+ "[bold blue]📊 Analyzing S3 lifecycle compliance and cost optimization opportunities...[/bold blue]"
1127
+ )
1128
+
1144
1129
  # Find buckets without lifecycle policies
1145
1130
  find_results = self.find_buckets_without_lifecycle(context, region=region)
1146
-
1131
+
1147
1132
  if not find_results or not find_results[0].success:
1148
1133
  result.mark_completed(OperationStatus.FAILED, "Failed to analyze buckets")
1149
1134
  return [result]
1150
-
1135
+
1151
1136
  buckets_data = find_results[0].response_data
1152
1137
  non_compliant_buckets = buckets_data.get("buckets_without_lifecycle", [])
1153
1138
  total_buckets_scanned = buckets_data.get("total_count", 0)
1154
-
1139
+
1155
1140
  # Calculate compliance metrics
1156
1141
  s3_client = self.get_client("s3")
1157
-
1142
+
1158
1143
  # Get total bucket count for compliance percentage
1159
1144
  list_response = self.execute_aws_call(s3_client, "list_buckets")
1160
1145
  total_buckets = len(list_response.get("Buckets", []))
1161
-
1162
- compliance_percentage = ((total_buckets - len(non_compliant_buckets)) / total_buckets * 100) if total_buckets > 0 else 100
1163
-
1146
+
1147
+ compliance_percentage = (
1148
+ ((total_buckets - len(non_compliant_buckets)) / total_buckets * 100) if total_buckets > 0 else 100
1149
+ )
1150
+
1164
1151
  # Generate recommendations
1165
1152
  recommendations = []
1166
1153
  potential_savings = 0
1167
-
1154
+
1168
1155
  if non_compliant_buckets:
1169
- recommendations.extend([
1170
- "🎯 Implement lifecycle policies to automatically transition objects to cheaper storage classes",
1171
- "💰 Configure automatic deletion of old object versions to reduce storage costs",
1172
- "📈 Set up transitions: Standard IA (30 days) Glacier (90 days) → Deep Archive (365 days)",
1173
- "🔧 Use prefixes to apply different policies to different object types",
1174
- "📊 Monitor lifecycle rule effectiveness with CloudWatch metrics"
1175
- ])
1176
-
1156
+ recommendations.extend(
1157
+ [
1158
+ "🎯 Implement lifecycle policies to automatically transition objects to cheaper storage classes",
1159
+ "💰 Configure automatic deletion of old object versions to reduce storage costs",
1160
+ "📈 Set up transitions: Standard IA (30 days) Glacier (90 days) → Deep Archive (365 days)",
1161
+ "🔧 Use prefixes to apply different policies to different object types",
1162
+ "📊 Monitor lifecycle rule effectiveness with CloudWatch metrics",
1163
+ ]
1164
+ )
1165
+
1177
1166
  # Estimate potential savings (rough calculation)
1178
1167
  estimated_savings_per_bucket = 25 # Estimated 25% savings per bucket
1179
1168
  potential_savings = len(non_compliant_buckets) * estimated_savings_per_bucket
@@ -1181,26 +1170,26 @@ class S3Operations(BaseOperation):
1181
1170
  # Create summary report
1182
1171
  from rich.panel import Panel
1183
1172
  from rich.table import Table
1184
-
1173
+
1185
1174
  # Compliance summary table
1186
1175
  summary_table = Table(show_header=True, header_style="bold magenta")
1187
1176
  summary_table.add_column("Metric", style="cyan")
1188
1177
  summary_table.add_column("Value", style="green")
1189
-
1178
+
1190
1179
  summary_table.add_row("Total Buckets", str(total_buckets))
1191
1180
  summary_table.add_row("Compliant Buckets", str(total_buckets - len(non_compliant_buckets)))
1192
1181
  summary_table.add_row("Non-Compliant Buckets", str(len(non_compliant_buckets)))
1193
1182
  summary_table.add_row("Compliance Percentage", f"{compliance_percentage:.1f}%")
1194
1183
  summary_table.add_row("Potential Cost Savings", f"~{potential_savings}%")
1195
-
1184
+
1196
1185
  console.print(Panel(summary_table, title="S3 Lifecycle Compliance Report", border_style="blue"))
1197
-
1186
+
1198
1187
  # Display recommendations if any
1199
1188
  if recommendations:
1200
1189
  console.print("\n[bold yellow]💡 Cost Optimization Recommendations:[/bold yellow]")
1201
1190
  for i, rec in enumerate(recommendations, 1):
1202
1191
  console.print(f" {i}. {rec}")
1203
-
1192
+
1204
1193
  # Compliance status color coding
1205
1194
  if compliance_percentage >= 90:
1206
1195
  compliance_status = "[bold green]EXCELLENT[/bold green]"
@@ -1210,9 +1199,9 @@ class S3Operations(BaseOperation):
1210
1199
  compliance_status = "[bold orange]NEEDS IMPROVEMENT[/bold orange]"
1211
1200
  else:
1212
1201
  compliance_status = "[bold red]POOR[/bold red]"
1213
-
1202
+
1214
1203
  console.print(f"\n[bold blue]Overall Compliance Status: {compliance_status}[/bold blue]")
1215
-
1204
+
1216
1205
  result.response_data = {
1217
1206
  "compliance_percentage": compliance_percentage,
1218
1207
  "total_buckets": total_buckets,
@@ -1221,7 +1210,7 @@ class S3Operations(BaseOperation):
1221
1210
  "non_compliant_details": non_compliant_buckets,
1222
1211
  "recommendations": recommendations,
1223
1212
  "potential_savings_percentage": potential_savings,
1224
- "compliance_status": compliance_status.replace("[bold ", "").replace("[/bold ", "").replace("]", "")
1213
+ "compliance_status": compliance_status.replace("[bold ", "").replace("[/bold ", "").replace("]", ""),
1225
1214
  }
1226
1215
  result.mark_completed(OperationStatus.SUCCESS)
1227
1216
 
@@ -18,7 +18,7 @@ Features:
18
18
 
19
19
  Author: CloudOps Runbooks Team
20
20
  Version: 0.7.8
21
- Enhanced for Sprint 2 VPC Scope Expansion
21
+ Enhanced for Phase 2 VPC Scope Expansion
22
22
  """
23
23
 
24
24
  import json