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.
- conftest.py +26 -0
- jupyter-agent/.env +2 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/.gradio/certificate.pem +31 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/__main__.log +8 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
- jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
- jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
- jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/README.md +337 -0
- runbooks/finops/__init__.py +86 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/aws_organization.png +0 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +1004 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security/README.md +447 -0
- runbooks/security/__init__.py +71 -0
- runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
- runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
- runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
- runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
- runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
- runbooks/{security_baseline → security}/config-origin.json +1 -1
- runbooks/{security_baseline → security}/config.json +1 -1
- runbooks/{security_baseline → security}/permission.json +1 -1
- runbooks/{security_baseline → security}/report_generator.py +10 -2
- runbooks/{security_baseline → security}/report_template_en.html +7 -7
- runbooks/{security_baseline → security}/report_template_jp.html +7 -7
- runbooks/{security_baseline → security}/report_template_kr.html +12 -12
- runbooks/{security_baseline → security}/report_template_vn.html +7 -7
- runbooks/{security_baseline → security}/run_script.py +8 -2
- runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
- runbooks/{security_baseline → security}/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.7.0.dist-info/METADATA +375 -0
- runbooks-0.7.0.dist-info/RECORD +249 -0
- {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
- runbooks-0.7.0.dist-info/entry_points.txt +7 -0
- runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
- runbooks-0.7.0.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.5.dist-info/METADATA +0 -439
- runbooks-0.2.5.dist-info/RECORD +0 -61
- runbooks-0.2.5.dist-info/entry_points.txt +0 -3
- runbooks-0.2.5.dist-info/top_level.txt +0 -1
- /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
- /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
- /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
- /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
- /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
- /runbooks/{security_baseline → security}/utils/enums.py +0 -0
- /runbooks/{security_baseline → security}/utils/language.py +0 -0
- /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
- /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()
|