runbooks 0.2.5__py3-none-any.whl → 0.7.0__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 (249) hide show
  1. conftest.py +26 -0
  2. jupyter-agent/.env +2 -0
  3. jupyter-agent/.env.template +2 -0
  4. jupyter-agent/.gitattributes +35 -0
  5. jupyter-agent/.gradio/certificate.pem +31 -0
  6. jupyter-agent/README.md +16 -0
  7. jupyter-agent/__main__.log +8 -0
  8. jupyter-agent/app.py +256 -0
  9. jupyter-agent/cloudops-agent.png +0 -0
  10. jupyter-agent/ds-system-prompt.txt +154 -0
  11. jupyter-agent/jupyter-agent.png +0 -0
  12. jupyter-agent/llama3_template.jinja +123 -0
  13. jupyter-agent/requirements.txt +9 -0
  14. jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
  15. jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
  16. jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
  17. jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
  18. jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
  19. jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
  20. jupyter-agent/utils.py +409 -0
  21. runbooks/__init__.py +71 -3
  22. runbooks/__main__.py +13 -0
  23. runbooks/aws/ec2_describe_instances.py +1 -1
  24. runbooks/aws/ec2_run_instances.py +8 -2
  25. runbooks/aws/ec2_start_stop_instances.py +17 -4
  26. runbooks/aws/ec2_unused_volumes.py +5 -1
  27. runbooks/aws/s3_create_bucket.py +4 -2
  28. runbooks/aws/s3_list_objects.py +6 -1
  29. runbooks/aws/tagging_lambda_handler.py +13 -2
  30. runbooks/aws/tags.json +12 -0
  31. runbooks/base.py +353 -0
  32. runbooks/cfat/README.md +49 -0
  33. runbooks/cfat/__init__.py +74 -0
  34. runbooks/cfat/app.ts +644 -0
  35. runbooks/cfat/assessment/__init__.py +40 -0
  36. runbooks/cfat/assessment/asana-import.csv +39 -0
  37. runbooks/cfat/assessment/cfat-checks.csv +31 -0
  38. runbooks/cfat/assessment/cfat.txt +520 -0
  39. runbooks/cfat/assessment/collectors.py +200 -0
  40. runbooks/cfat/assessment/jira-import.csv +39 -0
  41. runbooks/cfat/assessment/runner.py +387 -0
  42. runbooks/cfat/assessment/validators.py +290 -0
  43. runbooks/cfat/cli.py +103 -0
  44. runbooks/cfat/docs/asana-import.csv +24 -0
  45. runbooks/cfat/docs/cfat-checks.csv +31 -0
  46. runbooks/cfat/docs/cfat.txt +335 -0
  47. runbooks/cfat/docs/checks-output.png +0 -0
  48. runbooks/cfat/docs/cloudshell-console-run.png +0 -0
  49. runbooks/cfat/docs/cloudshell-download.png +0 -0
  50. runbooks/cfat/docs/cloudshell-output.png +0 -0
  51. runbooks/cfat/docs/downloadfile.png +0 -0
  52. runbooks/cfat/docs/jira-import.csv +24 -0
  53. runbooks/cfat/docs/open-cloudshell.png +0 -0
  54. runbooks/cfat/docs/report-header.png +0 -0
  55. runbooks/cfat/models.py +1026 -0
  56. runbooks/cfat/package-lock.json +5116 -0
  57. runbooks/cfat/package.json +38 -0
  58. runbooks/cfat/report.py +496 -0
  59. runbooks/cfat/reporting/__init__.py +46 -0
  60. runbooks/cfat/reporting/exporters.py +337 -0
  61. runbooks/cfat/reporting/formatters.py +496 -0
  62. runbooks/cfat/reporting/templates.py +135 -0
  63. runbooks/cfat/run-assessment.sh +23 -0
  64. runbooks/cfat/runner.py +69 -0
  65. runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
  66. runbooks/cfat/src/actions/check-config-existence.ts +37 -0
  67. runbooks/cfat/src/actions/check-control-tower.ts +37 -0
  68. runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
  69. runbooks/cfat/src/actions/check-iam-users.ts +50 -0
  70. runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
  71. runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
  72. runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
  73. runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
  74. runbooks/cfat/src/actions/create-backlog.ts +372 -0
  75. runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
  76. runbooks/cfat/src/actions/create-report.ts +616 -0
  77. runbooks/cfat/src/actions/define-account-type.ts +51 -0
  78. runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
  79. runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
  80. runbooks/cfat/src/actions/get-idc-info.ts +34 -0
  81. runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
  82. runbooks/cfat/src/actions/get-org-details.ts +35 -0
  83. runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
  84. runbooks/cfat/src/actions/get-org-ous.ts +35 -0
  85. runbooks/cfat/src/actions/get-regions.ts +22 -0
  86. runbooks/cfat/src/actions/zip-assessment.ts +27 -0
  87. runbooks/cfat/src/types/index.d.ts +147 -0
  88. runbooks/cfat/tests/__init__.py +141 -0
  89. runbooks/cfat/tests/test_cli.py +340 -0
  90. runbooks/cfat/tests/test_integration.py +290 -0
  91. runbooks/cfat/tests/test_models.py +505 -0
  92. runbooks/cfat/tests/test_reporting.py +354 -0
  93. runbooks/cfat/tsconfig.json +16 -0
  94. runbooks/cfat/webpack.config.cjs +27 -0
  95. runbooks/config.py +260 -0
  96. runbooks/finops/README.md +337 -0
  97. runbooks/finops/__init__.py +86 -0
  98. runbooks/finops/aws_client.py +245 -0
  99. runbooks/finops/cli.py +151 -0
  100. runbooks/finops/cost_processor.py +410 -0
  101. runbooks/finops/dashboard_runner.py +448 -0
  102. runbooks/finops/helpers.py +355 -0
  103. runbooks/finops/main.py +14 -0
  104. runbooks/finops/profile_processor.py +174 -0
  105. runbooks/finops/types.py +66 -0
  106. runbooks/finops/visualisations.py +80 -0
  107. runbooks/inventory/.gitignore +354 -0
  108. runbooks/inventory/ArgumentsClass.py +261 -0
  109. runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
  110. runbooks/inventory/Inventory_Modules.py +6130 -0
  111. runbooks/inventory/LandingZone/delete_lz.py +1075 -0
  112. runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -0
  113. runbooks/inventory/README.md +1320 -0
  114. runbooks/inventory/__init__.py +62 -0
  115. runbooks/inventory/account_class.py +532 -0
  116. runbooks/inventory/all_my_instances_wrapper.py +123 -0
  117. runbooks/inventory/aws_decorators.py +201 -0
  118. runbooks/inventory/aws_organization.png +0 -0
  119. runbooks/inventory/cfn_move_stack_instances.py +1526 -0
  120. runbooks/inventory/check_cloudtrail_compliance.py +614 -0
  121. runbooks/inventory/check_controltower_readiness.py +1107 -0
  122. runbooks/inventory/check_landingzone_readiness.py +711 -0
  123. runbooks/inventory/cloudtrail.md +727 -0
  124. runbooks/inventory/collectors/__init__.py +20 -0
  125. runbooks/inventory/collectors/aws_compute.py +518 -0
  126. runbooks/inventory/collectors/aws_networking.py +275 -0
  127. runbooks/inventory/collectors/base.py +222 -0
  128. runbooks/inventory/core/__init__.py +19 -0
  129. runbooks/inventory/core/collector.py +303 -0
  130. runbooks/inventory/core/formatter.py +296 -0
  131. runbooks/inventory/delete_s3_buckets_objects.py +169 -0
  132. runbooks/inventory/discovery.md +81 -0
  133. runbooks/inventory/draw_org_structure.py +748 -0
  134. runbooks/inventory/ec2_vpc_utils.py +341 -0
  135. runbooks/inventory/find_cfn_drift_detection.py +272 -0
  136. runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
  137. runbooks/inventory/find_cfn_stackset_drift.py +733 -0
  138. runbooks/inventory/find_ec2_security_groups.py +669 -0
  139. runbooks/inventory/find_landingzone_versions.py +201 -0
  140. runbooks/inventory/find_vpc_flow_logs.py +1221 -0
  141. runbooks/inventory/inventory.sh +659 -0
  142. runbooks/inventory/list_cfn_stacks.py +558 -0
  143. runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
  144. runbooks/inventory/list_cfn_stackset_operations.py +734 -0
  145. runbooks/inventory/list_cfn_stacksets.py +453 -0
  146. runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
  147. runbooks/inventory/list_ds_directories.py +354 -0
  148. runbooks/inventory/list_ec2_availability_zones.py +286 -0
  149. runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
  150. runbooks/inventory/list_ec2_instances.py +425 -0
  151. runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
  152. runbooks/inventory/list_elbs_load_balancers.py +411 -0
  153. runbooks/inventory/list_enis_network_interfaces.py +526 -0
  154. runbooks/inventory/list_guardduty_detectors.py +568 -0
  155. runbooks/inventory/list_iam_policies.py +404 -0
  156. runbooks/inventory/list_iam_roles.py +518 -0
  157. runbooks/inventory/list_iam_saml_providers.py +359 -0
  158. runbooks/inventory/list_lambda_functions.py +882 -0
  159. runbooks/inventory/list_org_accounts.py +446 -0
  160. runbooks/inventory/list_org_accounts_users.py +354 -0
  161. runbooks/inventory/list_rds_db_instances.py +406 -0
  162. runbooks/inventory/list_route53_hosted_zones.py +318 -0
  163. runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
  164. runbooks/inventory/list_sns_topics.py +360 -0
  165. runbooks/inventory/list_ssm_parameters.py +402 -0
  166. runbooks/inventory/list_vpc_subnets.py +433 -0
  167. runbooks/inventory/list_vpcs.py +422 -0
  168. runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
  169. runbooks/inventory/models/__init__.py +24 -0
  170. runbooks/inventory/models/account.py +192 -0
  171. runbooks/inventory/models/inventory.py +309 -0
  172. runbooks/inventory/models/resource.py +247 -0
  173. runbooks/inventory/recover_cfn_stack_ids.py +205 -0
  174. runbooks/inventory/requirements.txt +12 -0
  175. runbooks/inventory/run_on_multi_accounts.py +211 -0
  176. runbooks/inventory/tests/common_test_data.py +3661 -0
  177. runbooks/inventory/tests/common_test_functions.py +204 -0
  178. runbooks/inventory/tests/setup.py +24 -0
  179. runbooks/inventory/tests/src.py +18 -0
  180. runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
  181. runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
  182. runbooks/inventory/tests/test_inventory_modules.py +55 -0
  183. runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
  184. runbooks/inventory/tests/test_moto_integration_example.py +273 -0
  185. runbooks/inventory/tests/test_org_list_accounts.py +49 -0
  186. runbooks/inventory/update_aws_actions.py +173 -0
  187. runbooks/inventory/update_cfn_stacksets.py +1215 -0
  188. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
  189. runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
  190. runbooks/inventory/update_s3_public_access_block.py +539 -0
  191. runbooks/inventory/utils/__init__.py +23 -0
  192. runbooks/inventory/utils/aws_helpers.py +510 -0
  193. runbooks/inventory/utils/threading_utils.py +493 -0
  194. runbooks/inventory/utils/validation.py +682 -0
  195. runbooks/inventory/verify_ec2_security_groups.py +1430 -0
  196. runbooks/main.py +1004 -0
  197. runbooks/organizations/__init__.py +12 -0
  198. runbooks/organizations/manager.py +374 -0
  199. runbooks/security/README.md +447 -0
  200. runbooks/security/__init__.py +71 -0
  201. runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
  202. runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
  203. runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
  204. runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
  205. runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
  206. runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
  207. runbooks/{security_baseline → security}/config-origin.json +1 -1
  208. runbooks/{security_baseline → security}/config.json +1 -1
  209. runbooks/{security_baseline → security}/permission.json +1 -1
  210. runbooks/{security_baseline → security}/report_generator.py +10 -2
  211. runbooks/{security_baseline → security}/report_template_en.html +7 -7
  212. runbooks/{security_baseline → security}/report_template_jp.html +7 -7
  213. runbooks/{security_baseline → security}/report_template_kr.html +12 -12
  214. runbooks/{security_baseline → security}/report_template_vn.html +7 -7
  215. runbooks/{security_baseline → security}/run_script.py +8 -2
  216. runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
  217. runbooks/{security_baseline → security}/utils/common.py +5 -1
  218. runbooks/utils/__init__.py +204 -0
  219. runbooks-0.7.0.dist-info/METADATA +375 -0
  220. runbooks-0.7.0.dist-info/RECORD +249 -0
  221. {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
  222. runbooks-0.7.0.dist-info/entry_points.txt +7 -0
  223. runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
  224. runbooks-0.7.0.dist-info/top_level.txt +3 -0
  225. runbooks/python101/calculator.py +0 -34
  226. runbooks/python101/config.py +0 -1
  227. runbooks/python101/exceptions.py +0 -16
  228. runbooks/python101/file_manager.py +0 -218
  229. runbooks/python101/toolkit.py +0 -153
  230. runbooks-0.2.5.dist-info/METADATA +0 -439
  231. runbooks-0.2.5.dist-info/RECORD +0 -61
  232. runbooks-0.2.5.dist-info/entry_points.txt +0 -3
  233. runbooks-0.2.5.dist-info/top_level.txt +0 -1
  234. /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
  235. /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
  236. /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
  237. /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
  238. /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
  239. /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
  240. /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
  241. /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
  242. /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
  243. /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
  244. /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
  245. /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
  246. /runbooks/{security_baseline → security}/utils/enums.py +0 -0
  247. /runbooks/{security_baseline → security}/utils/language.py +0 -0
  248. /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
  249. /runbooks/{security_baseline → security}/utils/permission_list.py +0 -0
runbooks/main.py ADDED
@@ -0,0 +1,1004 @@
1
+ """
2
+ CloudOps Runbooks - Main CLI entry point for all runbook commands.
3
+
4
+ This module provides the command-line interface for CloudOps automation,
5
+ integrating AWS Cloud Foundations best practices with operational runbooks.
6
+
7
+ Following KISS principle: this is the main entry point combining both CLI logic and main execution.
8
+ """
9
+
10
+ import sys
11
+ from datetime import datetime
12
+ from pathlib import Path
13
+ from typing import Optional
14
+
15
+ import click
16
+ from loguru import logger
17
+
18
+ try:
19
+ from rich.console import Console
20
+ from rich.table import Table
21
+
22
+ _HAS_RICH = True
23
+ except ImportError:
24
+ _HAS_RICH = False
25
+
26
+ # Simple fallback console
27
+ class Console:
28
+ def print(self, *args, **kwargs):
29
+ print(*args)
30
+
31
+ def status(self, message):
32
+ print(f"Status: {message}")
33
+ return self
34
+
35
+ def __enter__(self):
36
+ return self
37
+
38
+ def __exit__(self, *args):
39
+ pass
40
+
41
+ class Table:
42
+ def __init__(self, title=""):
43
+ self.title = title
44
+ self.columns = []
45
+ self.rows = []
46
+
47
+ def add_column(self, name, style=""):
48
+ self.columns.append(name)
49
+
50
+ def add_row(self, *args):
51
+ self.rows.append(args)
52
+
53
+ def __str__(self):
54
+ if not self.columns:
55
+ return ""
56
+
57
+ # Simple text table
58
+ output = f"\n{self.title}\n" + "=" * len(self.title) + "\n"
59
+ output += " | ".join(self.columns) + "\n"
60
+ output += "-" * (len(" | ".join(self.columns))) + "\n"
61
+
62
+ for row in self.rows:
63
+ output += " | ".join(str(cell) for cell in row) + "\n"
64
+
65
+ return output
66
+
67
+
68
+ from runbooks import __version__
69
+ from runbooks.cfat.runner import AssessmentRunner
70
+ from runbooks.config import load_config, save_config
71
+ from runbooks.inventory.core.collector import InventoryCollector
72
+ from runbooks.organizations.manager import OUManager
73
+ from runbooks.utils import setup_logging
74
+
75
+ console = Console()
76
+
77
+
78
+ @click.group(invoke_without_command=True)
79
+ @click.version_option(version=__version__)
80
+ @click.option("--debug", is_flag=True, help="Enable debug logging")
81
+ @click.option("--profile", default="default", help="AWS profile to use")
82
+ @click.option("--region", help="AWS region (overrides profile region)")
83
+ @click.option("--config", type=click.Path(), help="Configuration file path")
84
+ @click.pass_context
85
+ def main(ctx, debug, profile, region, config):
86
+ """
87
+ CloudOps Runbooks - Enterprise CloudOps Automation Toolkit.
88
+
89
+ This tool provides comprehensive AWS automation capabilities including:
90
+ - Cloud Foundations Assessment Tool (CFAT)
91
+ - Multi-account resource inventory
92
+ - Organization management
93
+ - Control Tower automation
94
+ - Identity and access management
95
+ - Centralized logging setup
96
+
97
+ Use 'runbooks COMMAND --help' for more information on specific commands.
98
+ """
99
+ # Initialize context
100
+ ctx.ensure_object(dict)
101
+ ctx.obj["debug"] = debug
102
+ ctx.obj["profile"] = profile
103
+ ctx.obj["region"] = region
104
+
105
+ # Setup logging
106
+ setup_logging(debug=debug)
107
+
108
+ # Load configuration
109
+ config_path = Path(config) if config else Path.home() / ".runbooks" / "config.yaml"
110
+ ctx.obj["config"] = load_config(config_path)
111
+
112
+ # Show help if no command provided
113
+ if ctx.invoked_subcommand is None:
114
+ click.echo(ctx.get_help())
115
+
116
+
117
+ # ============================================================================
118
+ # CFAT Commands
119
+ # ============================================================================
120
+
121
+
122
+ @main.group()
123
+ @click.pass_context
124
+ def cfat(ctx):
125
+ """Cloud Foundations Assessment Tool - Assess AWS account configuration."""
126
+ pass
127
+
128
+
129
+ @cfat.command()
130
+ @click.option(
131
+ "--output",
132
+ type=click.Choice(["console", "html", "csv", "json", "markdown", "all"]),
133
+ default="console",
134
+ help="Output format (use 'all' for multiple formats)",
135
+ )
136
+ @click.option("--output-file", type=click.Path(), help="Output file path (auto-generated if not specified)")
137
+ @click.option("--checks", multiple=True, help="Specific checks to run")
138
+ @click.option("--skip-checks", multiple=True, help="Checks to skip")
139
+ @click.option("--categories", multiple=True, help="Assessment categories to include")
140
+ @click.option("--skip-categories", multiple=True, help="Assessment categories to exclude")
141
+ @click.option("--severity", type=click.Choice(["INFO", "WARNING", "CRITICAL"]), help="Minimum severity level to report")
142
+ @click.option("--parallel/--sequential", default=True, help="Enable/disable parallel execution")
143
+ @click.option("--max-workers", type=int, default=10, help="Maximum parallel workers")
144
+ @click.option("--compliance-framework", help="Target compliance framework (SOC2, PCI-DSS, HIPAA)")
145
+ @click.option("--export-jira", type=click.Path(), help="Export findings to Jira CSV format")
146
+ @click.option("--export-asana", type=click.Path(), help="Export findings to Asana CSV format")
147
+ @click.option("--export-servicenow", type=click.Path(), help="Export findings to ServiceNow JSON format")
148
+ @click.option("--serve-web", is_flag=True, help="Start web server for interactive reports")
149
+ @click.option("--web-port", type=int, default=8080, help="Port for web server")
150
+ @click.pass_context
151
+ def assess(
152
+ ctx,
153
+ output,
154
+ output_file,
155
+ checks,
156
+ skip_checks,
157
+ categories,
158
+ skip_categories,
159
+ severity,
160
+ parallel,
161
+ max_workers,
162
+ compliance_framework,
163
+ export_jira,
164
+ export_asana,
165
+ export_servicenow,
166
+ serve_web,
167
+ web_port,
168
+ ):
169
+ """
170
+ Run enhanced Cloud Foundations assessment with enterprise features.
171
+
172
+ This command performs a comprehensive assessment of your AWS account
173
+ configuration against Cloud Foundations best practices with advanced
174
+ features including multi-format reporting, parallel execution,
175
+ compliance framework alignment, and project management integration.
176
+
177
+ Examples:
178
+ # Basic assessment with HTML report
179
+ runbooks cfat assess --output html --output-file report.html
180
+
181
+ # Target specific categories and severity
182
+ runbooks cfat assess --categories iam,cloudtrail --severity CRITICAL
183
+
184
+ # Parallel execution with custom workers
185
+ runbooks cfat assess --parallel --max-workers 5
186
+
187
+ # Compliance framework assessment
188
+ runbooks cfat assess --compliance-framework SOC2 --output all
189
+
190
+ # Export to project management tools
191
+ runbooks cfat assess --export-jira jira_tasks.csv --export-asana asana_tasks.csv
192
+
193
+ # Interactive web report
194
+ runbooks cfat assess --serve-web --web-port 8080
195
+ """
196
+ logger.info(f"Starting enhanced Cloud Foundations assessment for profile: {ctx.obj['profile']}")
197
+
198
+ with console.status("[bold green]Running enhanced assessment checks...") as status:
199
+ try:
200
+ # Initialize enhanced assessment runner
201
+ runner = AssessmentRunner(profile=ctx.obj["profile"], region=ctx.obj["region"])
202
+
203
+ # Configure assessment parameters
204
+ if checks:
205
+ runner.set_checks(list(checks))
206
+ if skip_checks:
207
+ runner.skip_checks(list(skip_checks))
208
+ if severity:
209
+ runner.set_min_severity(severity)
210
+
211
+ # Configure categories
212
+ if categories:
213
+ runner.assessment_config.included_categories = list(categories)
214
+ if skip_categories:
215
+ runner.assessment_config.excluded_categories = list(skip_categories)
216
+
217
+ # Configure execution settings
218
+ runner.assessment_config.parallel_execution = parallel
219
+ runner.assessment_config.max_workers = max_workers
220
+
221
+ # Set compliance framework
222
+ if compliance_framework:
223
+ runner.assessment_config.compliance_framework = compliance_framework
224
+
225
+ status.update("[bold green]Executing assessment checks...")
226
+
227
+ # Run assessment
228
+ report = runner.run_assessment()
229
+
230
+ status.update("[bold green]Generating reports...")
231
+
232
+ # Display console summary
233
+ display_assessment_results(report)
234
+
235
+ # Generate output files
236
+ generated_files = []
237
+
238
+ if output == "all":
239
+ # Generate all formats
240
+ timestamp = report.timestamp.strftime("%Y%m%d_%H%M%S")
241
+ base_name = f"cfat_report_{timestamp}"
242
+
243
+ report.to_html(f"{base_name}.html")
244
+ generated_files.append(f"{base_name}.html")
245
+
246
+ report.to_json(f"{base_name}.json")
247
+ generated_files.append(f"{base_name}.json")
248
+
249
+ report.to_csv(f"{base_name}.csv")
250
+ generated_files.append(f"{base_name}.csv")
251
+
252
+ report.to_markdown(f"{base_name}.md")
253
+ generated_files.append(f"{base_name}.md")
254
+
255
+ elif output != "console":
256
+ # Generate specific format
257
+ if not output_file:
258
+ timestamp = report.timestamp.strftime("%Y%m%d_%H%M%S")
259
+ output_file = f"cfat_report_{timestamp}.{output}"
260
+
261
+ if output == "html":
262
+ report.to_html(output_file)
263
+ elif output == "csv":
264
+ report.to_csv(output_file)
265
+ elif output == "json":
266
+ report.to_json(output_file)
267
+ elif output == "markdown":
268
+ report.to_markdown(output_file)
269
+
270
+ generated_files.append(output_file)
271
+
272
+ # Export to project management tools
273
+ if export_jira:
274
+ from runbooks.cfat.reporting.exporters import JiraExporter
275
+
276
+ exporter = JiraExporter()
277
+ exporter.export(report, export_jira)
278
+ generated_files.append(export_jira)
279
+
280
+ if export_asana:
281
+ from runbooks.cfat.reporting.exporters import AsanaExporter
282
+
283
+ exporter = AsanaExporter()
284
+ exporter.export(report, export_asana)
285
+ generated_files.append(export_asana)
286
+
287
+ if export_servicenow:
288
+ from runbooks.cfat.reporting.exporters import ServiceNowExporter
289
+
290
+ exporter = ServiceNowExporter()
291
+ exporter.export(report, export_servicenow)
292
+ generated_files.append(export_servicenow)
293
+
294
+ # Start web server if requested
295
+ if serve_web:
296
+ start_web_server(report, web_port)
297
+
298
+ # Display success message
299
+ if generated_files:
300
+ console.print(f"\n[green]✓ Assessment completed successfully![/green]")
301
+ console.print(f"[green]✓ Generated files:[/green]")
302
+ for file in generated_files:
303
+ console.print(f" • {file}")
304
+
305
+ # Display summary statistics
306
+ console.print(f"\n[bold]Assessment Summary:[/bold]")
307
+ console.print(f"• Compliance Score: [bold]{report.summary.compliance_score}/100[/bold]")
308
+ console.print(f"• Risk Level: [bold]{report.summary.risk_level}[/bold]")
309
+ console.print(f"• Critical Issues: [bold red]{report.summary.critical_issues}[/bold red]")
310
+ console.print(f"• Total Checks: {report.summary.total_checks}")
311
+ console.print(f"• Pass Rate: {report.summary.pass_rate:.1f}%")
312
+
313
+ except Exception as e:
314
+ logger.error(f"Assessment failed: {e}")
315
+ console.print(f"[red]✗ Assessment failed: {e}[/red]")
316
+ sys.exit(1)
317
+
318
+
319
+ def start_web_server(report, port: int = 8080):
320
+ """
321
+ Start interactive web server for assessment results.
322
+
323
+ Args:
324
+ report: Assessment report to serve
325
+ port: Port number for web server
326
+ """
327
+ try:
328
+ import os
329
+ import tempfile
330
+ import threading
331
+ import webbrowser
332
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
333
+
334
+ # Generate HTML report in temporary directory
335
+ temp_dir = tempfile.mkdtemp()
336
+ html_file = os.path.join(temp_dir, "assessment_report.html")
337
+ report.to_html(html_file)
338
+
339
+ # Change to temp directory for serving
340
+ os.chdir(temp_dir)
341
+
342
+ # Start web server in background thread
343
+ def serve():
344
+ httpd = HTTPServer(("localhost", port), SimpleHTTPRequestHandler)
345
+ console.print(f"[green]🌐 Web server started at http://localhost:{port}[/green]")
346
+ console.print(f"[yellow]Press Ctrl+C to stop the server[/yellow]")
347
+ httpd.serve_forever()
348
+
349
+ server_thread = threading.Thread(target=serve, daemon=True)
350
+ server_thread.start()
351
+
352
+ # Open browser
353
+ webbrowser.open(f"http://localhost:{port}/assessment_report.html")
354
+
355
+ # Keep main thread alive
356
+ try:
357
+ server_thread.join()
358
+ except KeyboardInterrupt:
359
+ console.print(f"\n[yellow]Web server stopped[/yellow]")
360
+
361
+ except ImportError:
362
+ console.print(f"[red]Web server functionality requires additional dependencies[/red]")
363
+ except Exception as e:
364
+ logger.error(f"Failed to start web server: {e}")
365
+ console.print(f"[red]Failed to start web server: {e}[/red]")
366
+
367
+
368
+ def display_assessment_results(report):
369
+ """
370
+ Display enhanced assessment results in formatted tables.
371
+
372
+ Args:
373
+ report: Assessment report to display
374
+ """
375
+ # Display executive summary first
376
+ console.print(f"\n[bold blue]📊 Cloud Foundations Assessment Results[/bold blue]")
377
+ console.print(f"[dim]Account: {report.account_id} | Region: {report.region} | Profile: {report.profile}[/dim]")
378
+
379
+ # Summary metrics table
380
+ summary_table = Table(title="Assessment Summary", show_header=True, header_style="bold magenta")
381
+ summary_table.add_column("Metric", style="cyan", width=20)
382
+ summary_table.add_column("Value", style="bold", width=15)
383
+ summary_table.add_column("Status", width=15)
384
+
385
+ # Add summary rows with enhanced formatting
386
+ summary_table.add_row(
387
+ "Compliance Score",
388
+ f"{report.summary.compliance_score}/100",
389
+ f"[{'green' if report.summary.compliance_score >= 80 else 'yellow' if report.summary.compliance_score >= 60 else 'red'}]{report.summary.risk_level}[/]",
390
+ )
391
+ summary_table.add_row("Total Checks", str(report.summary.total_checks), "✓ Completed")
392
+ summary_table.add_row(
393
+ "Pass Rate",
394
+ f"{report.summary.pass_rate:.1f}%",
395
+ f"[{'green' if report.summary.pass_rate >= 80 else 'yellow' if report.summary.pass_rate >= 60 else 'red'}]{'Good' if report.summary.pass_rate >= 80 else 'Fair' if report.summary.pass_rate >= 60 else 'Poor'}[/]",
396
+ )
397
+ summary_table.add_row(
398
+ "Critical Issues",
399
+ str(report.summary.critical_issues),
400
+ f"[{'red' if report.summary.critical_issues > 0 else 'green'}]{'Action Required' if report.summary.critical_issues > 0 else 'None'}[/]",
401
+ )
402
+ summary_table.add_row(
403
+ "Execution Time",
404
+ f"{report.summary.total_execution_time:.1f}s",
405
+ f"[dim]{report.summary.avg_execution_time:.2f}s avg[/dim]",
406
+ )
407
+
408
+ console.print(summary_table)
409
+
410
+ # Category breakdown
411
+ if report.results:
412
+ console.print(f"\n[bold]📋 Results by Category[/bold]")
413
+ category_summary = report.get_category_summary()
414
+
415
+ category_table = Table(show_header=True, header_style="bold magenta")
416
+ category_table.add_column("Category", style="cyan")
417
+ category_table.add_column("Total", justify="center")
418
+ category_table.add_column("Passed", justify="center", style="green")
419
+ category_table.add_column("Failed", justify="center", style="red")
420
+ category_table.add_column("Critical", justify="center", style="bold red")
421
+ category_table.add_column("Pass Rate", justify="center")
422
+
423
+ for category, stats in category_summary.items():
424
+ pass_rate = (stats["passed"] / stats["total"] * 100) if stats["total"] > 0 else 0
425
+ pass_rate_color = "green" if pass_rate >= 80 else "yellow" if pass_rate >= 60 else "red"
426
+
427
+ category_table.add_row(
428
+ category.upper(),
429
+ str(stats["total"]),
430
+ str(stats["passed"]),
431
+ str(stats["failed"]),
432
+ str(stats["critical"]),
433
+ f"[{pass_rate_color}]{pass_rate:.1f}%[/{pass_rate_color}]",
434
+ )
435
+
436
+ console.print(category_table)
437
+
438
+ # Show failed checks if any
439
+ failed_results = report.get_failed_results()
440
+ if failed_results:
441
+ console.print(f"\n[bold red]🚨 Failed Checks ({len(failed_results)})[/bold red]")
442
+
443
+ failed_table = Table(show_header=True, header_style="bold magenta")
444
+ failed_table.add_column("Finding ID", style="cyan", width=12)
445
+ failed_table.add_column("Check", style="bold", width=25)
446
+ failed_table.add_column("Severity", width=10)
447
+ failed_table.add_column("Message", style="dim", width=50)
448
+
449
+ for result in failed_results[:10]: # Show first 10 failed checks
450
+ severity_color = {"INFO": "blue", "WARNING": "yellow", "CRITICAL": "red"}.get(
451
+ result.severity.value, "white"
452
+ )
453
+
454
+ failed_table.add_row(
455
+ result.finding_id,
456
+ result.check_name,
457
+ f"[{severity_color}]{result.severity.value}[/{severity_color}]",
458
+ result.message[:47] + "..." if len(result.message) > 50 else result.message,
459
+ )
460
+
461
+ if len(failed_results) > 10:
462
+ console.print(f"[dim]... and {len(failed_results) - 10} more failed checks[/dim]")
463
+
464
+ console.print(failed_table)
465
+
466
+ # Show critical findings if any
467
+ critical_results = report.get_critical_results()
468
+ if critical_results:
469
+ console.print(f"\n[bold red]⚠️ Critical Findings Requiring Immediate Action[/bold red]")
470
+ for i, result in enumerate(critical_results[:3], 1): # Show first 3 critical
471
+ console.print(f"[red]{i}. {result.finding_id}[/red]: {result.message}")
472
+ if result.recommendations:
473
+ console.print(f" [yellow]→ {result.recommendations[0]}[/yellow]")
474
+
475
+ if len(critical_results) > 3:
476
+ console.print(f" [dim]... and {len(critical_results) - 3} more critical findings[/dim]")
477
+
478
+ # Final recommendations
479
+ console.print(f"\n[bold]📝 Next Steps[/bold]")
480
+ if report.summary.critical_issues > 0:
481
+ console.print(f"[red]• Address {report.summary.critical_issues} critical security issues immediately[/red]")
482
+ if report.summary.failed_checks > 0:
483
+ console.print(f"[yellow]• Review and remediate {report.summary.failed_checks} failed checks[/yellow]")
484
+ if report.summary.pass_rate < 80:
485
+ console.print(
486
+ f"[yellow]• Improve overall compliance score (currently {report.summary.compliance_score}/100)[/yellow]"
487
+ )
488
+ else:
489
+ console.print(f"[green]• Maintain current security posture and continue monitoring[/green]")
490
+
491
+
492
+ # ============================================================================
493
+ # Inventory Commands
494
+ # ============================================================================
495
+
496
+
497
+ @main.group()
498
+ @click.pass_context
499
+ def inventory(ctx):
500
+ """Multi-account resource inventory and discovery."""
501
+ pass
502
+
503
+
504
+ @inventory.command()
505
+ @click.option("--resources", "-r", multiple=True, help="Resource types to inventory (ec2, rds, lambda, etc.)")
506
+ @click.option("--all-resources", is_flag=True, help="Inventory all resource types")
507
+ @click.option("--accounts", "-a", multiple=True, help="Account IDs to inventory")
508
+ @click.option("--all-accounts", is_flag=True, help="Inventory all organization accounts")
509
+ @click.option("--output", type=click.Choice(["table", "csv", "json", "excel"]), default="table", help="Output format")
510
+ @click.option("--output-file", type=click.Path(), help="Output file path")
511
+ @click.option("--include-costs", is_flag=True, help="Include cost information")
512
+ @click.option("--parallel", is_flag=True, default=True, help="Run in parallel")
513
+ @click.pass_context
514
+ def collect(ctx, resources, all_resources, accounts, all_accounts, output, output_file, include_costs, parallel):
515
+ """
516
+ Collect inventory of AWS resources across accounts.
517
+
518
+ Examples:
519
+ runbooks inventory collect --all-resources --output excel
520
+ runbooks inventory collect -r ec2 -r rds --accounts 123456789012
521
+ runbooks inventory collect --all-accounts --include-costs
522
+ """
523
+ logger.info("Starting resource inventory collection")
524
+
525
+ with console.status("[bold green]Collecting inventory...") as status:
526
+ try:
527
+ # Initialize inventory collector
528
+ collector = InventoryCollector(profile=ctx.obj["profile"], region=ctx.obj["region"], parallel=parallel)
529
+
530
+ # Configure resources
531
+ if all_resources:
532
+ resource_types = collector.get_all_resource_types()
533
+ elif resources:
534
+ resource_types = list(resources)
535
+ else:
536
+ resource_types = ["ec2", "rds", "s3", "lambda"] # Default set
537
+
538
+ # Configure accounts
539
+ if all_accounts:
540
+ account_ids = collector.get_organization_accounts()
541
+ elif accounts:
542
+ account_ids = list(accounts)
543
+ else:
544
+ account_ids = [collector.get_current_account_id()]
545
+
546
+ # Collect inventory
547
+ results = collector.collect_inventory(
548
+ resource_types=resource_types, account_ids=account_ids, include_costs=include_costs
549
+ )
550
+
551
+ # Generate output
552
+ if output == "table":
553
+ display_inventory_results(results)
554
+ else:
555
+ from runbooks.inventory.core.formatter import InventoryFormatter
556
+
557
+ formatter = InventoryFormatter(results)
558
+
559
+ if not output_file:
560
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
561
+ output_file = f"inventory_{timestamp}.{output}"
562
+
563
+ if output == "csv":
564
+ formatter.to_csv(output_file)
565
+ elif output == "json":
566
+ formatter.to_json(output_file)
567
+ elif output == "excel":
568
+ formatter.to_excel(output_file)
569
+
570
+ console.print(f"[green]✓ Inventory saved to: {output_file}[/green]")
571
+
572
+ except Exception as e:
573
+ logger.error(f"Inventory collection failed: {e}")
574
+ console.print(f"[red]✗ Collection failed: {e}[/red]")
575
+ sys.exit(1)
576
+
577
+
578
+ # ============================================================================
579
+ # Organizations Commands
580
+ # ============================================================================
581
+
582
+
583
+ @main.group()
584
+ @click.pass_context
585
+ def org(ctx):
586
+ """AWS Organizations management and automation."""
587
+ pass
588
+
589
+
590
+ @org.command()
591
+ @click.option(
592
+ "--template",
593
+ type=click.Choice(["standard", "security", "custom"]),
594
+ default="standard",
595
+ help="OU structure template",
596
+ )
597
+ @click.option("--config-file", type=click.Path(exists=True), help="Custom OU structure configuration file")
598
+ @click.option("--dry-run", is_flag=True, help="Show what would be created")
599
+ @click.pass_context
600
+ def setup_ous(ctx, template, config_file, dry_run):
601
+ """
602
+ Set up organizational unit structure.
603
+
604
+ Creates a best-practice OU structure for AWS Organizations
605
+ based on Cloud Foundations recommendations.
606
+
607
+ Examples:
608
+ runbooks org setup-ous --template security
609
+ runbooks org setup-ous --config-file ou_structure.yaml --dry-run
610
+ """
611
+ logger.info(f"Setting up OU structure with template: {template}")
612
+
613
+ try:
614
+ manager = OUManager(profile=ctx.obj["profile"], region=ctx.obj["region"])
615
+
616
+ if config_file:
617
+ structure = manager.load_structure_from_file(config_file)
618
+ else:
619
+ structure = manager.get_template_structure(template)
620
+
621
+ if dry_run:
622
+ console.print("[yellow]DRY RUN - No changes will be made[/yellow]\n")
623
+ display_ou_structure(structure)
624
+ else:
625
+ with console.status("[bold green]Creating OU structure..."):
626
+ results = manager.create_ou_structure(structure)
627
+ display_creation_results(results)
628
+
629
+ except Exception as e:
630
+ logger.error(f"OU setup failed: {e}")
631
+ console.print(f"[red]✗ Setup failed: {e}[/red]")
632
+ sys.exit(1)
633
+
634
+
635
+ def display_inventory_results(results):
636
+ """Display inventory results in a formatted table."""
637
+ from runbooks.inventory.core.formatter import InventoryFormatter
638
+
639
+ formatter = InventoryFormatter(results)
640
+ console_output = formatter.format_console_table()
641
+ console.print(console_output)
642
+
643
+
644
+ def display_ou_structure(structure):
645
+ """Display OU structure in a formatted view."""
646
+ table = Table(title=f"OU Structure: {structure.get('name', 'Unnamed')}")
647
+ table.add_column("OU Name", style="cyan")
648
+ table.add_column("Description", style="dim")
649
+ table.add_column("Level", style="bold")
650
+
651
+ def add_ou_to_table(ou_def, level=0):
652
+ indent = " " * level
653
+ table.add_row(f"{indent}{ou_def['name']}", ou_def.get("description", ""), str(level))
654
+
655
+ for child in ou_def.get("children", []):
656
+ add_ou_to_table(child, level + 1)
657
+
658
+ for ou in structure.get("organizational_units", []):
659
+ add_ou_to_table(ou)
660
+
661
+ console.print(table)
662
+
663
+
664
+ def display_creation_results(results):
665
+ """Display OU creation results."""
666
+ table = Table(title="OU Creation Results")
667
+ table.add_column("OU Name", style="cyan")
668
+ table.add_column("OU ID", style="bold")
669
+ table.add_column("Parent ID", style="dim")
670
+ table.add_column("Status", style="green")
671
+
672
+ def add_results_to_table(ou_result, level=0):
673
+ indent = " " * level
674
+ table.add_row(f"{indent}{ou_result['name']}", ou_result["id"], ou_result["parent_id"], "✓ Created")
675
+
676
+ for child in ou_result.get("children", []):
677
+ add_results_to_table(child, level + 1)
678
+
679
+ for ou_result in results.get("created_ous", []):
680
+ add_results_to_table(ou_result)
681
+
682
+ console.print(table)
683
+
684
+ if results.get("errors"):
685
+ console.print("\n[red]Errors:[/red]")
686
+ for error in results["errors"]:
687
+ console.print(f" [red]✗ {error}[/red]")
688
+
689
+
690
+ # ============================================================================
691
+ # FinOps Commands
692
+ # ============================================================================
693
+
694
+
695
+ @main.group(invoke_without_command=True)
696
+ @click.option(
697
+ "--config-file",
698
+ "-C",
699
+ help="Path to a TOML, YAML, or JSON configuration file.",
700
+ type=str,
701
+ )
702
+ @click.option(
703
+ "--profiles",
704
+ "-p",
705
+ multiple=True,
706
+ help="Specific AWS profiles to use (repeat option to pass multiple)",
707
+ type=str,
708
+ )
709
+ @click.option(
710
+ "--regions",
711
+ "-r",
712
+ multiple=True,
713
+ help="AWS regions to check for EC2 instances (repeat option to pass multiple)",
714
+ type=str,
715
+ )
716
+ @click.option("--all", "-a", is_flag=True, help="Use all available AWS profiles")
717
+ @click.option(
718
+ "--combine",
719
+ "-c",
720
+ is_flag=True,
721
+ help="Combine profiles from the same AWS account",
722
+ )
723
+ @click.option(
724
+ "--report-name",
725
+ "-n",
726
+ help="Specify the base name for the report file (without extension)",
727
+ default=None,
728
+ type=str,
729
+ )
730
+ @click.option(
731
+ "--report-type",
732
+ "-y",
733
+ multiple=True,
734
+ type=click.Choice(["csv", "json", "pdf"]),
735
+ help="Specify one or more report types (repeat option): csv, json, pdf",
736
+ default=("csv",),
737
+ )
738
+ @click.option(
739
+ "--dir",
740
+ "-d",
741
+ help="Directory to save the report files (default: current directory)",
742
+ type=str,
743
+ )
744
+ @click.option(
745
+ "--time-range",
746
+ "-t",
747
+ help="Time range for cost data in days (default: current month). Examples: 7, 30, 90",
748
+ type=int,
749
+ )
750
+ @click.option(
751
+ "--tag",
752
+ "-g",
753
+ multiple=True,
754
+ help="Cost allocation tag filter(s), e.g., --tag Team=DevOps (repeat for multiple)",
755
+ type=str,
756
+ )
757
+ @click.option(
758
+ "--trend",
759
+ is_flag=True,
760
+ help="Display a trend report as bars for the past 6 months time range",
761
+ )
762
+ @click.option(
763
+ "--audit",
764
+ is_flag=True,
765
+ help="Display an audit report with cost anomalies, stopped EC2 instances, unused EBS volumes, budget alerts, and more",
766
+ )
767
+ @click.pass_context
768
+ def finops(ctx, **kwargs):
769
+ """AWS FinOps Dashboard - Cost and Resource Monitoring."""
770
+ if ctx.invoked_subcommand is None:
771
+ import argparse
772
+
773
+ from runbooks.finops.dashboard_runner import run_dashboard
774
+
775
+ args = argparse.Namespace(**kwargs)
776
+ run_dashboard(args)
777
+
778
+
779
+ # ============================================================================
780
+ # Security Commands
781
+ # ============================================================================
782
+
783
+
784
+ @main.group(invoke_without_command=True)
785
+ @click.option(
786
+ "--profile",
787
+ default="default",
788
+ help="AWS IAM profile to use for authentication (default: 'default')"
789
+ )
790
+ @click.option(
791
+ "--language",
792
+ type=click.Choice(["EN", "JP", "KR", "VN"]),
793
+ default="EN",
794
+ help="Language for security reports (default: 'EN')"
795
+ )
796
+ @click.option(
797
+ "--output",
798
+ help="Custom output directory for reports (default: ./results)"
799
+ )
800
+ @click.pass_context
801
+ def security(ctx, profile, language, output):
802
+ """AWS Security Baseline Assessment Tool.
803
+
804
+ Comprehensive security baseline testing with multilingual reporting
805
+ and enterprise-grade assessment features.
806
+
807
+ Examples:
808
+ runbooks security assess --profile prod --language EN
809
+ runbooks security assess --language KR --output /reports
810
+ runbooks security check root-mfa --profile production
811
+ """
812
+ if ctx.invoked_subcommand is None:
813
+ from runbooks.security import run_security_script
814
+
815
+ # Create mock args namespace for backward compatibility
816
+ import argparse
817
+ args = argparse.Namespace(
818
+ profile=profile,
819
+ language=language,
820
+ output=output
821
+ )
822
+
823
+ # Import and run the main security function
824
+ from runbooks.security.security_baseline_tester import SecurityBaselineTester
825
+
826
+ try:
827
+ console.print(f"[blue]🔒 AWS Security Baseline Assessment[/blue]")
828
+ console.print(f"[dim]Profile: {profile} | Language: {language} | Output: {output or './results'}[/dim]")
829
+
830
+ tester = SecurityBaselineTester(profile, language, output)
831
+ tester.run()
832
+
833
+ console.print(f"[green]✅ Security assessment completed successfully![/green]")
834
+
835
+ except Exception as e:
836
+ console.print(f"[red]❌ Error running security assessment: {e}[/red]")
837
+ raise click.ClickException(str(e))
838
+
839
+
840
+ @security.command()
841
+ @click.option(
842
+ "--profile",
843
+ default="default",
844
+ help="AWS IAM profile to use for authentication"
845
+ )
846
+ @click.option(
847
+ "--language",
848
+ type=click.Choice(["EN", "JP", "KR", "VN"]),
849
+ default="EN",
850
+ help="Language for security reports"
851
+ )
852
+ @click.option(
853
+ "--output",
854
+ help="Custom output directory for reports"
855
+ )
856
+ @click.option(
857
+ "--checks",
858
+ multiple=True,
859
+ help="Specific security checks to run (repeat for multiple)"
860
+ )
861
+ @click.option(
862
+ "--format",
863
+ type=click.Choice(["html", "json", "console"]),
864
+ default="html",
865
+ help="Output format for results"
866
+ )
867
+ @click.pass_context
868
+ def assess(ctx, profile, language, output, checks, format):
869
+ """Run comprehensive security baseline assessment.
870
+
871
+ Evaluates AWS account against security best practices and generates
872
+ detailed reports with findings and remediation guidance.
873
+
874
+ Examples:
875
+ runbooks security assess --profile prod
876
+ runbooks security assess --language KR --format json
877
+ runbooks security assess --checks root_mfa --checks iam_password_policy
878
+ """
879
+ try:
880
+ from runbooks.security.security_baseline_tester import SecurityBaselineTester
881
+
882
+ console.print(f"[blue]🔒 Running Security Baseline Assessment[/blue]")
883
+ console.print(f"[dim]Profile: {profile} | Language: {language} | Format: {format}[/dim]")
884
+
885
+ if checks:
886
+ console.print(f"[dim]Specific checks: {', '.join(checks)}[/dim]")
887
+
888
+ # Initialize and run security assessment
889
+ tester = SecurityBaselineTester(profile, language, output)
890
+
891
+ # TODO: Add support for specific checks filtering
892
+ # For now, run all checks
893
+ tester.run()
894
+
895
+ console.print(f"[green]✅ Security assessment completed![/green]")
896
+
897
+ # Display results summary
898
+ console.print(f"\n[bold]📊 Assessment Summary:[/bold]")
899
+ console.print(f"[green]• Report generated in {format.upper()} format[/green]")
900
+ console.print(f"[yellow]• Output directory: {output or './results'}[/yellow]")
901
+ console.print(f"[blue]• Language: {language}[/blue]")
902
+
903
+ except Exception as e:
904
+ console.print(f"[red]❌ Error running security assessment: {e}[/red]")
905
+ raise click.ClickException(str(e))
906
+
907
+
908
+ @security.command()
909
+ @click.argument("check_name")
910
+ @click.option(
911
+ "--profile",
912
+ default="default",
913
+ help="AWS IAM profile to use"
914
+ )
915
+ @click.option(
916
+ "--language",
917
+ type=click.Choice(["EN", "JP", "KR", "VN"]),
918
+ default="EN",
919
+ help="Language for output"
920
+ )
921
+ @click.pass_context
922
+ def check(ctx, check_name, profile, language):
923
+ """Run a specific security check.
924
+
925
+ Available checks:
926
+ root_mfa, root_usage, root_access_key, iam_user_mfa,
927
+ iam_password_policy, direct_attached_policy, alternate_contacts,
928
+ trail_enabled, multi_region_trail, account_level_bucket_public_access,
929
+ bucket_public_access, cloudwatch_alarm_configuration,
930
+ multi_region_instance_usage, guardduty_enabled, trusted_advisor
931
+
932
+ Examples:
933
+ runbooks security check root_mfa --profile prod
934
+ runbooks security check iam_password_policy --language KR
935
+ """
936
+ try:
937
+ console.print(f"[blue]🔍 Running security check: {check_name}[/blue]")
938
+ console.print(f"[dim]Profile: {profile} | Language: {language}[/dim]")
939
+
940
+ # TODO: Implement individual check execution
941
+ # For now, show available checks
942
+ available_checks = [
943
+ "root_mfa", "root_usage", "root_access_key", "iam_user_mfa",
944
+ "iam_password_policy", "direct_attached_policy", "alternate_contacts",
945
+ "trail_enabled", "multi_region_trail", "account_level_bucket_public_access",
946
+ "bucket_public_access", "cloudwatch_alarm_configuration",
947
+ "multi_region_instance_usage", "guardduty_enabled", "trusted_advisor"
948
+ ]
949
+
950
+ if check_name not in available_checks:
951
+ console.print(f"[red]❌ Unknown check: {check_name}[/red]")
952
+ console.print(f"[yellow]Available checks:[/yellow]")
953
+ for check in available_checks:
954
+ console.print(f" • {check}")
955
+ raise click.ClickException(f"Invalid check name: {check_name}")
956
+
957
+ console.print(f"[yellow]⚠️ Individual check execution not yet implemented[/yellow]")
958
+ console.print(f"[blue]💡 Use 'runbooks security assess' to run all checks[/blue]")
959
+
960
+ except Exception as e:
961
+ console.print(f"[red]❌ Error running security check: {e}[/red]")
962
+ raise click.ClickException(str(e))
963
+
964
+
965
+ @security.command()
966
+ @click.pass_context
967
+ def list_checks(ctx):
968
+ """List all available security checks."""
969
+ console.print(f"[blue]📋 Available Security Checks[/blue]")
970
+ console.print(f"[dim]These checks evaluate AWS account security against best practices[/dim]\n")
971
+
972
+ checks = {
973
+ "root_mfa": "Check if MFA is enabled for root account",
974
+ "root_usage": "Check root account usage patterns",
975
+ "root_access_key": "Check for root account access keys",
976
+ "iam_user_mfa": "Check MFA settings for IAM users",
977
+ "iam_password_policy": "Evaluate IAM password policy",
978
+ "direct_attached_policy": "Check for directly attached IAM policies",
979
+ "alternate_contacts": "Verify alternate contact information",
980
+ "trail_enabled": "Check if CloudTrail is enabled",
981
+ "multi_region_trail": "Check for multi-region CloudTrail",
982
+ "account_level_bucket_public_access": "Check S3 account-level public access",
983
+ "bucket_public_access": "Check individual S3 bucket public access",
984
+ "cloudwatch_alarm_configuration": "Verify CloudWatch alarm configuration",
985
+ "multi_region_instance_usage": "Check multi-region EC2 usage",
986
+ "guardduty_enabled": "Check if GuardDuty is enabled",
987
+ "trusted_advisor": "Check Trusted Advisor configuration"
988
+ }
989
+
990
+ for check_name, description in checks.items():
991
+ console.print(f"[cyan]{check_name:35}[/cyan] {description}")
992
+
993
+ console.print(f"\n[yellow]💡 Run individual checks:[/yellow]")
994
+ console.print(f" runbooks security check <check_name>")
995
+ console.print(f"\n[yellow]💡 Run all checks:[/yellow]")
996
+ console.print(f" runbooks security assess")
997
+
998
+
999
+ # ============================================================================
1000
+ # Main entry point - KISS principle: everything in one file
1001
+ # ============================================================================
1002
+
1003
+ if __name__ == "__main__":
1004
+ main()