aws-inventory-manager 0.13.2__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.
Potentially problematic release.
This version of aws-inventory-manager might be problematic. Click here for more details.
- aws_inventory_manager-0.13.2.dist-info/LICENSE +21 -0
- aws_inventory_manager-0.13.2.dist-info/METADATA +1226 -0
- aws_inventory_manager-0.13.2.dist-info/RECORD +145 -0
- aws_inventory_manager-0.13.2.dist-info/WHEEL +5 -0
- aws_inventory_manager-0.13.2.dist-info/entry_points.txt +2 -0
- aws_inventory_manager-0.13.2.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 +3626 -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/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 +451 -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/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 +749 -0
- src/storage/inventory_store.py +320 -0
- src/storage/resource_store.py +413 -0
- src/storage/schema.py +288 -0
- src/storage/snapshot_store.py +346 -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 +379 -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 +949 -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 +2251 -0
- src/web/templates/pages/snapshot_detail.html +271 -0
- src/web/templates/pages/snapshots.html +429 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}SQL Queries - AWS Inventory Browser{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block content %}
|
|
6
|
+
<div class="space-y-6" x-data="{
|
|
7
|
+
query: 'SELECT resource_type, COUNT(*) as count FROM resources GROUP BY resource_type ORDER BY count DESC LIMIT 20',
|
|
8
|
+
results: null,
|
|
9
|
+
columns: [],
|
|
10
|
+
loading: false,
|
|
11
|
+
error: null,
|
|
12
|
+
savedQueries: [],
|
|
13
|
+
showSaveModal: false,
|
|
14
|
+
queryName: '',
|
|
15
|
+
queryDescription: ''
|
|
16
|
+
}">
|
|
17
|
+
<div class="md:flex md:items-center md:justify-between">
|
|
18
|
+
<div class="min-w-0 flex-1">
|
|
19
|
+
<h1 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl">SQL Query Editor</h1>
|
|
20
|
+
<p class="mt-1 text-sm text-gray-500">Run SQL queries against your resource database</p>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
|
25
|
+
<!-- Saved Queries Sidebar -->
|
|
26
|
+
<div class="lg:col-span-1">
|
|
27
|
+
<div class="bg-white shadow rounded-lg">
|
|
28
|
+
<div class="px-4 py-3 border-b border-gray-200">
|
|
29
|
+
<h3 class="text-sm font-medium text-gray-900">Saved Queries</h3>
|
|
30
|
+
</div>
|
|
31
|
+
<div id="saved-queries-list"
|
|
32
|
+
hx-get="/api/queries"
|
|
33
|
+
hx-trigger="load"
|
|
34
|
+
hx-swap="innerHTML"
|
|
35
|
+
class="divide-y divide-gray-200 max-h-96 overflow-y-auto">
|
|
36
|
+
<div class="p-4 text-center text-gray-500 text-sm">Loading...</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Quick Examples -->
|
|
41
|
+
<div class="mt-4 bg-white shadow rounded-lg">
|
|
42
|
+
<div class="px-4 py-3 border-b border-gray-200">
|
|
43
|
+
<h3 class="text-sm font-medium text-gray-900">Quick Examples</h3>
|
|
44
|
+
</div>
|
|
45
|
+
<ul class="divide-y divide-gray-200 text-sm">
|
|
46
|
+
<li class="px-4 py-2 hover:bg-gray-50 cursor-pointer"
|
|
47
|
+
@click="query = 'SELECT resource_type, COUNT(*) as count FROM resources GROUP BY resource_type ORDER BY count DESC'">
|
|
48
|
+
Resources by type
|
|
49
|
+
</li>
|
|
50
|
+
<li class="px-4 py-2 hover:bg-gray-50 cursor-pointer"
|
|
51
|
+
@click="query = 'SELECT region, COUNT(*) as count FROM resources GROUP BY region'">
|
|
52
|
+
Resources by region
|
|
53
|
+
</li>
|
|
54
|
+
<li class="px-4 py-2 hover:bg-gray-50 cursor-pointer"
|
|
55
|
+
@click="query = 'SELECT * FROM resources WHERE resource_type LIKE \\'%s3%\\' LIMIT 50'">
|
|
56
|
+
S3 buckets
|
|
57
|
+
</li>
|
|
58
|
+
<li class="px-4 py-2 hover:bg-gray-50 cursor-pointer"
|
|
59
|
+
@click="query = 'SELECT key, value, COUNT(*) as count FROM resource_tags GROUP BY key, value ORDER BY count DESC LIMIT 50'">
|
|
60
|
+
Tag distribution
|
|
61
|
+
</li>
|
|
62
|
+
</ul>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<!-- Query Editor -->
|
|
67
|
+
<div class="lg:col-span-3 space-y-4">
|
|
68
|
+
<div class="bg-white shadow rounded-lg">
|
|
69
|
+
<div class="p-4">
|
|
70
|
+
<label for="query" class="block text-sm font-medium text-gray-700 mb-2">SQL Query</label>
|
|
71
|
+
<textarea id="query" x-model="query" rows="6"
|
|
72
|
+
class="font-mono text-sm w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 bg-gray-900 text-green-400 p-4"
|
|
73
|
+
placeholder="Enter your SQL query..."></textarea>
|
|
74
|
+
|
|
75
|
+
<div class="mt-4 flex items-center justify-between">
|
|
76
|
+
<div class="flex space-x-2">
|
|
77
|
+
<button @click="executeQuery()"
|
|
78
|
+
:disabled="loading || !query.trim()"
|
|
79
|
+
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 disabled:opacity-50">
|
|
80
|
+
<svg class="-ml-1 mr-2 h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
81
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/>
|
|
82
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
83
|
+
</svg>
|
|
84
|
+
<span x-text="loading ? 'Running...' : 'Run Query'"></span>
|
|
85
|
+
</button>
|
|
86
|
+
<button @click="showSaveModal = true"
|
|
87
|
+
:disabled="!query.trim()"
|
|
88
|
+
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
|
|
89
|
+
<svg class="-ml-1 mr-2 h-5 w-5 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
90
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/>
|
|
91
|
+
</svg>
|
|
92
|
+
Save Query
|
|
93
|
+
</button>
|
|
94
|
+
</div>
|
|
95
|
+
<div x-show="results" class="text-sm text-gray-500">
|
|
96
|
+
<span x-text="results?.row_count || 0"></span> rows in
|
|
97
|
+
<span x-text="results?.execution_time_ms || 0"></span>ms
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<!-- Results -->
|
|
104
|
+
<div x-show="results" x-cloak class="bg-white shadow rounded-lg overflow-hidden">
|
|
105
|
+
<div class="overflow-x-auto">
|
|
106
|
+
<table class="min-w-full divide-y divide-gray-200">
|
|
107
|
+
<thead class="bg-gray-50">
|
|
108
|
+
<tr>
|
|
109
|
+
<template x-for="col in columns" :key="col">
|
|
110
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" x-text="col"></th>
|
|
111
|
+
</template>
|
|
112
|
+
</tr>
|
|
113
|
+
</thead>
|
|
114
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
|
115
|
+
<template x-for="(row, index) in results?.rows?.slice(0, 100)" :key="index">
|
|
116
|
+
<tr class="hover:bg-gray-50">
|
|
117
|
+
<template x-for="col in columns" :key="col">
|
|
118
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
119
|
+
<span x-text="typeof row[col] === 'object' ? JSON.stringify(row[col]) : row[col]"></span>
|
|
120
|
+
</td>
|
|
121
|
+
</template>
|
|
122
|
+
</tr>
|
|
123
|
+
</template>
|
|
124
|
+
</tbody>
|
|
125
|
+
</table>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<!-- Error -->
|
|
130
|
+
<div x-show="error" x-cloak class="bg-red-50 border border-red-200 rounded-lg p-4">
|
|
131
|
+
<p class="text-red-800 font-mono text-sm" x-text="error"></p>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<!-- Save Query Modal -->
|
|
137
|
+
<div x-show="showSaveModal" x-cloak class="fixed z-10 inset-0 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
|
138
|
+
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
|
139
|
+
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="showSaveModal = false"></div>
|
|
140
|
+
<div class="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
|
|
141
|
+
<h3 class="text-lg font-medium text-gray-900 mb-4">Save Query</h3>
|
|
142
|
+
<div class="space-y-4">
|
|
143
|
+
<div>
|
|
144
|
+
<label class="block text-sm font-medium text-gray-700">Name</label>
|
|
145
|
+
<input type="text" x-model="queryName" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
|
|
146
|
+
</div>
|
|
147
|
+
<div>
|
|
148
|
+
<label class="block text-sm font-medium text-gray-700">Description (optional)</label>
|
|
149
|
+
<textarea x-model="queryDescription" rows="2" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"></textarea>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="mt-5 sm:mt-6 sm:flex sm:flex-row-reverse">
|
|
153
|
+
<button @click="saveQuery()" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 sm:ml-3 sm:w-auto sm:text-sm">
|
|
154
|
+
Save
|
|
155
|
+
</button>
|
|
156
|
+
<button @click="showSaveModal = false" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 sm:mt-0 sm:w-auto sm:text-sm">
|
|
157
|
+
Cancel
|
|
158
|
+
</button>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
{% endblock %}
|
|
165
|
+
|
|
166
|
+
{% block scripts %}
|
|
167
|
+
<script>
|
|
168
|
+
function executeQuery() {
|
|
169
|
+
const data = Alpine.$data(document.querySelector('[x-data]'));
|
|
170
|
+
data.loading = true;
|
|
171
|
+
data.error = null;
|
|
172
|
+
|
|
173
|
+
fetch('/api/queries/execute?limit=500', {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
176
|
+
body: 'sql=' + encodeURIComponent(data.query)
|
|
177
|
+
})
|
|
178
|
+
.then(r => r.json())
|
|
179
|
+
.then(result => {
|
|
180
|
+
if (result.detail) {
|
|
181
|
+
data.error = result.detail;
|
|
182
|
+
} else {
|
|
183
|
+
data.results = result;
|
|
184
|
+
data.columns = result.columns || [];
|
|
185
|
+
}
|
|
186
|
+
data.loading = false;
|
|
187
|
+
})
|
|
188
|
+
.catch(err => {
|
|
189
|
+
data.error = err.message;
|
|
190
|
+
data.loading = false;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function saveQuery() {
|
|
195
|
+
const data = Alpine.$data(document.querySelector('[x-data]'));
|
|
196
|
+
|
|
197
|
+
fetch('/api/queries', {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: { 'Content-Type': 'application/json' },
|
|
200
|
+
body: JSON.stringify({
|
|
201
|
+
name: data.queryName,
|
|
202
|
+
description: data.queryDescription,
|
|
203
|
+
sql_text: data.query
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
.then(r => r.json())
|
|
207
|
+
.then(() => {
|
|
208
|
+
data.showSaveModal = false;
|
|
209
|
+
data.queryName = '';
|
|
210
|
+
data.queryDescription = '';
|
|
211
|
+
htmx.trigger('#saved-queries-list', 'load');
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Handle saved queries list response
|
|
216
|
+
document.body.addEventListener('htmx:afterSwap', function(evt) {
|
|
217
|
+
if (evt.detail.target.id === 'saved-queries-list') {
|
|
218
|
+
const response = JSON.parse(evt.detail.xhr.responseText);
|
|
219
|
+
renderSavedQueries(response.queries || [], evt.detail.target);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
function renderSavedQueries(queries, target) {
|
|
224
|
+
if (queries.length === 0) {
|
|
225
|
+
target.innerHTML = '<div class="p-4 text-center text-gray-500 text-sm">No saved queries yet</div>';
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let html = '';
|
|
230
|
+
queries.forEach(q => {
|
|
231
|
+
html += `
|
|
232
|
+
<div class="px-4 py-3 hover:bg-gray-50 cursor-pointer" onclick="loadQuery('${q.sql_text.replace(/'/g, "\\'")}')">
|
|
233
|
+
<p class="text-sm font-medium text-gray-900">${q.name}</p>
|
|
234
|
+
<p class="text-xs text-gray-500">${q.description || 'No description'}</p>
|
|
235
|
+
</div>
|
|
236
|
+
`;
|
|
237
|
+
});
|
|
238
|
+
target.innerHTML = html;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function loadQuery(sql) {
|
|
242
|
+
const data = Alpine.$data(document.querySelector('[x-data]'));
|
|
243
|
+
data.query = sql;
|
|
244
|
+
}
|
|
245
|
+
</script>
|
|
246
|
+
{% endblock %}
|