runbooks 0.2.3__py3-none-any.whl → 0.6.1__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.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/README.md +16 -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/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/__init__.py +88 -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/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -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/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/script_test_data.py +0 -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 +785 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security_baseline/README.md +324 -0
- runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
- runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
- runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
- runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
- runbooks/security_baseline/checklist/root_access_key.py +6 -1
- runbooks/security_baseline/config-origin.json +1 -1
- runbooks/security_baseline/config.json +1 -1
- runbooks/security_baseline/permission.json +1 -1
- runbooks/security_baseline/report_generator.py +10 -2
- runbooks/security_baseline/report_template_en.html +8 -8
- runbooks/security_baseline/report_template_jp.html +8 -8
- runbooks/security_baseline/report_template_kr.html +13 -13
- runbooks/security_baseline/report_template_vn.html +8 -8
- runbooks/security_baseline/requirements.txt +7 -0
- runbooks/security_baseline/run_script.py +8 -2
- runbooks/security_baseline/security_baseline_tester.py +10 -2
- runbooks/security_baseline/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.6.1.dist-info/METADATA +373 -0
- runbooks-0.6.1.dist-info/RECORD +237 -0
- {runbooks-0.2.3.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
- runbooks-0.6.1.dist-info/entry_points.txt +7 -0
- runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
- runbooks-0.6.1.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.3.dist-info/METADATA +0 -435
- runbooks-0.2.3.dist-info/RECORD +0 -61
- runbooks-0.2.3.dist-info/entry_points.txt +0 -3
- runbooks-0.2.3.dist-info/top_level.txt +0 -1
runbooks/base.py
ADDED
@@ -0,0 +1,353 @@
|
|
1
|
+
"""
|
2
|
+
Base classes for Cloud Foundations modules.
|
3
|
+
|
4
|
+
This module provides common base classes and utilities used across
|
5
|
+
all Cloud Foundations components including CFAT, inventory, and organizations.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from abc import ABC, abstractmethod
|
9
|
+
from datetime import datetime
|
10
|
+
from pathlib import Path
|
11
|
+
from typing import Any, Dict, List, Optional, Union
|
12
|
+
|
13
|
+
import boto3
|
14
|
+
from botocore.exceptions import BotoCoreError, ClientError
|
15
|
+
from loguru import logger
|
16
|
+
|
17
|
+
try:
|
18
|
+
from pydantic import BaseModel
|
19
|
+
except ImportError:
|
20
|
+
# Fallback BaseModel
|
21
|
+
class BaseModel:
|
22
|
+
def __init__(self, **kwargs):
|
23
|
+
for key, value in kwargs.items():
|
24
|
+
setattr(self, key, value)
|
25
|
+
|
26
|
+
def model_dump(self, exclude_none=True):
|
27
|
+
result = {}
|
28
|
+
for key, value in self.__dict__.items():
|
29
|
+
if not exclude_none or value is not None:
|
30
|
+
result[key] = value
|
31
|
+
return result
|
32
|
+
|
33
|
+
|
34
|
+
from runbooks.config import RunbooksConfig
|
35
|
+
from runbooks.utils import retry_with_backoff
|
36
|
+
|
37
|
+
|
38
|
+
class CloudFoundationsResult(BaseModel):
|
39
|
+
"""Base result model for Cloud Foundations operations."""
|
40
|
+
|
41
|
+
timestamp: datetime
|
42
|
+
success: bool
|
43
|
+
message: str
|
44
|
+
data: Optional[Dict[str, Any]] = None
|
45
|
+
errors: List[str] = []
|
46
|
+
|
47
|
+
class Config:
|
48
|
+
"""Pydantic config."""
|
49
|
+
|
50
|
+
json_encoders = {datetime: lambda dt: dt.isoformat()}
|
51
|
+
|
52
|
+
|
53
|
+
class CloudFoundationsBase(ABC):
|
54
|
+
"""
|
55
|
+
Abstract base class for Cloud Foundations components.
|
56
|
+
|
57
|
+
Provides common functionality for AWS operations, error handling,
|
58
|
+
session management, and result formatting.
|
59
|
+
"""
|
60
|
+
|
61
|
+
def __init__(
|
62
|
+
self, profile: Optional[str] = None, region: Optional[str] = None, config: Optional[RunbooksConfig] = None
|
63
|
+
):
|
64
|
+
"""
|
65
|
+
Initialize base Cloud Foundations component.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
profile: AWS profile name
|
69
|
+
region: AWS region
|
70
|
+
config: Runbooks configuration
|
71
|
+
"""
|
72
|
+
self.config = config or RunbooksConfig()
|
73
|
+
self.profile = profile or self.config.aws_profile
|
74
|
+
self.region = region or self.config.aws_region
|
75
|
+
self._session = None
|
76
|
+
self._clients = {}
|
77
|
+
|
78
|
+
logger.debug(f"Initialized {self.__class__.__name__} with profile: {self.profile}, region: {self.region}")
|
79
|
+
|
80
|
+
@property
|
81
|
+
def session(self) -> boto3.Session:
|
82
|
+
"""Get or create boto3 session."""
|
83
|
+
if self._session is None:
|
84
|
+
self._session = self._create_session()
|
85
|
+
return self._session
|
86
|
+
|
87
|
+
def _create_session(self) -> boto3.Session:
|
88
|
+
"""Create boto3 session with appropriate configuration."""
|
89
|
+
session_kwargs = {"profile_name": self.profile}
|
90
|
+
if self.region:
|
91
|
+
session_kwargs["region_name"] = self.region
|
92
|
+
|
93
|
+
try:
|
94
|
+
session = boto3.Session(**session_kwargs)
|
95
|
+
# Test session by getting caller identity
|
96
|
+
sts = session.client("sts")
|
97
|
+
identity = sts.get_caller_identity()
|
98
|
+
logger.debug(f"Session created for account: {identity.get('Account')}")
|
99
|
+
return session
|
100
|
+
except Exception as e:
|
101
|
+
logger.error(f"Failed to create AWS session: {e}")
|
102
|
+
raise
|
103
|
+
|
104
|
+
def get_client(self, service_name: str, region: Optional[str] = None) -> Any:
|
105
|
+
"""
|
106
|
+
Get or create AWS service client with caching.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
service_name: AWS service name (e.g., 'ec2', 's3')
|
110
|
+
region: Optional region override
|
111
|
+
|
112
|
+
Returns:
|
113
|
+
Boto3 client for the service
|
114
|
+
"""
|
115
|
+
client_region = region or self.region
|
116
|
+
cache_key = f"{service_name}:{client_region}"
|
117
|
+
|
118
|
+
if cache_key not in self._clients:
|
119
|
+
client_kwargs = {}
|
120
|
+
if client_region:
|
121
|
+
client_kwargs["region_name"] = client_region
|
122
|
+
|
123
|
+
self._clients[cache_key] = self.session.client(service_name, **client_kwargs)
|
124
|
+
logger.debug(f"Created {service_name} client for region: {client_region}")
|
125
|
+
|
126
|
+
return self._clients[cache_key]
|
127
|
+
|
128
|
+
def get_resource(self, service_name: str, region: Optional[str] = None) -> Any:
|
129
|
+
"""
|
130
|
+
Get AWS service resource.
|
131
|
+
|
132
|
+
Args:
|
133
|
+
service_name: AWS service name (e.g., 'ec2', 's3')
|
134
|
+
region: Optional region override
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
Boto3 resource for the service
|
138
|
+
"""
|
139
|
+
resource_kwargs = {}
|
140
|
+
if region or self.region:
|
141
|
+
resource_kwargs["region_name"] = region or self.region
|
142
|
+
|
143
|
+
return self.session.resource(service_name, **resource_kwargs)
|
144
|
+
|
145
|
+
@retry_with_backoff(max_retries=3, backoff_factor=1.0)
|
146
|
+
def _make_aws_call(self, client_method, **kwargs) -> Any:
|
147
|
+
"""
|
148
|
+
Make AWS API call with retry logic.
|
149
|
+
|
150
|
+
Args:
|
151
|
+
client_method: Boto3 client method to call
|
152
|
+
**kwargs: Arguments for the method
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
API response
|
156
|
+
"""
|
157
|
+
try:
|
158
|
+
return client_method(**kwargs)
|
159
|
+
except ClientError as e:
|
160
|
+
error_code = e.response["Error"]["Code"]
|
161
|
+
error_message = e.response["Error"]["Message"]
|
162
|
+
logger.error(f"AWS API error ({error_code}): {error_message}")
|
163
|
+
raise
|
164
|
+
except BotoCoreError as e:
|
165
|
+
logger.error(f"AWS SDK error: {e}")
|
166
|
+
raise
|
167
|
+
|
168
|
+
def get_account_id(self) -> str:
|
169
|
+
"""Get current AWS account ID."""
|
170
|
+
try:
|
171
|
+
sts = self.get_client("sts")
|
172
|
+
response = self._make_aws_call(sts.get_caller_identity)
|
173
|
+
return response["Account"]
|
174
|
+
except Exception as e:
|
175
|
+
logger.error(f"Failed to get account ID: {e}")
|
176
|
+
raise
|
177
|
+
|
178
|
+
def get_available_regions(self, service_name: str = "ec2") -> List[str]:
|
179
|
+
"""
|
180
|
+
Get list of available AWS regions for a service.
|
181
|
+
|
182
|
+
Args:
|
183
|
+
service_name: AWS service name
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
List of region names
|
187
|
+
"""
|
188
|
+
try:
|
189
|
+
client = self.get_client(service_name)
|
190
|
+
response = self._make_aws_call(client.describe_regions)
|
191
|
+
return [region["RegionName"] for region in response["Regions"]]
|
192
|
+
except Exception as e:
|
193
|
+
logger.error(f"Failed to get available regions: {e}")
|
194
|
+
return []
|
195
|
+
|
196
|
+
def validate_permissions(self, required_actions: List[str]) -> Dict[str, bool]:
|
197
|
+
"""
|
198
|
+
Validate that current credentials have required permissions.
|
199
|
+
|
200
|
+
Args:
|
201
|
+
required_actions: List of IAM actions to check
|
202
|
+
|
203
|
+
Returns:
|
204
|
+
Dictionary mapping actions to permission status
|
205
|
+
"""
|
206
|
+
results = {}
|
207
|
+
iam = self.get_client("iam")
|
208
|
+
|
209
|
+
for action in required_actions:
|
210
|
+
try:
|
211
|
+
# Use simulate_principal_policy to check permissions
|
212
|
+
response = self._make_aws_call(
|
213
|
+
iam.simulate_principal_policy,
|
214
|
+
PolicySourceArn=f"arn:aws:iam::{self.get_account_id()}:user/*",
|
215
|
+
ActionNames=[action],
|
216
|
+
)
|
217
|
+
|
218
|
+
if response["EvaluationResults"]:
|
219
|
+
decision = response["EvaluationResults"][0]["EvalDecision"]
|
220
|
+
results[action] = decision == "allowed"
|
221
|
+
else:
|
222
|
+
results[action] = False
|
223
|
+
|
224
|
+
except Exception as e:
|
225
|
+
logger.warning(f"Could not check permission for {action}: {e}")
|
226
|
+
results[action] = False
|
227
|
+
|
228
|
+
return results
|
229
|
+
|
230
|
+
@abstractmethod
|
231
|
+
def run(self) -> CloudFoundationsResult:
|
232
|
+
"""
|
233
|
+
Run the main operation for this component.
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
Result object with operation status and data
|
237
|
+
"""
|
238
|
+
pass
|
239
|
+
|
240
|
+
def create_result(
|
241
|
+
self, success: bool, message: str, data: Optional[Dict[str, Any]] = None, errors: Optional[List[str]] = None
|
242
|
+
) -> CloudFoundationsResult:
|
243
|
+
"""
|
244
|
+
Create a standardized result object.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
success: Whether operation succeeded
|
248
|
+
message: Result message
|
249
|
+
data: Optional result data
|
250
|
+
errors: Optional list of errors
|
251
|
+
|
252
|
+
Returns:
|
253
|
+
CloudFoundationsResult object
|
254
|
+
"""
|
255
|
+
return CloudFoundationsResult(
|
256
|
+
timestamp=datetime.now(), success=success, message=message, data=data or {}, errors=errors or []
|
257
|
+
)
|
258
|
+
|
259
|
+
|
260
|
+
class CloudFoundationsFormatter:
|
261
|
+
"""Base formatter for Cloud Foundations output."""
|
262
|
+
|
263
|
+
def __init__(self, data: Any):
|
264
|
+
"""Initialize formatter with data."""
|
265
|
+
self.data = data
|
266
|
+
|
267
|
+
def to_dict(self) -> Dict[str, Any]:
|
268
|
+
"""Convert data to dictionary format."""
|
269
|
+
if hasattr(self.data, "model_dump"):
|
270
|
+
return self.data.model_dump()
|
271
|
+
elif hasattr(self.data, "dict"):
|
272
|
+
return self.data.dict()
|
273
|
+
else:
|
274
|
+
return {"data": self.data}
|
275
|
+
|
276
|
+
def to_json(self, file_path: Union[str, Path]) -> None:
|
277
|
+
"""Save data as JSON file."""
|
278
|
+
import json
|
279
|
+
|
280
|
+
output_path = Path(file_path)
|
281
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
282
|
+
|
283
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
284
|
+
json.dump(self.to_dict(), f, indent=2, default=str)
|
285
|
+
|
286
|
+
logger.info(f"JSON output saved to: {output_path}")
|
287
|
+
|
288
|
+
def to_csv(self, file_path: Union[str, Path]) -> None:
|
289
|
+
"""Save data as CSV file."""
|
290
|
+
try:
|
291
|
+
import pandas as pd
|
292
|
+
except ImportError:
|
293
|
+
logger.error("pandas is required for CSV export. Install with: pip install pandas")
|
294
|
+
return
|
295
|
+
|
296
|
+
output_path = Path(file_path)
|
297
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
298
|
+
|
299
|
+
# Convert to DataFrame
|
300
|
+
if isinstance(self.data, list):
|
301
|
+
df = pd.DataFrame(self.data)
|
302
|
+
else:
|
303
|
+
df = pd.DataFrame([self.to_dict()])
|
304
|
+
|
305
|
+
df.to_csv(output_path, index=False)
|
306
|
+
logger.info(f"CSV output saved to: {output_path}")
|
307
|
+
|
308
|
+
def to_excel(self, file_path: Union[str, Path]) -> None:
|
309
|
+
"""Save data as Excel file."""
|
310
|
+
try:
|
311
|
+
import pandas as pd
|
312
|
+
except ImportError:
|
313
|
+
logger.error("pandas is required for Excel export. Install with: pip install pandas")
|
314
|
+
return
|
315
|
+
|
316
|
+
output_path = Path(file_path)
|
317
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
318
|
+
|
319
|
+
# Convert to DataFrame
|
320
|
+
if isinstance(self.data, list):
|
321
|
+
df = pd.DataFrame(self.data)
|
322
|
+
else:
|
323
|
+
df = pd.DataFrame([self.to_dict()])
|
324
|
+
|
325
|
+
df.to_excel(output_path, index=False)
|
326
|
+
logger.info(f"Excel output saved to: {output_path}")
|
327
|
+
|
328
|
+
|
329
|
+
class ProgressTracker:
|
330
|
+
"""Simple progress tracking for long-running operations."""
|
331
|
+
|
332
|
+
def __init__(self, total: int, description: str = "Processing"):
|
333
|
+
"""Initialize progress tracker."""
|
334
|
+
self.total = total
|
335
|
+
self.current = 0
|
336
|
+
self.description = description
|
337
|
+
self.start_time = datetime.now()
|
338
|
+
|
339
|
+
def update(self, increment: int = 1, status: Optional[str] = None) -> None:
|
340
|
+
"""Update progress."""
|
341
|
+
self.current += increment
|
342
|
+
percentage = (self.current / self.total) * 100 if self.total > 0 else 0
|
343
|
+
|
344
|
+
elapsed = datetime.now() - self.start_time
|
345
|
+
|
346
|
+
status_msg = f" - {status}" if status else ""
|
347
|
+
logger.info(f"{self.description}: {self.current}/{self.total} ({percentage:.1f}%){status_msg}")
|
348
|
+
|
349
|
+
def complete(self, message: Optional[str] = None) -> None:
|
350
|
+
"""Mark progress as complete."""
|
351
|
+
elapsed = datetime.now() - self.start_time
|
352
|
+
final_msg = message or f"{self.description} completed"
|
353
|
+
logger.info(f"{final_msg} in {elapsed.total_seconds():.1f}s")
|
runbooks/cfat/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Cloud Foundation Assessment Tool (CFAT)
|
2
|
+
|
3
|
+
CFAT is an open-source solution designed to provide automated discovery of an AWS environment and its multi-account architecture. Additionally, CFAT will review the environment, checking for common configurations and best practices for your AWS Organization. The tool will produce a backlog of tasks to complete, along with remediation guidance. CFAT is simple to execute, requiring only that it be executed within the AWS Management Account in the AWS CloudShell.
|
4
|
+
|
5
|
+
>**Note:** CFAT can operate with `READONLY` permissions **plus CloudShell permissions** to the AWS account, and does not make any changes to the AWS environment. All information generated from the tool is outputted to your local AWS CloudShell environment.
|
6
|
+
|
7
|
+
## How to Use
|
8
|
+
|
9
|
+
1. Go into an AWS account which is a `Management Account` and open CloudShell terminal.
|
10
|
+

|
11
|
+
2. Ensure you have right now admin permissions or the proper *READONLY* permissions that include ability to use AWS CloudShell
|
12
|
+
* For least privilege readonly, leverage the IAM Managed Policies:
|
13
|
+
- `arn:aws:iam::aws:policy/ReadOnlyAccess`
|
14
|
+
- `arn:aws:iam::aws:policy/AWSCloudShellFullAccess`
|
15
|
+
3. Within AWS CloudShell the following command: `curl -sSL https://raw.githubusercontent.com/cloud-foundations-on-aws/cloud-foundations-templates/main/cfat/run-assessment.sh | sh`
|
16
|
+

|
17
|
+
4. Watch screen scroll through output looking for any errors.
|
18
|
+
<br/><img src="./docs/cloudshell-output.png" alt="drawing" width="50%" height="50%"/>
|
19
|
+
5. Once done you will see the tool created a directory called ./cfat
|
20
|
+
* verify by running `ls` in the current working directory you are in
|
21
|
+
6. The CFAT creates a zip archive containing several artifacts (see [Generated Documentation and Artifacts](#generated-documentation-and-artifacts))
|
22
|
+
7. Within the CloudShell window, go to top right of the page and click on `Actions` button and click on `Download File`
|
23
|
+
<br/><img src="./docs/cloudshell-download.png" alt="drawing" width="30%" height="30%"/>
|
24
|
+
8. In the download file enter `./cfat/assessment.zip`
|
25
|
+
<br/><img src="./docs/downloadfile.png" alt="drawing" width="50%" height="50%"/>
|
26
|
+
9. File is download which you can unzip and view the generated documentation and artifacts.
|
27
|
+
|
28
|
+
## Generated Documentation and Artifacts
|
29
|
+
|
30
|
+
Running the CFAT produces an archive assessments folder `./cfat/assessment.zip` in the current working directory you run the program from. Unzipping the archive will enable you to access 4 files which were generated during the assessment:
|
31
|
+
|
32
|
+
1. **cfat.txt** - file contains a detailed text report of the assessment. Use this file to determine work needed to be completed. An example of a generated report can be found at [./docs/cfat.txt](./docs/cfat.txt). As illustrated below, the header in the report will give you a quick status and an estimated level of effort (loe) to complete the requirements.
|
33
|
+

|
34
|
+
2. **cfat-checks.csv** - output file of the table result which is located in the detailed report (cfat.txt). The csv output is to let you organize and sort the findings. An example of a generated report can be found at [./docs/cfat-checks.csv](./docs/cfat-checks.csv)
|
35
|
+

|
36
|
+
3. **asana-import.csv** - all tasks created in a csv file format that allow you to easily import the items into your Asana managed backlog. An example of a generated import can be found at [./docs/asana-import.csv](./docs/asana-import.csv)
|
37
|
+
4. **jira-import.csv** - all tasks created in a csv file format that allow you to easily import the items into your Jira managed backlog. An example of a generated import can be found at[./docs/jira-import.csv](./docs/jira-import.csv)
|
38
|
+
|
39
|
+
## Features
|
40
|
+
|
41
|
+
* **Automated Discovery:** CFAT automates the discovery process, minimizing the need for manual checks and providing a quick overview of the environment.
|
42
|
+
* **READONLY Access:** The tool operates with READONLY access (**plus CloudShell permissions**) to the AWS account, ensuring that it does not make any modifications or interfere with the existing setup.
|
43
|
+
* **Importable Backlog:** The tool creates common project management software importable file allowing you to import CFAT findings into services like Jira and Asana.
|
44
|
+
* **AWS CloudShell Compatibility:** CFAT is designed to be executed within AWS CloudShell, providing a convenient and secure environment for running discovery.
|
45
|
+
* **Developed in JavaScript and AWS-SDK v3:** CFAT is implemented using JavaScript and relies on the latest AWS-SDK v3 for seamless interaction with AWS services.
|
46
|
+
|
47
|
+
## Security Considerations
|
48
|
+
|
49
|
+
* The tool is designed to operate with `READONLY` access (**plus permissions to run CloudShell**), minimizing the risk of unintended changes to your environment. All data is outputted into your local CloudShell environment.
|
@@ -0,0 +1,74 @@
|
|
1
|
+
"""
|
2
|
+
Cloud Foundations Assessment Tool (CFAT) - Enterprise CloudOps Assessment.
|
3
|
+
|
4
|
+
This module provides comprehensive AWS account assessment capabilities
|
5
|
+
following Cloud Foundations best practices with enterprise-grade
|
6
|
+
features including:
|
7
|
+
|
8
|
+
- Multi-format reporting (HTML, CSV, JSON, Markdown)
|
9
|
+
- Parallel assessment execution
|
10
|
+
- Customizable check configurations
|
11
|
+
- Compliance framework alignment
|
12
|
+
- Advanced scoring and risk analysis
|
13
|
+
|
14
|
+
The CFAT module is designed for DevOps and SRE teams to automate
|
15
|
+
compliance validation, security assessment, and operational
|
16
|
+
readiness evaluation across AWS environments.
|
17
|
+
|
18
|
+
Example:
|
19
|
+
```python
|
20
|
+
from runbooks.cfat import AssessmentRunner, Severity
|
21
|
+
|
22
|
+
# Initialize and configure assessment
|
23
|
+
runner = AssessmentRunner(profile="prod", region="us-east-1")
|
24
|
+
runner.set_min_severity(Severity.WARNING)
|
25
|
+
|
26
|
+
# Run assessment
|
27
|
+
report = runner.run_assessment()
|
28
|
+
|
29
|
+
# Export results
|
30
|
+
report.to_html("assessment_report.html")
|
31
|
+
report.to_json("findings.json")
|
32
|
+
|
33
|
+
print(f"Compliance Score: {report.summary.compliance_score}/100")
|
34
|
+
print(f"Critical Issues: {report.summary.critical_issues}")
|
35
|
+
```
|
36
|
+
|
37
|
+
Version: 0.5.0 (Enhanced with enterprise features)
|
38
|
+
"""
|
39
|
+
|
40
|
+
# Core assessment engine
|
41
|
+
# Enhanced data models
|
42
|
+
from runbooks.cfat.models import (
|
43
|
+
AssessmentConfig,
|
44
|
+
# Core models
|
45
|
+
AssessmentReport,
|
46
|
+
AssessmentResult,
|
47
|
+
AssessmentSummary,
|
48
|
+
CheckConfig,
|
49
|
+
CheckStatus,
|
50
|
+
# Enums
|
51
|
+
Severity,
|
52
|
+
)
|
53
|
+
from runbooks.cfat.runner import AssessmentRunner
|
54
|
+
|
55
|
+
# Version info
|
56
|
+
__version__ = "0.5.0"
|
57
|
+
__author__ = "CloudOps Runbooks Team"
|
58
|
+
|
59
|
+
# Public API exports
|
60
|
+
__all__ = [
|
61
|
+
# Core functionality
|
62
|
+
"AssessmentRunner",
|
63
|
+
# Data models
|
64
|
+
"AssessmentReport",
|
65
|
+
"AssessmentResult",
|
66
|
+
"AssessmentSummary",
|
67
|
+
"AssessmentConfig",
|
68
|
+
"CheckConfig",
|
69
|
+
# Enums
|
70
|
+
"Severity",
|
71
|
+
"CheckStatus",
|
72
|
+
# Metadata
|
73
|
+
"__version__",
|
74
|
+
]
|