cdk-cost-analyzer 0.1.10 → 0.1.11
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.
- package/.cdk-cost-analyzer-cache/metadata.json +26 -2
- package/.kiro/specs/single-template-analysis/tasks.md +67 -54
- package/README.md +31 -1
- package/dist/analysis/SingleTemplateAnalyzer.d.ts +23 -0
- package/dist/analysis/SingleTemplateAnalyzer.js +168 -0
- package/dist/analysis/index.d.ts +1 -0
- package/dist/analysis/index.js +6 -0
- package/dist/api/index.d.ts +24 -0
- package/dist/api/index.js +56 -1
- package/dist/api/single-template-types.d.ts +83 -0
- package/dist/api/single-template-types.js +3 -0
- package/dist/cli/index.js +43 -54
- package/dist/pricing/calculators/LambdaCalculator.d.ts +2 -0
- package/dist/pricing/calculators/LambdaCalculator.js +48 -1
- package/dist/releasetag.txt +1 -1
- package/dist/reporter/SingleTemplateReporter.d.ts +31 -0
- package/dist/reporter/SingleTemplateReporter.js +186 -0
- package/dist/reporter/index.d.ts +1 -0
- package/dist/reporter/index.js +4 -2
- package/docs/SINGLE-TEMPLATE-ANALYSIS.md +372 -0
- package/examples/single-template-usage.js +208 -0
- package/package.json +1 -1
- package/test-cdk-project/README-COMPUTE.md +0 -141
- package/test-cdk-project/README.md +0 -95
- package/test-cdk-project/app-with-compute.js +0 -102
- package/test-cdk-project/app.js +0 -81
- package/test-cdk-project/cdk-compute.json +0 -3
- package/test-cdk-project/cdk.context.json +0 -7
- package/test-cdk-project/cdk.json +0 -3
- package/test-cdk-project/cdk.out/TestStack.assets.json +0 -21
- package/test-cdk-project/cdk.out/TestStack.template.json +0 -115
- package/test-cdk-project/cdk.out/cdk.out +0 -1
- package/test-cdk-project/cdk.out/manifest.json +0 -503
- package/test-cdk-project/cdk.out/tree.json +0 -1
- package/test-cdk-project/cdk.out.base/TestStack.assets.json +0 -21
- package/test-cdk-project/cdk.out.base/TestStack.template.json +0 -115
- package/test-cdk-project/cdk.out.base/cdk.out +0 -1
- package/test-cdk-project/cdk.out.base/manifest.json +0 -503
- package/test-cdk-project/cdk.out.base/tree.json +0 -1
- package/test-cdk-project/cdk.out.target/TestStack.assets.json +0 -21
- package/test-cdk-project/cdk.out.target/TestStack.template.json +0 -183
- package/test-cdk-project/cdk.out.target/cdk.out +0 -1
- package/test-cdk-project/cdk.out.target/manifest.json +0 -521
- package/test-cdk-project/cdk.out.target/tree.json +0 -1
- package/test-cdk-project/package-lock.json +0 -422
- package/test-cdk-project/package.json +0 -17
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SingleTemplateReporter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Reporter for generating formatted output for single template cost analysis
|
|
6
|
+
*/
|
|
7
|
+
class SingleTemplateReporter {
|
|
8
|
+
/**
|
|
9
|
+
* Generate a formatted report from single template analysis result
|
|
10
|
+
*
|
|
11
|
+
* @param result - The analysis result
|
|
12
|
+
* @param format - Output format (text, json, or markdown)
|
|
13
|
+
* @param options - Optional formatting preferences
|
|
14
|
+
* @returns Formatted report string
|
|
15
|
+
*/
|
|
16
|
+
generateReport(result, format, options) {
|
|
17
|
+
const opts = {
|
|
18
|
+
showBreakdown: true,
|
|
19
|
+
showAssumptions: true,
|
|
20
|
+
groupByType: true,
|
|
21
|
+
sortBy: 'cost',
|
|
22
|
+
...options,
|
|
23
|
+
};
|
|
24
|
+
switch (format) {
|
|
25
|
+
case 'json':
|
|
26
|
+
return this.generateJsonReport(result);
|
|
27
|
+
case 'markdown':
|
|
28
|
+
return this.generateMarkdownReport(result, opts);
|
|
29
|
+
case 'text':
|
|
30
|
+
default:
|
|
31
|
+
return this.generateTextReport(result, opts);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generate JSON format report
|
|
36
|
+
*/
|
|
37
|
+
generateJsonReport(result) {
|
|
38
|
+
return JSON.stringify(result, null, 2);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Generate text format report
|
|
42
|
+
*/
|
|
43
|
+
generateTextReport(result, options) {
|
|
44
|
+
let report = '';
|
|
45
|
+
// Header
|
|
46
|
+
report += '='.repeat(80) + '\n';
|
|
47
|
+
report += 'Single Template Cost Analysis\n';
|
|
48
|
+
report += '='.repeat(80) + '\n\n';
|
|
49
|
+
// Summary
|
|
50
|
+
report += `Total Monthly Cost: $${result.totalMonthlyCost.toFixed(2)} ${result.currency}\n`;
|
|
51
|
+
report += `Analysis Date: ${result.metadata.analyzedAt.toISOString()}\n`;
|
|
52
|
+
report += `Region: ${result.metadata.region}\n`;
|
|
53
|
+
report += `Template Hash: ${result.metadata.templateHash}\n\n`;
|
|
54
|
+
// Resource counts
|
|
55
|
+
report += `Total Resources: ${result.metadata.resourceCount}\n`;
|
|
56
|
+
report += `Supported Resources: ${result.metadata.supportedResourceCount}\n`;
|
|
57
|
+
report += `Unsupported Resources: ${result.metadata.unsupportedResourceCount}\n\n`;
|
|
58
|
+
// Cost breakdown by confidence
|
|
59
|
+
if (result.costBreakdown.byConfidenceLevel.length > 0) {
|
|
60
|
+
report += '-'.repeat(80) + '\n';
|
|
61
|
+
report += 'Cost Confidence Breakdown\n';
|
|
62
|
+
report += '-'.repeat(80) + '\n\n';
|
|
63
|
+
for (const conf of result.costBreakdown.byConfidenceLevel) {
|
|
64
|
+
const percentage = ((conf.totalCost / result.totalMonthlyCost) * 100).toFixed(1);
|
|
65
|
+
report += `${conf.confidence.toUpperCase().padEnd(10)} ${conf.count} resources $${conf.totalCost.toFixed(2)} (${percentage}%)\n`;
|
|
66
|
+
}
|
|
67
|
+
report += '\n';
|
|
68
|
+
}
|
|
69
|
+
// Resource breakdown
|
|
70
|
+
if (options.showBreakdown && options.groupByType) {
|
|
71
|
+
report += '-'.repeat(80) + '\n';
|
|
72
|
+
report += 'Cost Breakdown by Resource Type\n';
|
|
73
|
+
report += '-'.repeat(80) + '\n\n';
|
|
74
|
+
for (const typeGroup of result.costBreakdown.byResourceType) {
|
|
75
|
+
const percentage = ((typeGroup.totalCost / result.totalMonthlyCost) * 100).toFixed(1);
|
|
76
|
+
report += `${typeGroup.resourceType}\n`;
|
|
77
|
+
report += ` Count: ${typeGroup.count}\n`;
|
|
78
|
+
report += ` Total Cost: $${typeGroup.totalCost.toFixed(2)} (${percentage}%)\n`;
|
|
79
|
+
// Show individual resources
|
|
80
|
+
const sortedResources = this.sortResources(typeGroup.resources, options.sortBy);
|
|
81
|
+
for (const resource of sortedResources) {
|
|
82
|
+
const conf = resource.monthlyCost.confidence;
|
|
83
|
+
const confIndicator = conf === 'high' ? '✓' : conf === 'medium' ? '~' : conf === 'low' ? '?' : '✗';
|
|
84
|
+
report += ` ${confIndicator} ${resource.logicalId.padEnd(40)} $${resource.monthlyCost.amount.toFixed(2)}\n`;
|
|
85
|
+
}
|
|
86
|
+
report += '\n';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Assumptions
|
|
90
|
+
if (options.showAssumptions && result.costBreakdown.assumptions.length > 0) {
|
|
91
|
+
report += '-'.repeat(80) + '\n';
|
|
92
|
+
report += 'Cost Calculation Assumptions\n';
|
|
93
|
+
report += '-'.repeat(80) + '\n\n';
|
|
94
|
+
for (const assumption of result.costBreakdown.assumptions) {
|
|
95
|
+
report += ` • ${assumption}\n`;
|
|
96
|
+
}
|
|
97
|
+
report += '\n';
|
|
98
|
+
}
|
|
99
|
+
// Legend
|
|
100
|
+
report += '-'.repeat(80) + '\n';
|
|
101
|
+
report += 'Legend: ✓ High confidence ~ Medium confidence ? Low confidence ✗ Unknown/Unsupported\n';
|
|
102
|
+
report += '='.repeat(80) + '\n';
|
|
103
|
+
return report;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Generate markdown format report
|
|
107
|
+
*/
|
|
108
|
+
generateMarkdownReport(result, options) {
|
|
109
|
+
let report = '';
|
|
110
|
+
// Header
|
|
111
|
+
report += '# Single Template Cost Analysis\n\n';
|
|
112
|
+
// Summary
|
|
113
|
+
report += '## Summary\n\n';
|
|
114
|
+
report += `**Total Monthly Cost:** $${result.totalMonthlyCost.toFixed(2)} ${result.currency}\n\n`;
|
|
115
|
+
report += `- **Analysis Date:** ${result.metadata.analyzedAt.toISOString()}\n`;
|
|
116
|
+
report += `- **Region:** ${result.metadata.region}\n`;
|
|
117
|
+
report += `- **Template Hash:** ${result.metadata.templateHash}\n\n`;
|
|
118
|
+
// Resource counts
|
|
119
|
+
report += '## Resource Overview\n\n';
|
|
120
|
+
report += `- **Total Resources:** ${result.metadata.resourceCount}\n`;
|
|
121
|
+
report += `- **Supported Resources:** ${result.metadata.supportedResourceCount}\n`;
|
|
122
|
+
report += `- **Unsupported Resources:** ${result.metadata.unsupportedResourceCount}\n\n`;
|
|
123
|
+
// Cost breakdown by confidence
|
|
124
|
+
if (result.costBreakdown.byConfidenceLevel.length > 0) {
|
|
125
|
+
report += '## Cost Confidence Breakdown\n\n';
|
|
126
|
+
report += '| Confidence | Resources | Cost | Percentage |\n';
|
|
127
|
+
report += '|------------|-----------|------|------------|\n';
|
|
128
|
+
for (const conf of result.costBreakdown.byConfidenceLevel) {
|
|
129
|
+
const percentage = ((conf.totalCost / result.totalMonthlyCost) * 100).toFixed(1);
|
|
130
|
+
report += `| ${conf.confidence} | ${conf.count} | $${conf.totalCost.toFixed(2)} | ${percentage}% |\n`;
|
|
131
|
+
}
|
|
132
|
+
report += '\n';
|
|
133
|
+
}
|
|
134
|
+
// Resource breakdown by type
|
|
135
|
+
if (options.showBreakdown && options.groupByType) {
|
|
136
|
+
report += '## Cost Breakdown by Resource Type\n\n';
|
|
137
|
+
for (const typeGroup of result.costBreakdown.byResourceType) {
|
|
138
|
+
const percentage = ((typeGroup.totalCost / result.totalMonthlyCost) * 100).toFixed(1);
|
|
139
|
+
report += `### ${typeGroup.resourceType}\n\n`;
|
|
140
|
+
report += `- **Count:** ${typeGroup.count}\n`;
|
|
141
|
+
report += `- **Total Cost:** $${typeGroup.totalCost.toFixed(2)} (${percentage}%)\n\n`;
|
|
142
|
+
// Show individual resources in table
|
|
143
|
+
report += '| Resource | Cost | Confidence |\n';
|
|
144
|
+
report += '|----------|------|------------|\n';
|
|
145
|
+
const sortedResources = this.sortResources(typeGroup.resources, options.sortBy);
|
|
146
|
+
for (const resource of sortedResources) {
|
|
147
|
+
const conf = resource.monthlyCost.confidence;
|
|
148
|
+
report += `| ${resource.logicalId} | $${resource.monthlyCost.amount.toFixed(2)} | ${conf} |\n`;
|
|
149
|
+
}
|
|
150
|
+
report += '\n';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Assumptions
|
|
154
|
+
if (options.showAssumptions && result.costBreakdown.assumptions.length > 0) {
|
|
155
|
+
report += '## Cost Calculation Assumptions\n\n';
|
|
156
|
+
for (const assumption of result.costBreakdown.assumptions) {
|
|
157
|
+
report += `- ${assumption}\n`;
|
|
158
|
+
}
|
|
159
|
+
report += '\n';
|
|
160
|
+
}
|
|
161
|
+
// Legend
|
|
162
|
+
report += '---\n\n';
|
|
163
|
+
report += '*Confidence levels: high (✓), medium (~), low (?), unknown/unsupported (✗)*\n';
|
|
164
|
+
return report;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Sort resources based on specified criteria
|
|
168
|
+
*/
|
|
169
|
+
sortResources(resources, sortBy) {
|
|
170
|
+
const sorted = [...resources];
|
|
171
|
+
switch (sortBy) {
|
|
172
|
+
case 'cost':
|
|
173
|
+
sorted.sort((a, b) => b.monthlyCost.amount - a.monthlyCost.amount);
|
|
174
|
+
break;
|
|
175
|
+
case 'name':
|
|
176
|
+
sorted.sort((a, b) => a.logicalId.localeCompare(b.logicalId));
|
|
177
|
+
break;
|
|
178
|
+
case 'type':
|
|
179
|
+
sorted.sort((a, b) => a.type.localeCompare(b.type));
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
return sorted;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.SingleTemplateReporter = SingleTemplateReporter;
|
|
186
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2luZ2xlVGVtcGxhdGVSZXBvcnRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZXBvcnRlci9TaW5nbGVUZW1wbGF0ZVJlcG9ydGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUtBOztHQUVHO0FBQ0gsTUFBYSxzQkFBc0I7SUFDakM7Ozs7Ozs7T0FPRztJQUNILGNBQWMsQ0FDWixNQUFnQyxFQUNoQyxNQUFvQyxFQUNwQyxPQUFxQztRQUVyQyxNQUFNLElBQUksR0FBRztZQUNYLGFBQWEsRUFBRSxJQUFJO1lBQ25CLGVBQWUsRUFBRSxJQUFJO1lBQ3JCLFdBQVcsRUFBRSxJQUFJO1lBQ2pCLE1BQU0sRUFBRSxNQUFrQztZQUMxQyxHQUFHLE9BQU87U0FDWCxDQUFDO1FBRUYsUUFBUSxNQUFNLEVBQUUsQ0FBQztZQUNmLEtBQUssTUFBTTtnQkFDVCxPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUN6QyxLQUFLLFVBQVU7Z0JBQ2IsT0FBTyxJQUFJLENBQUMsc0JBQXNCLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQ25ELEtBQUssTUFBTSxDQUFDO1lBQ1o7Z0JBQ0UsT0FBTyxJQUFJLENBQUMsa0JBQWtCLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ2pELENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxrQkFBa0IsQ0FBQyxNQUFnQztRQUN6RCxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRUQ7O09BRUc7SUFDSyxrQkFBa0IsQ0FDeEIsTUFBZ0MsRUFDaEMsT0FBOEM7UUFFOUMsSUFBSSxNQUFNLEdBQUcsRUFBRSxDQUFDO1FBRWhCLFNBQVM7UUFDVCxNQUFNLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUM7UUFD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R0FBRyxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUM7UUFFOUIsUUFBUSxNQUFNLEVBQUUsQ0FBQztZQUNmLEtBQUssTUFBTTtnQkFDVCxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDbkUsTUFBTTtZQUNSLEtBQUssTUFBTTtnQkFDVCxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUM7Z0JBQzlELE1BQU07WUFDUixLQUFLLE1BQU07Z0JBQ1QsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2dCQUNwRCxNQUFNO1FBQ1YsQ0FBQztRQUVELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7Q0FDRjtBQTdORCx3REE2TkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBTaW5nbGVUZW1wbGF0ZUNvc3RSZXN1bHQsXG4gIFNpbmdsZVRlbXBsYXRlUmVwb3J0T3B0aW9ucyxcbn0gZnJvbSAnLi4vYXBpL3NpbmdsZS10ZW1wbGF0ZS10eXBlcyc7XG5cbi8qKlxuICogUmVwb3J0ZXIgZm9yIGdlbmVyYXRpbmcgZm9ybWF0dGVkIG91dHB1dCBmb3Igc2luZ2xlIHRlbXBsYXRlIGNvc3QgYW5hbHlzaXNcbiAqL1xuZXhwb3J0IGNsYXNzIFNpbmdsZVRlbXBsYXRlUmVwb3J0ZXIge1xuICAvKipcbiAgICogR2VuZXJhdGUgYSBmb3JtYXR0ZWQgcmVwb3J0IGZyb20gc2luZ2xlIHRlbXBsYXRlIGFuYWx5c2lzIHJlc3VsdFxuICAgKiBcbiAgICogQHBhcmFtIHJlc3VsdCAtIFRoZSBhbmFseXNpcyByZXN1bHRcbiAgICogQHBhcmFtIGZvcm1hdCAtIE91dHB1dCBmb3JtYXQgKHRleHQsIGpzb24sIG9yIG1hcmtkb3duKVxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIGZvcm1hdHRpbmcgcHJlZmVyZW5jZXNcbiAgICogQHJldHVybnMgRm9ybWF0dGVkIHJlcG9ydCBzdHJpbmdcbiAgICovXG4gIGdlbmVyYXRlUmVwb3J0KFxuICAgIHJlc3VsdDogU2luZ2xlVGVtcGxhdGVDb3N0UmVzdWx0LFxuICAgIGZvcm1hdDogJ3RleHQnIHwgJ2pzb24nIHwgJ21hcmtkb3duJyxcbiAgICBvcHRpb25zPzogU2luZ2xlVGVtcGxhdGVSZXBvcnRPcHRpb25zLFxuICApOiBzdHJpbmcge1xuICAgIGNvbnN0IG9wdHMgPSB7XG4gICAgICBzaG93QnJlYWtkb3duOiB0cnVlLFxuICAgICAgc2hvd0Fzc3VtcHRpb25zOiB0cnVlLFxuICAgICAgZ3JvdXBCeVR5cGU6IHRydWUsXG4gICAgICBzb3J0Qnk6ICdjb3N0JyBhcyAnY29zdCcgfCAnbmFtZScgfCAndHlwZScsXG4gICAgICAuLi5vcHRpb25zLFxuICAgIH07XG5cbiAgICBzd2l0Y2ggKGZvcm1hdCkge1xuICAgICAgY2FzZSAnanNvbic6XG4gICAgICAgIHJldHVybiB0aGlzLmdlbmVyYXRlSnNvblJlcG9ydChyZXN1bHQpO1xuICAgICAgY2FzZSAnbWFya2Rvd24nOlxuICAgICAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZU1hcmtkb3duUmVwb3J0KHJlc3VsdCwgb3B0cyk7XG4gICAgICBjYXNlICd0ZXh0JzpcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiB0aGlzLmdlbmVyYXRlVGV4dFJlcG9ydChyZXN1bHQsIG9wdHMpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBKU09OIGZvcm1hdCByZXBvcnRcbiAgICovXG4gIHByaXZhdGUgZ2VuZXJhdGVKc29uUmVwb3J0KHJlc3VsdDogU2luZ2xlVGVtcGxhdGVDb3N0UmVzdWx0KTogc3RyaW5nIHtcbiAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkocmVzdWx0LCBudWxsLCAyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSB0ZXh0IGZvcm1hdCByZXBvcnRcbiAgICovXG4gIHByaXZhdGUgZ2VuZXJhdGVUZXh0UmVwb3J0KFxuICAgIHJlc3VsdDogU2luZ2xlVGVtcGxhdGVDb3N0UmVzdWx0LFxuICAgIG9wdGlvbnM6IFJlcXVpcmVkPFNpbmdsZVRlbXBsYXRlUmVwb3J0T3B0aW9ucz4sXG4gICk6IHN0cmluZyB7XG4gICAgbGV0IHJlcG9ydCA9ICcnO1xuXG4gICAgLy8gSGVhZGVyXG4gICAgcmVwb3J0ICs9ICc9Jy5yZXBlYXQoODApICsgJ1xcbic7XG4gICAgcmVwb3J0ICs9ICdTaW5nbGUgVGVtcGxhdGUgQ29zdCBBbmFseXNpc1xcbic7XG4gICAgcmVwb3J0ICs9ICc9Jy5yZXBlYXQoODApICsgJ1xcblxcbic7XG5cbiAgICAvLyBTdW1tYXJ5XG4gICAgcmVwb3J0ICs9IGBUb3RhbCBNb250aGx5IENvc3Q6ICQke3Jlc3VsdC50b3RhbE1vbnRobHlDb3N0LnRvRml4ZWQoMil9ICR7cmVzdWx0LmN1cnJlbmN5fVxcbmA7XG4gICAgcmVwb3J0ICs9IGBBbmFseXNpcyBEYXRlOiAke3Jlc3VsdC5tZXRhZGF0YS5hbmFseXplZEF0LnRvSVNPU3RyaW5nKCl9XFxuYDtcbiAgICByZXBvcnQgKz0gYFJlZ2lvbjogJHtyZXN1bHQubWV0YWRhdGEucmVnaW9ufVxcbmA7XG4gICAgcmVwb3J0ICs9IGBUZW1wbGF0ZSBIYXNoOiAke3Jlc3VsdC5tZXRhZGF0YS50ZW1wbGF0ZUhhc2h9XFxuXFxuYDtcblxuICAgIC8vIFJlc291cmNlIGNvdW50c1xuICAgIHJlcG9ydCArPSBgVG90YWwgUmVzb3VyY2VzOiAke3Jlc3VsdC5tZXRhZGF0YS5yZXNvdXJjZUNvdW50fVxcbmA7XG4gICAgcmVwb3J0ICs9IGBTdXBwb3J0ZWQgUmVzb3VyY2VzOiAke3Jlc3VsdC5tZXRhZGF0YS5zdXBwb3J0ZWRSZXNvdXJjZUNvdW50fVxcbmA7XG4gICAgcmVwb3J0ICs9IGBVbnN1cHBvcnRlZCBSZXNvdXJjZXM6ICR7cmVzdWx0Lm1ldGFkYXRhLnVuc3VwcG9ydGVkUmVzb3VyY2VDb3VudH1cXG5cXG5gO1xuXG4gICAgLy8gQ29zdCBicmVha2Rvd24gYnkgY29uZmlkZW5jZVxuICAgIGlmIChyZXN1bHQuY29zdEJyZWFrZG93bi5ieUNvbmZpZGVuY2VMZXZlbC5sZW5ndGggPiAwKSB7XG4gICAgICByZXBvcnQgKz0gJy0nLnJlcGVhdCg4MCkgKyAnXFxuJztcbiAgICAgIHJlcG9ydCArPSAnQ29zdCBDb25maWRlbmNlIEJyZWFrZG93blxcbic7XG4gICAgICByZXBvcnQgKz0gJy0nLnJlcGVhdCg4MCkgKyAnXFxuXFxuJztcblxuICAgICAgZm9yIChjb25zdCBjb25mIG9mIHJlc3VsdC5jb3N0QnJlYWtkb3duLmJ5Q29uZmlkZW5jZUxldmVsKSB7XG4gICAgICAgIGNvbnN0IHBlcmNlbnRhZ2UgPSAoKGNvbmYudG90YWxDb3N0IC8gcmVzdWx0LnRvdGFsTW9udGhseUNvc3QpICogMTAwKS50b0ZpeGVkKDEpO1xuICAgICAgICByZXBvcnQgKz0gYCR7Y29uZi5jb25maWRlbmNlLnRvVXBwZXJDYXNlKCkucGFkRW5kKDEwKX0gJHtjb25mLmNvdW50fSByZXNvdXJjZXMgICQke2NvbmYudG90YWxDb3N0LnRvRml4ZWQoMil9ICgke3BlcmNlbnRhZ2V9JSlcXG5gO1xuICAgICAgfVxuICAgICAgcmVwb3J0ICs9ICdcXG4nO1xuICAgIH1cblxuICAgIC8vIFJlc291cmNlIGJyZWFrZG93blxuICAgIGlmIChvcHRpb25zLnNob3dCcmVha2Rvd24gJiYgb3B0aW9ucy5ncm91cEJ5VHlwZSkge1xuICAgICAgcmVwb3J0ICs9ICctJy5yZXBlYXQoODApICsgJ1xcbic7XG4gICAgICByZXBvcnQgKz0gJ0Nvc3QgQnJlYWtkb3duIGJ5IFJlc291cmNlIFR5cGVcXG4nO1xuICAgICAgcmVwb3J0ICs9ICctJy5yZXBlYXQoODApICsgJ1xcblxcbic7XG5cbiAgICAgIGZvciAoY29uc3QgdHlwZUdyb3VwIG9mIHJlc3VsdC5jb3N0QnJlYWtkb3duLmJ5UmVzb3VyY2VUeXBlKSB7XG4gICAgICAgIGNvbnN0IHBlcmNlbnRhZ2UgPSAoKHR5cGVHcm91cC50b3RhbENvc3QgLyByZXN1bHQudG90YWxNb250aGx5Q29zdCkgKiAxMDApLnRvRml4ZWQoMSk7XG4gICAgICAgIHJlcG9ydCArPSBgJHt0eXBlR3JvdXAucmVzb3VyY2VUeXBlfVxcbmA7XG4gICAgICAgIHJlcG9ydCArPSBgICBDb3VudDogJHt0eXBlR3JvdXAuY291bnR9XFxuYDtcbiAgICAgICAgcmVwb3J0ICs9IGAgIFRvdGFsIENvc3Q6ICQke3R5cGVHcm91cC50b3RhbENvc3QudG9GaXhlZCgyKX0gKCR7cGVyY2VudGFnZX0lKVxcbmA7XG5cbiAgICAgICAgLy8gU2hvdyBpbmRpdmlkdWFsIHJlc291cmNlc1xuICAgICAgICBjb25zdCBzb3J0ZWRSZXNvdXJjZXMgPSB0aGlzLnNvcnRSZXNvdXJjZXModHlwZUdyb3VwLnJlc291cmNlcywgb3B0aW9ucy5zb3J0QnkpO1xuICAgICAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIHNvcnRlZFJlc291cmNlcykge1xuICAgICAgICAgIGNvbnN0IGNvbmYgPSByZXNvdXJjZS5tb250aGx5Q29zdC5jb25maWRlbmNlO1xuICAgICAgICAgIGNvbnN0IGNvbmZJbmRpY2F0b3IgPSBjb25mID09PSAnaGlnaCcgPyAn4pyTJyA6IGNvbmYgPT09ICdtZWRpdW0nID8gJ34nIDogY29uZiA9PT0gJ2xvdycgPyAnPycgOiAn4pyXJztcbiAgICAgICAgICByZXBvcnQgKz0gYCAgICAke2NvbmZJbmRpY2F0b3J9ICR7cmVzb3VyY2UubG9naWNhbElkLnBhZEVuZCg0MCl9ICQke3Jlc291cmNlLm1vbnRobHlDb3N0LmFtb3VudC50b0ZpeGVkKDIpfVxcbmA7XG4gICAgICAgIH1cbiAgICAgICAgcmVwb3J0ICs9ICdcXG4nO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEFzc3VtcHRpb25zXG4gICAgaWYgKG9wdGlvbnMuc2hvd0Fzc3VtcHRpb25zICYmIHJlc3VsdC5jb3N0QnJlYWtkb3duLmFzc3VtcHRpb25zLmxlbmd0aCA+IDApIHtcbiAgICAgIHJlcG9ydCArPSAnLScucmVwZWF0KDgwKSArICdcXG4nO1xuICAgICAgcmVwb3J0ICs9ICdDb3N0IENhbGN1bGF0aW9uIEFzc3VtcHRpb25zXFxuJztcbiAgICAgIHJlcG9ydCArPSAnLScucmVwZWF0KDgwKSArICdcXG5cXG4nO1xuXG4gICAgICBmb3IgKGNvbnN0IGFzc3VtcHRpb24gb2YgcmVzdWx0LmNvc3RCcmVha2Rvd24uYXNzdW1wdGlvbnMpIHtcbiAgICAgICAgcmVwb3J0ICs9IGAgIOKAoiAke2Fzc3VtcHRpb259XFxuYDtcbiAgICAgIH1cbiAgICAgIHJlcG9ydCArPSAnXFxuJztcbiAgICB9XG5cbiAgICAvLyBMZWdlbmRcbiAgICByZXBvcnQgKz0gJy0nLnJlcGVhdCg4MCkgKyAnXFxuJztcbiAgICByZXBvcnQgKz0gJ0xlZ2VuZDog4pyTIEhpZ2ggY29uZmlkZW5jZSAgfiBNZWRpdW0gY29uZmlkZW5jZSAgPyBMb3cgY29uZmlkZW5jZSAg4pyXIFVua25vd24vVW5zdXBwb3J0ZWRcXG4nO1xuICAgIHJlcG9ydCArPSAnPScucmVwZWF0KDgwKSArICdcXG4nO1xuXG4gICAgcmV0dXJuIHJlcG9ydDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBtYXJrZG93biBmb3JtYXQgcmVwb3J0XG4gICAqL1xuICBwcml2YXRlIGdlbmVyYXRlTWFya2Rvd25SZXBvcnQoXG4gICAgcmVzdWx0OiBTaW5nbGVUZW1wbGF0ZUNvc3RSZXN1bHQsXG4gICAgb3B0aW9uczogUmVxdWlyZWQ8U2luZ2xlVGVtcGxhdGVSZXBvcnRPcHRpb25zPixcbiAgKTogc3RyaW5nIHtcbiAgICBsZXQgcmVwb3J0ID0gJyc7XG5cbiAgICAvLyBIZWFkZXJcbiAgICByZXBvcnQgKz0gJyMgU2luZ2xlIFRlbXBsYXRlIENvc3QgQW5hbHlzaXNcXG5cXG4nO1xuXG4gICAgLy8gU3VtbWFyeVxuICAgIHJlcG9ydCArPSAnIyMgU3VtbWFyeVxcblxcbic7XG4gICAgcmVwb3J0ICs9IGAqKlRvdGFsIE1vbnRobHkgQ29zdDoqKiAkJHtyZXN1bHQudG90YWxNb250aGx5Q29zdC50b0ZpeGVkKDIpfSAke3Jlc3VsdC5jdXJyZW5jeX1cXG5cXG5gO1xuICAgIHJlcG9ydCArPSBgLSAqKkFuYWx5c2lzIERhdGU6KiogJHtyZXN1bHQubWV0YWRhdGEuYW5hbHl6ZWRBdC50b0lTT1N0cmluZygpfVxcbmA7XG4gICAgcmVwb3J0ICs9IGAtICoqUmVnaW9uOioqICR7cmVzdWx0Lm1ldGFkYXRhLnJlZ2lvbn1cXG5gO1xuICAgIHJlcG9ydCArPSBgLSAqKlRlbXBsYXRlIEhhc2g6KiogJHtyZXN1bHQubWV0YWRhdGEudGVtcGxhdGVIYXNofVxcblxcbmA7XG5cbiAgICAvLyBSZXNvdXJjZSBjb3VudHNcbiAgICByZXBvcnQgKz0gJyMjIFJlc291cmNlIE92ZXJ2aWV3XFxuXFxuJztcbiAgICByZXBvcnQgKz0gYC0gKipUb3RhbCBSZXNvdXJjZXM6KiogJHtyZXN1bHQubWV0YWRhdGEucmVzb3VyY2VDb3VudH1cXG5gO1xuICAgIHJlcG9ydCArPSBgLSAqKlN1cHBvcnRlZCBSZXNvdXJjZXM6KiogJHtyZXN1bHQubWV0YWRhdGEuc3VwcG9ydGVkUmVzb3VyY2VDb3VudH1cXG5gO1xuICAgIHJlcG9ydCArPSBgLSAqKlVuc3VwcG9ydGVkIFJlc291cmNlczoqKiAke3Jlc3VsdC5tZXRhZGF0YS51bnN1cHBvcnRlZFJlc291cmNlQ291bnR9XFxuXFxuYDtcblxuICAgIC8vIENvc3QgYnJlYWtkb3duIGJ5IGNvbmZpZGVuY2VcbiAgICBpZiAocmVzdWx0LmNvc3RCcmVha2Rvd24uYnlDb25maWRlbmNlTGV2ZWwubGVuZ3RoID4gMCkge1xuICAgICAgcmVwb3J0ICs9ICcjIyBDb3N0IENvbmZpZGVuY2UgQnJlYWtkb3duXFxuXFxuJztcbiAgICAgIHJlcG9ydCArPSAnfCBDb25maWRlbmNlIHwgUmVzb3VyY2VzIHwgQ29zdCB8IFBlcmNlbnRhZ2UgfFxcbic7XG4gICAgICByZXBvcnQgKz0gJ3wtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS18LS0tLS0tfC0tLS0tLS0tLS0tLXxcXG4nO1xuXG4gICAgICBmb3IgKGNvbnN0IGNvbmYgb2YgcmVzdWx0LmNvc3RCcmVha2Rvd24uYnlDb25maWRlbmNlTGV2ZWwpIHtcbiAgICAgICAgY29uc3QgcGVyY2VudGFnZSA9ICgoY29uZi50b3RhbENvc3QgLyByZXN1bHQudG90YWxNb250aGx5Q29zdCkgKiAxMDApLnRvRml4ZWQoMSk7XG4gICAgICAgIHJlcG9ydCArPSBgfCAke2NvbmYuY29uZmlkZW5jZX0gfCAke2NvbmYuY291bnR9IHwgJCR7Y29uZi50b3RhbENvc3QudG9GaXhlZCgyKX0gfCAke3BlcmNlbnRhZ2V9JSB8XFxuYDtcbiAgICAgIH1cbiAgICAgIHJlcG9ydCArPSAnXFxuJztcbiAgICB9XG5cbiAgICAvLyBSZXNvdXJjZSBicmVha2Rvd24gYnkgdHlwZVxuICAgIGlmIChvcHRpb25zLnNob3dCcmVha2Rvd24gJiYgb3B0aW9ucy5ncm91cEJ5VHlwZSkge1xuICAgICAgcmVwb3J0ICs9ICcjIyBDb3N0IEJyZWFrZG93biBieSBSZXNvdXJjZSBUeXBlXFxuXFxuJztcblxuICAgICAgZm9yIChjb25zdCB0eXBlR3JvdXAgb2YgcmVzdWx0LmNvc3RCcmVha2Rvd24uYnlSZXNvdXJjZVR5cGUpIHtcbiAgICAgICAgY29uc3QgcGVyY2VudGFnZSA9ICgodHlwZUdyb3VwLnRvdGFsQ29zdCAvIHJlc3VsdC50b3RhbE1vbnRobHlDb3N0KSAqIDEwMCkudG9GaXhlZCgxKTtcbiAgICAgICAgcmVwb3J0ICs9IGAjIyMgJHt0eXBlR3JvdXAucmVzb3VyY2VUeXBlfVxcblxcbmA7XG4gICAgICAgIHJlcG9ydCArPSBgLSAqKkNvdW50OioqICR7dHlwZUdyb3VwLmNvdW50fVxcbmA7XG4gICAgICAgIHJlcG9ydCArPSBgLSAqKlRvdGFsIENvc3Q6KiogJCR7dHlwZUdyb3VwLnRvdGFsQ29zdC50b0ZpeGVkKDIpfSAoJHtwZXJjZW50YWdlfSUpXFxuXFxuYDtcblxuICAgICAgICAvLyBTaG93IGluZGl2aWR1YWwgcmVzb3VyY2VzIGluIHRhYmxlXG4gICAgICAgIHJlcG9ydCArPSAnfCBSZXNvdXJjZSB8IENvc3QgfCBDb25maWRlbmNlIHxcXG4nO1xuICAgICAgICByZXBvcnQgKz0gJ3wtLS0tLS0tLS0tfC0tLS0tLXwtLS0tLS0tLS0tLS18XFxuJztcblxuICAgICAgICBjb25zdCBzb3J0ZWRSZXNvdXJjZXMgPSB0aGlzLnNvcnRSZXNvdXJjZXModHlwZUdyb3VwLnJlc291cmNlcywgb3B0aW9ucy5zb3J0QnkpO1xuICAgICAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIHNvcnRlZFJlc291cmNlcykge1xuICAgICAgICAgIGNvbnN0IGNvbmYgPSByZXNvdXJjZS5tb250aGx5Q29zdC5jb25maWRlbmNlO1xuICAgICAgICAgIHJlcG9ydCArPSBgfCAke3Jlc291cmNlLmxvZ2ljYWxJZH0gfCAkJHtyZXNvdXJjZS5tb250aGx5Q29zdC5hbW91bnQudG9GaXhlZCgyKX0gfCAke2NvbmZ9IHxcXG5gO1xuICAgICAgICB9XG4gICAgICAgIHJlcG9ydCArPSAnXFxuJztcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBc3N1bXB0aW9uc1xuICAgIGlmIChvcHRpb25zLnNob3dBc3N1bXB0aW9ucyAmJiByZXN1bHQuY29zdEJyZWFrZG93bi5hc3N1bXB0aW9ucy5sZW5ndGggPiAwKSB7XG4gICAgICByZXBvcnQgKz0gJyMjIENvc3QgQ2FsY3VsYXRpb24gQXNzdW1wdGlvbnNcXG5cXG4nO1xuICAgICAgZm9yIChjb25zdCBhc3N1bXB0aW9uIG9mIHJlc3VsdC5jb3N0QnJlYWtkb3duLmFzc3VtcHRpb25zKSB7XG4gICAgICAgIHJlcG9ydCArPSBgLSAke2Fzc3VtcHRpb259XFxuYDtcbiAgICAgIH1cbiAgICAgIHJlcG9ydCArPSAnXFxuJztcbiAgICB9XG5cbiAgICAvLyBMZWdlbmRcbiAgICByZXBvcnQgKz0gJy0tLVxcblxcbic7XG4gICAgcmVwb3J0ICs9ICcqQ29uZmlkZW5jZSBsZXZlbHM6IGhpZ2ggKOKckyksIG1lZGl1bSAofiksIGxvdyAoPyksIHVua25vd24vdW5zdXBwb3J0ZWQgKOKclykqXFxuJztcblxuICAgIHJldHVybiByZXBvcnQ7XG4gIH1cblxuICAvKipcbiAgICogU29ydCByZXNvdXJjZXMgYmFzZWQgb24gc3BlY2lmaWVkIGNyaXRlcmlhXG4gICAqL1xuICBwcml2YXRlIHNvcnRSZXNvdXJjZXMoXG4gICAgcmVzb3VyY2VzOiBBcnJheTx7IGxvZ2ljYWxJZDogc3RyaW5nOyB0eXBlOiBzdHJpbmc7IG1vbnRobHlDb3N0OiBhbnkgfT4sXG4gICAgc29ydEJ5OiAnY29zdCcgfCAnbmFtZScgfCAndHlwZScsXG4gICk6IEFycmF5PHsgbG9naWNhbElkOiBzdHJpbmc7IHR5cGU6IHN0cmluZzsgbW9udGhseUNvc3Q6IGFueSB9PiB7XG4gICAgY29uc3Qgc29ydGVkID0gWy4uLnJlc291cmNlc107XG5cbiAgICBzd2l0Y2ggKHNvcnRCeSkge1xuICAgICAgY2FzZSAnY29zdCc6XG4gICAgICAgIHNvcnRlZC5zb3J0KChhLCBiKSA9PiBiLm1vbnRobHlDb3N0LmFtb3VudCAtIGEubW9udGhseUNvc3QuYW1vdW50KTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICduYW1lJzpcbiAgICAgICAgc29ydGVkLnNvcnQoKGEsIGIpID0+IGEubG9naWNhbElkLmxvY2FsZUNvbXBhcmUoYi5sb2dpY2FsSWQpKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd0eXBlJzpcbiAgICAgICAgc29ydGVkLnNvcnQoKGEsIGIpID0+IGEudHlwZS5sb2NhbGVDb21wYXJlKGIudHlwZSkpO1xuICAgICAgICBicmVhaztcbiAgICB9XG5cbiAgICByZXR1cm4gc29ydGVkO1xuICB9XG59XG4iXX0=
|
package/dist/reporter/index.d.ts
CHANGED
package/dist/reporter/index.js
CHANGED
|
@@ -14,8 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.Reporter = void 0;
|
|
17
|
+
exports.SingleTemplateReporter = exports.Reporter = void 0;
|
|
18
18
|
var Reporter_1 = require("./Reporter");
|
|
19
19
|
Object.defineProperty(exports, "Reporter", { enumerable: true, get: function () { return Reporter_1.Reporter; } });
|
|
20
|
+
var SingleTemplateReporter_1 = require("./SingleTemplateReporter");
|
|
21
|
+
Object.defineProperty(exports, "SingleTemplateReporter", { enumerable: true, get: function () { return SingleTemplateReporter_1.SingleTemplateReporter; } });
|
|
20
22
|
__exportStar(require("./types"), exports);
|
|
21
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVwb3J0ZXIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSx1Q0FBc0M7QUFBN0Isb0dBQUEsUUFBUSxPQUFBO0FBQ2pCLG1FQUFrRTtBQUF6RCxnSUFBQSxzQkFBc0IsT0FBQTtBQUMvQiwwQ0FBd0IiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgeyBSZXBvcnRlciB9IGZyb20gJy4vUmVwb3J0ZXInO1xuZXhwb3J0IHsgU2luZ2xlVGVtcGxhdGVSZXBvcnRlciB9IGZyb20gJy4vU2luZ2xlVGVtcGxhdGVSZXBvcnRlcic7XG5leHBvcnQgKiBmcm9tICcuL3R5cGVzJztcbiJdfQ==
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# Single Template Cost Analysis - Quick Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The single template cost analysis feature allows you to estimate the monthly costs of AWS resources defined in a CloudFormation template without requiring a comparison baseline.
|
|
6
|
+
|
|
7
|
+
## CLI Usage
|
|
8
|
+
|
|
9
|
+
### Basic Analysis
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Analyze a template with default settings (eu-central-1)
|
|
13
|
+
cdk-cost-analyzer analyze template.json
|
|
14
|
+
|
|
15
|
+
# Specify a region
|
|
16
|
+
cdk-cost-analyzer analyze template.yaml --region us-east-1
|
|
17
|
+
|
|
18
|
+
# Different output formats
|
|
19
|
+
cdk-cost-analyzer analyze template.json --format text # Default, human-readable
|
|
20
|
+
cdk-cost-analyzer analyze template.json --format json # Machine-readable
|
|
21
|
+
cdk-cost-analyzer analyze template.json --format markdown # Documentation-friendly
|
|
22
|
+
|
|
23
|
+
# Enable debug logging
|
|
24
|
+
cdk-cost-analyzer analyze template.json --debug
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### With Configuration
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Use a configuration file for usage assumptions and exclusions
|
|
31
|
+
cdk-cost-analyzer analyze template.json --config .cdk-cost-analyzer.yml --region us-east-1
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Example configuration file (`.cdk-cost-analyzer.yml`):
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
usageAssumptions:
|
|
38
|
+
lambda:
|
|
39
|
+
invocationsPerMonth: 10000000
|
|
40
|
+
averageDurationMs: 500
|
|
41
|
+
s3:
|
|
42
|
+
storageGB: 1000
|
|
43
|
+
requestsPerMonth: 1000000
|
|
44
|
+
natGateway:
|
|
45
|
+
dataProcessedGB: 500
|
|
46
|
+
|
|
47
|
+
exclusions:
|
|
48
|
+
resourceTypes:
|
|
49
|
+
- AWS::IAM::Role
|
|
50
|
+
- AWS::Logs::LogGroup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API Usage
|
|
54
|
+
|
|
55
|
+
### TypeScript/JavaScript
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { analyzeSingleTemplate } from 'cdk-cost-analyzer';
|
|
59
|
+
|
|
60
|
+
// Basic analysis
|
|
61
|
+
const result = await analyzeSingleTemplate({
|
|
62
|
+
template: templateContent, // JSON or YAML string
|
|
63
|
+
region: 'us-east-1',
|
|
64
|
+
format: 'text'
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log(result.summary);
|
|
68
|
+
console.log(`Total cost: $${result.totalMonthlyCost.toFixed(2)}`);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### With Configuration
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const result = await analyzeSingleTemplate({
|
|
75
|
+
template: templateContent,
|
|
76
|
+
region: 'us-east-1',
|
|
77
|
+
config: {
|
|
78
|
+
usageAssumptions: {
|
|
79
|
+
lambda: {
|
|
80
|
+
invocationsPerMonth: 10000000,
|
|
81
|
+
averageDurationMs: 500
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
excludedResourceTypes: ['AWS::IAM::Role']
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Accessing Detailed Results
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const result = await analyzeSingleTemplate({
|
|
93
|
+
template: templateContent,
|
|
94
|
+
region: 'us-east-1'
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Total cost
|
|
98
|
+
console.log(`Total: $${result.totalMonthlyCost} ${result.currency}`);
|
|
99
|
+
|
|
100
|
+
// Metadata
|
|
101
|
+
console.log(`Total resources: ${result.metadata.resourceCount}`);
|
|
102
|
+
console.log(`Supported: ${result.metadata.supportedResourceCount}`);
|
|
103
|
+
console.log(`Unsupported: ${result.metadata.unsupportedResourceCount}`);
|
|
104
|
+
|
|
105
|
+
// Resource costs
|
|
106
|
+
result.resourceCosts.forEach(rc => {
|
|
107
|
+
console.log(`${rc.logicalId} (${rc.type}): $${rc.monthlyCost.amount}`);
|
|
108
|
+
console.log(` Confidence: ${rc.monthlyCost.confidence}`);
|
|
109
|
+
console.log(` Assumptions: ${rc.monthlyCost.assumptions.join(', ')}`);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Cost breakdown by type
|
|
113
|
+
result.costBreakdown.byResourceType.forEach(group => {
|
|
114
|
+
console.log(`${group.resourceType}: ${group.count} resources, $${group.totalCost}`);
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Output Formats
|
|
119
|
+
|
|
120
|
+
### Text Format (Default)
|
|
121
|
+
|
|
122
|
+
Human-readable report with:
|
|
123
|
+
- Total monthly cost
|
|
124
|
+
- Resource counts and support status
|
|
125
|
+
- Cost confidence breakdown
|
|
126
|
+
- Cost breakdown by resource type
|
|
127
|
+
- Individual resource costs with visual indicators
|
|
128
|
+
- Assumptions list
|
|
129
|
+
- Legend
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
```
|
|
133
|
+
================================================================================
|
|
134
|
+
Single Template Cost Analysis
|
|
135
|
+
================================================================================
|
|
136
|
+
|
|
137
|
+
Total Monthly Cost: $123.45 USD
|
|
138
|
+
Analysis Date: 2024-01-20T10:30:00.000Z
|
|
139
|
+
Region: us-east-1
|
|
140
|
+
Template Hash: abc123def456
|
|
141
|
+
|
|
142
|
+
Total Resources: 5
|
|
143
|
+
Supported: 4
|
|
144
|
+
Unsupported: 1
|
|
145
|
+
|
|
146
|
+
--------------------------------------------------------------------------------
|
|
147
|
+
Cost Confidence Breakdown
|
|
148
|
+
--------------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
HIGH 2 resources $100.00 (81.0%)
|
|
151
|
+
MEDIUM 2 resources $23.45 (19.0%)
|
|
152
|
+
|
|
153
|
+
--------------------------------------------------------------------------------
|
|
154
|
+
Cost Breakdown by Resource Type
|
|
155
|
+
--------------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
AWS::EC2::Instance
|
|
158
|
+
Count: 1
|
|
159
|
+
Total Cost: $100.00 (81.0%)
|
|
160
|
+
✓ MyInstance $100.00
|
|
161
|
+
|
|
162
|
+
AWS::S3::Bucket
|
|
163
|
+
Count: 1
|
|
164
|
+
Total Cost: $23.45 (19.0%)
|
|
165
|
+
~ MyBucket $23.45
|
|
166
|
+
|
|
167
|
+
...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### JSON Format
|
|
171
|
+
|
|
172
|
+
Machine-readable format for programmatic processing:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"totalMonthlyCost": 123.45,
|
|
177
|
+
"currency": "USD",
|
|
178
|
+
"resourceCosts": [...],
|
|
179
|
+
"costBreakdown": {
|
|
180
|
+
"byResourceType": [...],
|
|
181
|
+
"byConfidenceLevel": [...],
|
|
182
|
+
"assumptions": [...]
|
|
183
|
+
},
|
|
184
|
+
"metadata": {
|
|
185
|
+
"templateHash": "abc123def456",
|
|
186
|
+
"region": "us-east-1",
|
|
187
|
+
"analyzedAt": "2024-01-20T10:30:00.000Z",
|
|
188
|
+
"resourceCount": 5,
|
|
189
|
+
"supportedResourceCount": 4,
|
|
190
|
+
"unsupportedResourceCount": 1
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Markdown Format
|
|
196
|
+
|
|
197
|
+
Documentation-friendly format with tables:
|
|
198
|
+
|
|
199
|
+
```markdown
|
|
200
|
+
# Single Template Cost Analysis
|
|
201
|
+
|
|
202
|
+
## Summary
|
|
203
|
+
|
|
204
|
+
**Total Monthly Cost:** $123.45 USD
|
|
205
|
+
|
|
206
|
+
- **Analysis Date:** 2024-01-20T10:30:00.000Z
|
|
207
|
+
- **Region:** us-east-1
|
|
208
|
+
- **Template Hash:** abc123def456
|
|
209
|
+
|
|
210
|
+
## Resource Overview
|
|
211
|
+
|
|
212
|
+
- **Total Resources:** 5
|
|
213
|
+
- **Supported Resources:** 4
|
|
214
|
+
- **Unsupported Resources:** 1
|
|
215
|
+
|
|
216
|
+
## Cost Confidence Breakdown
|
|
217
|
+
|
|
218
|
+
| Confidence | Resources | Cost | Percentage |
|
|
219
|
+
|------------|-----------|------|------------|
|
|
220
|
+
| high | 2 | $100.00 | 81.0% |
|
|
221
|
+
| medium | 2 | $23.45 | 19.0% |
|
|
222
|
+
|
|
223
|
+
...
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Error Handling
|
|
227
|
+
|
|
228
|
+
### Missing Template File
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
$ cdk-cost-analyzer analyze missing.json
|
|
232
|
+
Error: Template file not found: missing.json
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Invalid Template
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
$ cdk-cost-analyzer analyze invalid.json
|
|
239
|
+
Error: Failed to parse template: Unexpected token...
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Missing AWS Credentials
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
$ cdk-cost-analyzer analyze template.json
|
|
246
|
+
Error: AWS credentials not configured. Please set AWS credentials using one of:
|
|
247
|
+
- AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables
|
|
248
|
+
- AWS_PROFILE environment variable
|
|
249
|
+
- AWS credentials file (~/.aws/credentials)
|
|
250
|
+
- For CI/CD, configure AWS credentials in your pipeline
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Confidence Levels
|
|
254
|
+
|
|
255
|
+
| Level | Symbol | Meaning |
|
|
256
|
+
|-------|--------|---------|
|
|
257
|
+
| high | ✓ | Pricing data available, minimal assumptions |
|
|
258
|
+
| medium | ~ | Some assumptions made about usage patterns |
|
|
259
|
+
| low | ? | Significant assumptions or limited pricing data |
|
|
260
|
+
| unknown | ✗ | Resource type not supported or no pricing data |
|
|
261
|
+
|
|
262
|
+
## Supported Resource Types
|
|
263
|
+
|
|
264
|
+
The analyzer supports 14+ AWS resource types including:
|
|
265
|
+
- EC2 Instances
|
|
266
|
+
- S3 Buckets
|
|
267
|
+
- Lambda Functions
|
|
268
|
+
- RDS Database Instances
|
|
269
|
+
- DynamoDB Tables
|
|
270
|
+
- ECS Services
|
|
271
|
+
- API Gateway (REST, HTTP, WebSocket)
|
|
272
|
+
- NAT Gateways
|
|
273
|
+
- Application Load Balancers
|
|
274
|
+
- Network Load Balancers
|
|
275
|
+
- VPC Endpoints
|
|
276
|
+
- CloudFront Distributions
|
|
277
|
+
- ElastiCache Clusters
|
|
278
|
+
|
|
279
|
+
See [CALCULATORS.md](../docs/CALCULATORS.md) for complete list and calculation details.
|
|
280
|
+
|
|
281
|
+
## Usage Assumptions
|
|
282
|
+
|
|
283
|
+
Default assumptions (can be overridden in config):
|
|
284
|
+
|
|
285
|
+
- **S3**: 100GB storage, 10,000 requests/month
|
|
286
|
+
- **Lambda**: 1M invocations/month, 1s average duration
|
|
287
|
+
- **NAT Gateway**: 500GB data processed/month
|
|
288
|
+
- **ALB**: 50 new connections/sec, 5,000 active/min, 1,000GB processed
|
|
289
|
+
- **CloudFront**: 100GB data transfer, 1M requests/month
|
|
290
|
+
|
|
291
|
+
All assumptions are displayed in the output report.
|
|
292
|
+
|
|
293
|
+
## Common Use Cases
|
|
294
|
+
|
|
295
|
+
### 1. Pre-Deployment Cost Estimation
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
# Before deploying a new stack
|
|
299
|
+
cdk synth > template.json
|
|
300
|
+
cdk-cost-analyzer analyze template.json --region us-east-1
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 2. Template Validation
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Check if a template can be analyzed for costs
|
|
307
|
+
cdk-cost-analyzer analyze template.yaml --format json | jq '.metadata.supportedResourceCount'
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 3. Cost Comparison of Template Variations
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# Analyze different configurations
|
|
314
|
+
cdk-cost-analyzer analyze template-small.json > cost-small.txt
|
|
315
|
+
cdk-cost-analyzer analyze template-large.json > cost-large.txt
|
|
316
|
+
diff cost-small.txt cost-large.txt
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 4. CI/CD Integration
|
|
320
|
+
|
|
321
|
+
```yaml
|
|
322
|
+
# .github/workflows/cost-check.yml
|
|
323
|
+
- name: Analyze Costs
|
|
324
|
+
run: |
|
|
325
|
+
cdk synth > template.json
|
|
326
|
+
cdk-cost-analyzer analyze template.json --format markdown >> $GITHUB_STEP_SUMMARY
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### 5. Documentation Generation
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
# Generate cost documentation for a template
|
|
333
|
+
cdk-cost-analyzer analyze infrastructure.json --format markdown > docs/cost-estimate.md
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Tips
|
|
337
|
+
|
|
338
|
+
1. **Use Debug Mode**: Add `--debug` flag to see detailed pricing API queries and responses
|
|
339
|
+
2. **Cache Pricing Data**: The tool automatically caches pricing data for 24 hours
|
|
340
|
+
3. **Regional Pricing**: Always specify the region where resources will be deployed
|
|
341
|
+
4. **Custom Assumptions**: Create a config file for your project's specific usage patterns
|
|
342
|
+
5. **JSON Output**: Use JSON format for integrating with other tools or dashboards
|
|
343
|
+
|
|
344
|
+
## Troubleshooting
|
|
345
|
+
|
|
346
|
+
### Zero costs for resources
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# Use debug mode to see pricing queries
|
|
350
|
+
cdk-cost-analyzer analyze template.json --debug 2>&1 | grep "Pricing API"
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Slow analysis
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
# Pricing data is cached, first run may be slow
|
|
357
|
+
# Subsequent runs use cache and are faster
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Inconsistent results
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
# Clear the cache if you get stale pricing data
|
|
364
|
+
rm -rf .cdk-cost-analyzer-cache
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Further Reading
|
|
368
|
+
|
|
369
|
+
- [Configuration Guide](../docs/CONFIGURATION.md)
|
|
370
|
+
- [Calculator Reference](../docs/CALCULATORS.md)
|
|
371
|
+
- [Troubleshooting Guide](../docs/TROUBLESHOOTING.md)
|
|
372
|
+
- [API Examples](../examples/api-usage.js)
|