aws-inventory-manager 0.17.12__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.
- aws_inventory_manager-0.17.12.dist-info/LICENSE +21 -0
- aws_inventory_manager-0.17.12.dist-info/METADATA +1292 -0
- aws_inventory_manager-0.17.12.dist-info/RECORD +152 -0
- aws_inventory_manager-0.17.12.dist-info/WHEEL +5 -0
- aws_inventory_manager-0.17.12.dist-info/entry_points.txt +2 -0
- aws_inventory_manager-0.17.12.dist-info/top_level.txt +1 -0
- src/__init__.py +3 -0
- src/aws/__init__.py +11 -0
- src/aws/client.py +128 -0
- src/aws/credentials.py +191 -0
- src/aws/rate_limiter.py +177 -0
- src/cli/__init__.py +12 -0
- src/cli/config.py +130 -0
- src/cli/main.py +4046 -0
- src/cloudtrail/__init__.py +5 -0
- src/cloudtrail/query.py +642 -0
- src/config_service/__init__.py +21 -0
- src/config_service/collector.py +346 -0
- src/config_service/detector.py +256 -0
- src/config_service/resource_type_mapping.py +328 -0
- src/cost/__init__.py +5 -0
- src/cost/analyzer.py +226 -0
- src/cost/explorer.py +209 -0
- src/cost/reporter.py +237 -0
- src/delta/__init__.py +5 -0
- src/delta/calculator.py +206 -0
- src/delta/differ.py +185 -0
- src/delta/formatters.py +272 -0
- src/delta/models.py +154 -0
- src/delta/reporter.py +234 -0
- src/matching/__init__.py +6 -0
- src/matching/config.py +52 -0
- src/matching/normalizer.py +450 -0
- src/matching/prompts.py +33 -0
- src/models/__init__.py +21 -0
- src/models/config_diff.py +135 -0
- src/models/cost_report.py +87 -0
- src/models/deletion_operation.py +104 -0
- src/models/deletion_record.py +97 -0
- src/models/delta_report.py +122 -0
- src/models/efs_resource.py +80 -0
- src/models/elasticache_resource.py +90 -0
- src/models/group.py +318 -0
- src/models/inventory.py +133 -0
- src/models/protection_rule.py +123 -0
- src/models/report.py +288 -0
- src/models/resource.py +111 -0
- src/models/security_finding.py +102 -0
- src/models/snapshot.py +122 -0
- src/restore/__init__.py +20 -0
- src/restore/audit.py +175 -0
- src/restore/cleaner.py +461 -0
- src/restore/config.py +209 -0
- src/restore/deleter.py +976 -0
- src/restore/dependency.py +254 -0
- src/restore/safety.py +115 -0
- src/security/__init__.py +0 -0
- src/security/checks/__init__.py +0 -0
- src/security/checks/base.py +56 -0
- src/security/checks/ec2_checks.py +88 -0
- src/security/checks/elasticache_checks.py +149 -0
- src/security/checks/iam_checks.py +102 -0
- src/security/checks/rds_checks.py +140 -0
- src/security/checks/s3_checks.py +95 -0
- src/security/checks/secrets_checks.py +96 -0
- src/security/checks/sg_checks.py +142 -0
- src/security/cis_mapper.py +97 -0
- src/security/models.py +53 -0
- src/security/reporter.py +174 -0
- src/security/scanner.py +87 -0
- src/snapshot/__init__.py +6 -0
- src/snapshot/capturer.py +453 -0
- src/snapshot/filter.py +259 -0
- src/snapshot/inventory_storage.py +236 -0
- src/snapshot/report_formatter.py +250 -0
- src/snapshot/reporter.py +189 -0
- src/snapshot/resource_collectors/__init__.py +5 -0
- src/snapshot/resource_collectors/apigateway.py +140 -0
- src/snapshot/resource_collectors/backup.py +136 -0
- src/snapshot/resource_collectors/base.py +81 -0
- src/snapshot/resource_collectors/cloudformation.py +55 -0
- src/snapshot/resource_collectors/cloudwatch.py +109 -0
- src/snapshot/resource_collectors/codebuild.py +69 -0
- src/snapshot/resource_collectors/codepipeline.py +82 -0
- src/snapshot/resource_collectors/dynamodb.py +65 -0
- src/snapshot/resource_collectors/ec2.py +240 -0
- src/snapshot/resource_collectors/ecs.py +215 -0
- src/snapshot/resource_collectors/efs_collector.py +102 -0
- src/snapshot/resource_collectors/eks.py +200 -0
- src/snapshot/resource_collectors/elasticache_collector.py +79 -0
- src/snapshot/resource_collectors/elb.py +126 -0
- src/snapshot/resource_collectors/eventbridge.py +156 -0
- src/snapshot/resource_collectors/glue.py +199 -0
- src/snapshot/resource_collectors/iam.py +188 -0
- src/snapshot/resource_collectors/kms.py +111 -0
- src/snapshot/resource_collectors/lambda_func.py +139 -0
- src/snapshot/resource_collectors/rds.py +109 -0
- src/snapshot/resource_collectors/route53.py +86 -0
- src/snapshot/resource_collectors/s3.py +105 -0
- src/snapshot/resource_collectors/secretsmanager.py +70 -0
- src/snapshot/resource_collectors/sns.py +68 -0
- src/snapshot/resource_collectors/sqs.py +82 -0
- src/snapshot/resource_collectors/ssm.py +160 -0
- src/snapshot/resource_collectors/stepfunctions.py +74 -0
- src/snapshot/resource_collectors/vpcendpoints.py +79 -0
- src/snapshot/resource_collectors/waf.py +159 -0
- src/snapshot/storage.py +351 -0
- src/storage/__init__.py +21 -0
- src/storage/audit_store.py +419 -0
- src/storage/database.py +294 -0
- src/storage/group_store.py +763 -0
- src/storage/inventory_store.py +320 -0
- src/storage/resource_store.py +416 -0
- src/storage/schema.py +339 -0
- src/storage/snapshot_store.py +363 -0
- src/utils/__init__.py +12 -0
- src/utils/export.py +305 -0
- src/utils/hash.py +60 -0
- src/utils/logging.py +63 -0
- src/utils/pagination.py +41 -0
- src/utils/paths.py +51 -0
- src/utils/progress.py +41 -0
- src/utils/unsupported_resources.py +306 -0
- src/web/__init__.py +5 -0
- src/web/app.py +97 -0
- src/web/dependencies.py +69 -0
- src/web/routes/__init__.py +1 -0
- src/web/routes/api/__init__.py +18 -0
- src/web/routes/api/charts.py +156 -0
- src/web/routes/api/cleanup.py +186 -0
- src/web/routes/api/filters.py +253 -0
- src/web/routes/api/groups.py +305 -0
- src/web/routes/api/inventories.py +80 -0
- src/web/routes/api/queries.py +202 -0
- src/web/routes/api/resources.py +393 -0
- src/web/routes/api/snapshots.py +314 -0
- src/web/routes/api/views.py +260 -0
- src/web/routes/pages.py +198 -0
- src/web/services/__init__.py +1 -0
- src/web/templates/base.html +955 -0
- src/web/templates/components/navbar.html +31 -0
- src/web/templates/components/sidebar.html +104 -0
- src/web/templates/pages/audit_logs.html +86 -0
- src/web/templates/pages/cleanup.html +279 -0
- src/web/templates/pages/dashboard.html +227 -0
- src/web/templates/pages/diff.html +175 -0
- src/web/templates/pages/error.html +30 -0
- src/web/templates/pages/groups.html +721 -0
- src/web/templates/pages/queries.html +246 -0
- src/web/templates/pages/resources.html +2429 -0
- src/web/templates/pages/snapshot_detail.html +271 -0
- src/web/templates/pages/snapshots.html +429 -0
src/restore/config.py
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""Protection rules configuration loader.
|
|
2
|
+
|
|
3
|
+
Loads protection rules from YAML config files and CLI options.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
import yaml
|
|
14
|
+
|
|
15
|
+
from src.models.protection_rule import ProtectionRule, RuleType
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
# Default config file locations (checked in order)
|
|
20
|
+
CONFIG_LOCATIONS = [
|
|
21
|
+
".awsinv-restore.yaml", # Project-local
|
|
22
|
+
".awsinv-restore.yml",
|
|
23
|
+
os.path.expanduser("~/.awsinv/restore.yaml"), # User-level
|
|
24
|
+
os.path.expanduser("~/.awsinv/restore.yml"),
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def find_config_file() -> Optional[Path]:
|
|
29
|
+
"""Find the first available config file.
|
|
30
|
+
|
|
31
|
+
Searches in order:
|
|
32
|
+
1. .awsinv-restore.yaml (current directory)
|
|
33
|
+
2. .awsinv-restore.yml (current directory)
|
|
34
|
+
3. ~/.awsinv/restore.yaml (user home)
|
|
35
|
+
4. ~/.awsinv/restore.yml (user home)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Path to config file if found, None otherwise
|
|
39
|
+
"""
|
|
40
|
+
for location in CONFIG_LOCATIONS:
|
|
41
|
+
path = Path(location)
|
|
42
|
+
if path.exists():
|
|
43
|
+
logger.debug(f"Found config file: {path}")
|
|
44
|
+
return path
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def load_config_file(config_path: Optional[str] = None) -> dict:
|
|
49
|
+
"""Load configuration from YAML file.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
config_path: Explicit path to config file (optional).
|
|
53
|
+
If not provided, searches default locations.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Configuration dictionary, empty dict if no config found
|
|
57
|
+
"""
|
|
58
|
+
if config_path:
|
|
59
|
+
path = Path(config_path)
|
|
60
|
+
if not path.exists():
|
|
61
|
+
raise FileNotFoundError(f"Config file not found: {config_path}")
|
|
62
|
+
else:
|
|
63
|
+
path = find_config_file()
|
|
64
|
+
if not path:
|
|
65
|
+
logger.debug("No config file found, using defaults")
|
|
66
|
+
return {}
|
|
67
|
+
|
|
68
|
+
logger.info(f"Loading config from: {path}")
|
|
69
|
+
|
|
70
|
+
with open(path) as f:
|
|
71
|
+
config = yaml.safe_load(f) or {}
|
|
72
|
+
|
|
73
|
+
return config
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def parse_protect_tag(tag_string: str) -> tuple[str, str]:
|
|
77
|
+
"""Parse a protect-tag CLI argument.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
tag_string: Tag in format "key=value"
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Tuple of (key, value)
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
ValueError: If format is invalid
|
|
87
|
+
"""
|
|
88
|
+
if "=" not in tag_string:
|
|
89
|
+
raise ValueError(f"Invalid tag format: '{tag_string}'. Expected 'key=value'")
|
|
90
|
+
|
|
91
|
+
parts = tag_string.split("=", 1)
|
|
92
|
+
key = parts[0].strip()
|
|
93
|
+
value = parts[1].strip()
|
|
94
|
+
|
|
95
|
+
if not key:
|
|
96
|
+
raise ValueError(f"Empty tag key in: '{tag_string}'")
|
|
97
|
+
|
|
98
|
+
return key, value
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def build_protection_rules(
|
|
102
|
+
config: dict,
|
|
103
|
+
cli_protect_tags: Optional[list[str]] = None,
|
|
104
|
+
) -> list[ProtectionRule]:
|
|
105
|
+
"""Build protection rules from config and CLI options.
|
|
106
|
+
|
|
107
|
+
CLI options are merged with config file rules. CLI rules have
|
|
108
|
+
higher priority (lower priority number = checked first).
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
config: Configuration dictionary from YAML file
|
|
112
|
+
cli_protect_tags: List of "key=value" tag strings from CLI
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
List of ProtectionRule objects, sorted by priority
|
|
116
|
+
"""
|
|
117
|
+
rules = []
|
|
118
|
+
rule_counter = 0
|
|
119
|
+
|
|
120
|
+
# 1. CLI protect-tags (highest priority: 1-10)
|
|
121
|
+
if cli_protect_tags:
|
|
122
|
+
for tag_string in cli_protect_tags:
|
|
123
|
+
try:
|
|
124
|
+
key, value = parse_protect_tag(tag_string)
|
|
125
|
+
rule_counter += 1
|
|
126
|
+
rule = ProtectionRule(
|
|
127
|
+
rule_id=f"cli-tag-{rule_counter}",
|
|
128
|
+
rule_type=RuleType.TAG,
|
|
129
|
+
enabled=True,
|
|
130
|
+
priority=rule_counter, # CLI rules get priority 1, 2, 3...
|
|
131
|
+
patterns={"tag_key": key, "tag_values": [value]},
|
|
132
|
+
description=f"CLI protection: {key}={value}",
|
|
133
|
+
)
|
|
134
|
+
rules.append(rule)
|
|
135
|
+
logger.debug(f"Added CLI protection rule: {key}={value}")
|
|
136
|
+
except ValueError as e:
|
|
137
|
+
logger.warning(f"Skipping invalid protect-tag: {e}")
|
|
138
|
+
|
|
139
|
+
# 2. Config file global tag rules (priority 11-50)
|
|
140
|
+
protection_config = config.get("protection", {})
|
|
141
|
+
global_rules = protection_config.get("global", [])
|
|
142
|
+
|
|
143
|
+
for i, rule_config in enumerate(global_rules):
|
|
144
|
+
rule_counter += 1
|
|
145
|
+
priority = 10 + rule_counter
|
|
146
|
+
|
|
147
|
+
if isinstance(rule_config, dict):
|
|
148
|
+
# Complex rule with property/value/type
|
|
149
|
+
prop = rule_config.get("property", "")
|
|
150
|
+
value = rule_config.get("value", "")
|
|
151
|
+
|
|
152
|
+
# Handle tag:Name format
|
|
153
|
+
if prop.startswith("tag:"):
|
|
154
|
+
tag_key = prop[4:] # Remove "tag:" prefix
|
|
155
|
+
rule = ProtectionRule(
|
|
156
|
+
rule_id=f"config-global-{i + 1}",
|
|
157
|
+
rule_type=RuleType.TAG,
|
|
158
|
+
enabled=True,
|
|
159
|
+
priority=priority,
|
|
160
|
+
patterns={"tag_key": tag_key, "tag_values": [value]},
|
|
161
|
+
description=f"Config global: {tag_key}={value}",
|
|
162
|
+
)
|
|
163
|
+
rules.append(rule)
|
|
164
|
+
elif isinstance(rule_config, str):
|
|
165
|
+
# Simple string format "key=value"
|
|
166
|
+
try:
|
|
167
|
+
key, value = parse_protect_tag(rule_config)
|
|
168
|
+
rule = ProtectionRule(
|
|
169
|
+
rule_id=f"config-global-{i + 1}",
|
|
170
|
+
rule_type=RuleType.TAG,
|
|
171
|
+
enabled=True,
|
|
172
|
+
priority=priority,
|
|
173
|
+
patterns={"tag_key": key, "tag_values": [value]},
|
|
174
|
+
description=f"Config global: {key}={value}",
|
|
175
|
+
)
|
|
176
|
+
rules.append(rule)
|
|
177
|
+
except ValueError:
|
|
178
|
+
logger.warning(f"Skipping invalid global rule: {rule_config}")
|
|
179
|
+
|
|
180
|
+
# 3. Excluded types (priority 51-100) - these are TYPE rules
|
|
181
|
+
excluded_types = protection_config.get("excluded_types", [])
|
|
182
|
+
if excluded_types:
|
|
183
|
+
rule = ProtectionRule(
|
|
184
|
+
rule_id="config-excluded-types",
|
|
185
|
+
rule_type=RuleType.TYPE,
|
|
186
|
+
enabled=True,
|
|
187
|
+
priority=51,
|
|
188
|
+
patterns={"resource_types": excluded_types},
|
|
189
|
+
description=f"Excluded types: {', '.join(excluded_types)}",
|
|
190
|
+
)
|
|
191
|
+
rules.append(rule)
|
|
192
|
+
|
|
193
|
+
# Sort by priority
|
|
194
|
+
rules.sort(key=lambda r: r.priority)
|
|
195
|
+
|
|
196
|
+
logger.info(f"Built {len(rules)} protection rules")
|
|
197
|
+
return rules
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_skip_aws_managed(config: dict) -> bool:
|
|
201
|
+
"""Get skip_aws_managed setting from config.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
config: Configuration dictionary
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
True if AWS-managed resources should be skipped (default: True)
|
|
208
|
+
"""
|
|
209
|
+
return config.get("skip_aws_managed", True)
|