runbooks 1.1.4__py3-none-any.whl → 1.1.6__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 (273) 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/assessment/compliance.py +1 -1
  8. runbooks/cfat/assessment/runner.py +1 -0
  9. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  10. runbooks/cli/__init__.py +1 -1
  11. runbooks/cli/commands/cfat.py +64 -23
  12. runbooks/cli/commands/finops.py +1005 -54
  13. runbooks/cli/commands/inventory.py +135 -91
  14. runbooks/cli/commands/operate.py +9 -36
  15. runbooks/cli/commands/security.py +42 -18
  16. runbooks/cli/commands/validation.py +432 -18
  17. runbooks/cli/commands/vpc.py +81 -17
  18. runbooks/cli/registry.py +22 -10
  19. runbooks/cloudops/__init__.py +20 -27
  20. runbooks/cloudops/base.py +96 -107
  21. runbooks/cloudops/cost_optimizer.py +544 -542
  22. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  23. runbooks/cloudops/interfaces.py +224 -225
  24. runbooks/cloudops/lifecycle_manager.py +5 -4
  25. runbooks/cloudops/mcp_cost_validation.py +252 -235
  26. runbooks/cloudops/models.py +78 -53
  27. runbooks/cloudops/monitoring_automation.py +5 -4
  28. runbooks/cloudops/notebook_framework.py +177 -213
  29. runbooks/cloudops/security_enforcer.py +125 -159
  30. runbooks/common/accuracy_validator.py +17 -12
  31. runbooks/common/aws_pricing.py +349 -326
  32. runbooks/common/aws_pricing_api.py +211 -212
  33. runbooks/common/aws_profile_manager.py +40 -36
  34. runbooks/common/aws_utils.py +74 -79
  35. runbooks/common/business_logic.py +126 -104
  36. runbooks/common/cli_decorators.py +36 -60
  37. runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
  38. runbooks/common/cross_account_manager.py +197 -204
  39. runbooks/common/date_utils.py +27 -39
  40. runbooks/common/decorators.py +29 -19
  41. runbooks/common/dry_run_examples.py +173 -208
  42. runbooks/common/dry_run_framework.py +157 -155
  43. runbooks/common/enhanced_exception_handler.py +15 -4
  44. runbooks/common/enhanced_logging_example.py +50 -64
  45. runbooks/common/enhanced_logging_integration_example.py +65 -37
  46. runbooks/common/env_utils.py +16 -16
  47. runbooks/common/error_handling.py +40 -38
  48. runbooks/common/lazy_loader.py +41 -23
  49. runbooks/common/logging_integration_helper.py +79 -86
  50. runbooks/common/mcp_cost_explorer_integration.py +476 -493
  51. runbooks/common/mcp_integration.py +99 -79
  52. runbooks/common/memory_optimization.py +140 -118
  53. runbooks/common/module_cli_base.py +37 -58
  54. runbooks/common/organizations_client.py +175 -193
  55. runbooks/common/patterns.py +23 -25
  56. runbooks/common/performance_monitoring.py +67 -71
  57. runbooks/common/performance_optimization_engine.py +283 -274
  58. runbooks/common/profile_utils.py +111 -37
  59. runbooks/common/rich_utils.py +315 -141
  60. runbooks/common/sre_performance_suite.py +177 -186
  61. runbooks/enterprise/__init__.py +1 -1
  62. runbooks/enterprise/logging.py +144 -106
  63. runbooks/enterprise/security.py +187 -204
  64. runbooks/enterprise/validation.py +43 -56
  65. runbooks/finops/__init__.py +26 -30
  66. runbooks/finops/account_resolver.py +1 -1
  67. runbooks/finops/advanced_optimization_engine.py +980 -0
  68. runbooks/finops/automation_core.py +268 -231
  69. runbooks/finops/business_case_config.py +184 -179
  70. runbooks/finops/cli.py +660 -139
  71. runbooks/finops/commvault_ec2_analysis.py +157 -164
  72. runbooks/finops/compute_cost_optimizer.py +336 -320
  73. runbooks/finops/config.py +20 -20
  74. runbooks/finops/cost_optimizer.py +484 -618
  75. runbooks/finops/cost_processor.py +332 -214
  76. runbooks/finops/dashboard_runner.py +1006 -172
  77. runbooks/finops/ebs_cost_optimizer.py +991 -657
  78. runbooks/finops/elastic_ip_optimizer.py +317 -257
  79. runbooks/finops/enhanced_mcp_integration.py +340 -0
  80. runbooks/finops/enhanced_progress.py +32 -29
  81. runbooks/finops/enhanced_trend_visualization.py +3 -2
  82. runbooks/finops/enterprise_wrappers.py +223 -285
  83. runbooks/finops/executive_export.py +203 -160
  84. runbooks/finops/helpers.py +130 -288
  85. runbooks/finops/iam_guidance.py +1 -1
  86. runbooks/finops/infrastructure/__init__.py +80 -0
  87. runbooks/finops/infrastructure/commands.py +506 -0
  88. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  89. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  90. runbooks/finops/markdown_exporter.py +337 -174
  91. runbooks/finops/mcp_validator.py +1952 -0
  92. runbooks/finops/nat_gateway_optimizer.py +1512 -481
  93. runbooks/finops/network_cost_optimizer.py +657 -587
  94. runbooks/finops/notebook_utils.py +226 -188
  95. runbooks/finops/optimization_engine.py +1136 -0
  96. runbooks/finops/optimizer.py +19 -23
  97. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  98. runbooks/finops/reservation_optimizer.py +427 -363
  99. runbooks/finops/scenario_cli_integration.py +64 -65
  100. runbooks/finops/scenarios.py +1277 -438
  101. runbooks/finops/schemas.py +218 -182
  102. runbooks/finops/snapshot_manager.py +2289 -0
  103. runbooks/finops/types.py +3 -3
  104. runbooks/finops/validation_framework.py +259 -265
  105. runbooks/finops/vpc_cleanup_exporter.py +189 -144
  106. runbooks/finops/vpc_cleanup_optimizer.py +591 -573
  107. runbooks/finops/workspaces_analyzer.py +171 -182
  108. runbooks/integration/__init__.py +89 -0
  109. runbooks/integration/mcp_integration.py +1920 -0
  110. runbooks/inventory/CLAUDE.md +816 -0
  111. runbooks/inventory/__init__.py +2 -2
  112. runbooks/inventory/aws_decorators.py +2 -3
  113. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  114. runbooks/inventory/check_controltower_readiness.py +152 -151
  115. runbooks/inventory/check_landingzone_readiness.py +85 -84
  116. runbooks/inventory/cloud_foundations_integration.py +144 -149
  117. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  118. runbooks/inventory/collectors/aws_networking.py +109 -99
  119. runbooks/inventory/collectors/base.py +4 -0
  120. runbooks/inventory/core/collector.py +495 -313
  121. runbooks/inventory/core/formatter.py +11 -0
  122. runbooks/inventory/draw_org_structure.py +8 -9
  123. runbooks/inventory/drift_detection_cli.py +69 -96
  124. runbooks/inventory/ec2_vpc_utils.py +2 -2
  125. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  126. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  127. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  128. runbooks/inventory/find_ec2_security_groups.py +48 -42
  129. runbooks/inventory/find_landingzone_versions.py +4 -6
  130. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  131. runbooks/inventory/inventory_mcp_cli.py +48 -46
  132. runbooks/inventory/inventory_modules.py +103 -91
  133. runbooks/inventory/list_cfn_stacks.py +9 -10
  134. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  135. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  136. runbooks/inventory/list_cfn_stacksets.py +8 -10
  137. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  138. runbooks/inventory/list_ds_directories.py +65 -53
  139. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  140. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  141. runbooks/inventory/list_ec2_instances.py +23 -28
  142. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  143. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  144. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  145. runbooks/inventory/list_guardduty_detectors.py +2 -4
  146. runbooks/inventory/list_iam_policies.py +2 -4
  147. runbooks/inventory/list_iam_roles.py +5 -7
  148. runbooks/inventory/list_iam_saml_providers.py +4 -6
  149. runbooks/inventory/list_lambda_functions.py +38 -38
  150. runbooks/inventory/list_org_accounts.py +6 -8
  151. runbooks/inventory/list_org_accounts_users.py +55 -44
  152. runbooks/inventory/list_rds_db_instances.py +31 -33
  153. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  154. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  155. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  156. runbooks/inventory/list_sns_topics.py +2 -4
  157. runbooks/inventory/list_ssm_parameters.py +4 -7
  158. runbooks/inventory/list_vpc_subnets.py +2 -4
  159. runbooks/inventory/list_vpcs.py +7 -10
  160. runbooks/inventory/mcp_inventory_validator.py +554 -468
  161. runbooks/inventory/mcp_vpc_validator.py +359 -442
  162. runbooks/inventory/organizations_discovery.py +63 -55
  163. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  164. runbooks/inventory/requirements.txt +0 -1
  165. runbooks/inventory/rich_inventory_display.py +35 -34
  166. runbooks/inventory/run_on_multi_accounts.py +3 -5
  167. runbooks/inventory/unified_validation_engine.py +281 -253
  168. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  169. runbooks/inventory/vpc_analyzer.py +735 -697
  170. runbooks/inventory/vpc_architecture_validator.py +293 -348
  171. runbooks/inventory/vpc_dependency_analyzer.py +384 -380
  172. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  173. runbooks/main.py +49 -34
  174. runbooks/main_final.py +91 -60
  175. runbooks/main_minimal.py +22 -10
  176. runbooks/main_optimized.py +131 -100
  177. runbooks/main_ultra_minimal.py +7 -2
  178. runbooks/mcp/__init__.py +36 -0
  179. runbooks/mcp/integration.py +679 -0
  180. runbooks/monitoring/performance_monitor.py +9 -4
  181. runbooks/operate/dynamodb_operations.py +3 -1
  182. runbooks/operate/ec2_operations.py +145 -137
  183. runbooks/operate/iam_operations.py +146 -152
  184. runbooks/operate/networking_cost_heatmap.py +29 -8
  185. runbooks/operate/rds_operations.py +223 -254
  186. runbooks/operate/s3_operations.py +107 -118
  187. runbooks/operate/vpc_operations.py +646 -616
  188. runbooks/remediation/base.py +1 -1
  189. runbooks/remediation/commons.py +10 -7
  190. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  191. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  192. runbooks/remediation/multi_account.py +24 -21
  193. runbooks/remediation/rds_snapshot_list.py +86 -60
  194. runbooks/remediation/remediation_cli.py +92 -146
  195. runbooks/remediation/universal_account_discovery.py +83 -79
  196. runbooks/remediation/workspaces_list.py +46 -41
  197. runbooks/security/__init__.py +19 -0
  198. runbooks/security/assessment_runner.py +1150 -0
  199. runbooks/security/baseline_checker.py +812 -0
  200. runbooks/security/cloudops_automation_security_validator.py +509 -535
  201. runbooks/security/compliance_automation_engine.py +17 -17
  202. runbooks/security/config/__init__.py +2 -2
  203. runbooks/security/config/compliance_config.py +50 -50
  204. runbooks/security/config_template_generator.py +63 -76
  205. runbooks/security/enterprise_security_framework.py +1 -1
  206. runbooks/security/executive_security_dashboard.py +519 -508
  207. runbooks/security/multi_account_security_controls.py +959 -1210
  208. runbooks/security/real_time_security_monitor.py +422 -444
  209. runbooks/security/security_baseline_tester.py +1 -1
  210. runbooks/security/security_cli.py +143 -112
  211. runbooks/security/test_2way_validation.py +439 -0
  212. runbooks/security/two_way_validation_framework.py +852 -0
  213. runbooks/sre/production_monitoring_framework.py +167 -177
  214. runbooks/tdd/__init__.py +15 -0
  215. runbooks/tdd/cli.py +1071 -0
  216. runbooks/utils/__init__.py +14 -17
  217. runbooks/utils/logger.py +7 -2
  218. runbooks/utils/version_validator.py +50 -47
  219. runbooks/validation/__init__.py +6 -6
  220. runbooks/validation/cli.py +9 -3
  221. runbooks/validation/comprehensive_2way_validator.py +745 -704
  222. runbooks/validation/mcp_validator.py +906 -228
  223. runbooks/validation/terraform_citations_validator.py +104 -115
  224. runbooks/validation/terraform_drift_detector.py +461 -454
  225. runbooks/vpc/README.md +617 -0
  226. runbooks/vpc/__init__.py +8 -1
  227. runbooks/vpc/analyzer.py +577 -0
  228. runbooks/vpc/cleanup_wrapper.py +476 -413
  229. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  230. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  231. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  232. runbooks/vpc/config.py +92 -97
  233. runbooks/vpc/cost_engine.py +411 -148
  234. runbooks/vpc/cost_explorer_integration.py +553 -0
  235. runbooks/vpc/cross_account_session.py +101 -106
  236. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  237. runbooks/vpc/eni_gate_validator.py +961 -0
  238. runbooks/vpc/heatmap_engine.py +185 -160
  239. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  240. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  241. runbooks/vpc/networking_wrapper.py +15 -8
  242. runbooks/vpc/pdca_remediation_planner.py +528 -0
  243. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  244. runbooks/vpc/runbooks_adapter.py +1167 -241
  245. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  246. runbooks/vpc/test_data_loader.py +358 -0
  247. runbooks/vpc/tests/conftest.py +314 -4
  248. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  249. runbooks/vpc/tests/test_cost_engine.py +0 -2
  250. runbooks/vpc/topology_generator.py +326 -0
  251. runbooks/vpc/unified_scenarios.py +1297 -1124
  252. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  253. runbooks-1.1.6.dist-info/METADATA +327 -0
  254. runbooks-1.1.6.dist-info/RECORD +489 -0
  255. runbooks/finops/README.md +0 -414
  256. runbooks/finops/accuracy_cross_validator.py +0 -647
  257. runbooks/finops/business_cases.py +0 -950
  258. runbooks/finops/dashboard_router.py +0 -922
  259. runbooks/finops/ebs_optimizer.py +0 -973
  260. runbooks/finops/embedded_mcp_validator.py +0 -1629
  261. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  262. runbooks/finops/finops_dashboard.py +0 -584
  263. runbooks/finops/finops_scenarios.py +0 -1218
  264. runbooks/finops/legacy_migration.py +0 -730
  265. runbooks/finops/multi_dashboard.py +0 -1519
  266. runbooks/finops/single_dashboard.py +0 -1113
  267. runbooks/finops/unlimited_scenarios.py +0 -393
  268. runbooks-1.1.4.dist-info/METADATA +0 -800
  269. runbooks-1.1.4.dist-info/RECORD +0 -468
  270. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
  271. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
  272. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
  273. {runbooks-1.1.4.dist-info → runbooks-1.1.6.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