@pagopa/dx-savemoney 0.1.1

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.
Files changed (83) hide show
  1. package/README.md +233 -0
  2. package/dist/azure/analyzer.d.ts +32 -0
  3. package/dist/azure/analyzer.d.ts.map +1 -0
  4. package/dist/azure/analyzer.js +128 -0
  5. package/dist/azure/analyzer.js.map +1 -0
  6. package/dist/azure/config.d.ts +19 -0
  7. package/dist/azure/config.d.ts.map +1 -0
  8. package/dist/azure/config.js +62 -0
  9. package/dist/azure/config.js.map +1 -0
  10. package/dist/azure/index.d.ts +9 -0
  11. package/dist/azure/index.d.ts.map +1 -0
  12. package/dist/azure/index.js +9 -0
  13. package/dist/azure/index.js.map +1 -0
  14. package/dist/azure/report.d.ts +12 -0
  15. package/dist/azure/report.d.ts.map +1 -0
  16. package/dist/azure/report.js +40 -0
  17. package/dist/azure/report.js.map +1 -0
  18. package/dist/azure/resources/app-service.d.ts +18 -0
  19. package/dist/azure/resources/app-service.d.ts.map +1 -0
  20. package/dist/azure/resources/app-service.js +65 -0
  21. package/dist/azure/resources/app-service.js.map +1 -0
  22. package/dist/azure/resources/disk.d.ts +16 -0
  23. package/dist/azure/resources/disk.d.ts.map +1 -0
  24. package/dist/azure/resources/disk.js +56 -0
  25. package/dist/azure/resources/disk.js.map +1 -0
  26. package/dist/azure/resources/index.d.ts +11 -0
  27. package/dist/azure/resources/index.d.ts.map +1 -0
  28. package/dist/azure/resources/index.js +11 -0
  29. package/dist/azure/resources/index.js.map +1 -0
  30. package/dist/azure/resources/nic.d.ts +15 -0
  31. package/dist/azure/resources/nic.d.ts.map +1 -0
  32. package/dist/azure/resources/nic.js +56 -0
  33. package/dist/azure/resources/nic.js.map +1 -0
  34. package/dist/azure/resources/private-endpoint.d.ts +15 -0
  35. package/dist/azure/resources/private-endpoint.d.ts.map +1 -0
  36. package/dist/azure/resources/private-endpoint.js +66 -0
  37. package/dist/azure/resources/private-endpoint.js.map +1 -0
  38. package/dist/azure/resources/public-ip.d.ts +18 -0
  39. package/dist/azure/resources/public-ip.d.ts.map +1 -0
  40. package/dist/azure/resources/public-ip.js +61 -0
  41. package/dist/azure/resources/public-ip.js.map +1 -0
  42. package/dist/azure/resources/storage.d.ts +16 -0
  43. package/dist/azure/resources/storage.d.ts.map +1 -0
  44. package/dist/azure/resources/storage.js +39 -0
  45. package/dist/azure/resources/storage.js.map +1 -0
  46. package/dist/azure/resources/vm.d.ts +19 -0
  47. package/dist/azure/resources/vm.d.ts.map +1 -0
  48. package/dist/azure/resources/vm.js +77 -0
  49. package/dist/azure/resources/vm.js.map +1 -0
  50. package/dist/azure/types.d.ts +34 -0
  51. package/dist/azure/types.d.ts.map +1 -0
  52. package/dist/azure/types.js +5 -0
  53. package/dist/azure/types.js.map +1 -0
  54. package/dist/azure/utils.d.ts +40 -0
  55. package/dist/azure/utils.d.ts.map +1 -0
  56. package/dist/azure/utils.js +104 -0
  57. package/dist/azure/utils.js.map +1 -0
  58. package/dist/index.d.ts +34 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +77 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/types.d.ts +21 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +14 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +60 -0
  67. package/src/azure/analyzer.ts +213 -0
  68. package/src/azure/config.ts +81 -0
  69. package/src/azure/index.ts +9 -0
  70. package/src/azure/report.ts +52 -0
  71. package/src/azure/resources/app-service.ts +118 -0
  72. package/src/azure/resources/disk.ts +88 -0
  73. package/src/azure/resources/index.ts +11 -0
  74. package/src/azure/resources/nic.ts +90 -0
  75. package/src/azure/resources/private-endpoint.ts +112 -0
  76. package/src/azure/resources/public-ip.ts +106 -0
  77. package/src/azure/resources/storage.ts +67 -0
  78. package/src/azure/resources/vm.ts +129 -0
  79. package/src/azure/types.ts +38 -0
  80. package/src/azure/utils.ts +141 -0
  81. package/src/index.test.ts +95 -0
  82. package/src/index.ts +99 -0
  83. package/src/types.ts +34 -0
package/README.md ADDED
@@ -0,0 +1,233 @@
1
+ # DX Save Money
2
+
3
+ A TypeScript library for analyzing CSP (Cloud Service Provider) resources to identify potential cost inefficiencies and underutilized resources. It operates in read-only mode and does not modify, tag, or delete any resources; instead, it generates detailed reports to support FinOps decisions.
4
+
5
+ ## Supported Cloud Providers
6
+
7
+ ### ✅ Azure
8
+
9
+ Full support for Azure resource analysis with intelligent detection and flexible reporting.
10
+
11
+ ### 🚧 AWS (Coming Soon)
12
+
13
+ AWS support is planned for future releases. The architecture is designed to support multiple CSPs with provider-specific analyzers.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @pagopa/dx-savemoney
19
+ # or
20
+ pnpm add @pagopa/dx-savemoney
21
+ # or
22
+ yarn add @pagopa/dx-savemoney
23
+ ```
24
+
25
+ ## Azure
26
+
27
+ ### Main Features
28
+
29
+ - **Multi-Subscription Analysis**: Scans multiple Azure subscriptions in a single command.
30
+ - **Intelligent Detection**: Uses Azure Monitor metrics (e.g. CPU, network traffic, transactions) to scientifically identify inactive resources.
31
+ - **Orphaned Resource Identification**: Detects commonly "forgotten" resources like unattached disks, unassociated public IPs, and unused network interfaces.
32
+ - **Flexible Reporting**: Offers multiple output formats:
33
+ - `table`: A human-readable summary for the terminal.
34
+ - `json`: Standard format for integration with other tools.
35
+ - `detailed-json`: A comprehensive output with all resource metadata, ideal for in-depth analysis via AI or custom scripts.
36
+ - **Simplified Configuration**: Supports configuration via files, command-line options, environment variables, or an interactive prompt.
37
+
38
+ ### Analyzed Resources
39
+
40
+ The tool analyzes the following Azure resource types with specific detection methods and risk levels:
41
+
42
+ | Resource Type | Detection Method | Cost Risk | What's Checked |
43
+ | :---------------------- | :---------------------- | :-------: | :-------------------------------------------------------------------------------------- |
44
+ | **Virtual Machines** | Instance View + Metrics | 🔴 High | Deallocated/stopped state, Low CPU usage (<1%), Low network traffic (<10MB) |
45
+ | **App Service Plans** | API Details + Metrics | 🔴 High | No apps deployed, Very low CPU (<5%), Very low memory (<10%), Oversized Premium tier |
46
+ | **Managed Disks** | API Details | 🟡 Medium | Unattached state, No `managedBy` property |
47
+ | **Public IP Addresses** | API Details + Metrics | 🟡 Medium | Not associated with any resource, Static IP not in use, Very low network traffic (<1MB) |
48
+ | **Network Interfaces** | API Details | 🟡 Medium | Not attached to VM or Private Endpoint, No public IP assigned |
49
+ | **Private Endpoints** | API Details | 🟡 Medium | No private link connections, Rejected/disconnected connections, No network interfaces |
50
+ | **Storage Accounts** | Metrics | 🟡 Medium | Very low transaction count (<100 in timespan) |
51
+
52
+ #### Generic Checks
53
+
54
+ All resources are also checked for:
55
+
56
+ - **Missing tags**: Resources without tags are flagged as potentially unmanaged
57
+ - **Location mismatch**: Resources not in the preferred location are reported
58
+
59
+ ### Prerequisites
60
+
61
+ 1. **Node.js**: Version 22 or higher.
62
+ 2. **Azure Credentials**: The library uses `DefaultAzureCredential` from `@azure/identity`, which supports various authentication methods:
63
+ - Azure CLI (`az login`)
64
+ - Managed Identity
65
+ - Environment variables
66
+ - Visual Studio Code
67
+ - And more...
68
+
69
+ ### Usage
70
+
71
+ #### Quick Start
72
+
73
+ ```typescript
74
+ import { azure, loadConfig } from "@pagopa/dx-savemoney";
75
+
76
+ // Load configuration (from file, env vars, or interactive prompt)
77
+ const config = await loadConfig("./config.json");
78
+
79
+ // Run analysis and generate report
80
+ await azure.analyzeAzureResources(config, "table");
81
+ ```
82
+
83
+ #### Configuration Inputs
84
+
85
+ The tool requires the following configuration:
86
+
87
+ | Input | Type | Required | Default | Description |
88
+ | :------------------ | :--------- | :------: | :----------- | :----------------------------------------------------------- |
89
+ | `tenantId` | `string` | ✅ | - | Azure Active Directory Tenant ID |
90
+ | `subscriptionIds` | `string[]` | ✅ | - | Array of Azure subscription IDs to analyze |
91
+ | `preferredLocation` | `string` | ❌ | `italynorth` | Preferred Azure region (resources elsewhere will be flagged) |
92
+ | `timespanDays` | `number` | ❌ | `30` | Number of days to look back for metrics analysis |
93
+ | `verbose` | `boolean` | ❌ | `false` | Enable detailed logging for each resource analyzed |
94
+
95
+ #### Output Formats
96
+
97
+ The tool supports multiple output formats for different use cases:
98
+
99
+ | Format | Description | Use Case |
100
+ | :-------------- | :---------------------------------------------- | :----------------------------- |
101
+ | `table` | Human-readable table in terminal | Quick visual inspection |
102
+ | `json` | Structured JSON with resource summaries | Integration with other tools |
103
+ | `detailed-json` | Complete JSON with full Azure resource metadata | AI analysis or deep inspection |
104
+
105
+ #### How to Load Configuration
106
+
107
+ The `loadConfig()` function loads configuration in the following priority order:
108
+
109
+ 1. **Configuration file** (pass file path as parameter)
110
+ 2. **Environment variables** (`ARM_TENANT_ID`, `ARM_SUBSCRIPTION_ID`)
111
+ 3. **Interactive prompt** (if no other configuration is found)
112
+
113
+ **Example:**
114
+
115
+ ```typescript
116
+ // From file
117
+ const config1 = await loadConfig("./config.json");
118
+
119
+ // From environment variables or prompt
120
+ const config2 = await loadConfig();
121
+ ```
122
+
123
+ #### Configuration File Example
124
+
125
+ ```json
126
+ {
127
+ "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
128
+ "subscriptionIds": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"],
129
+ "preferredLocation": "italynorth",
130
+ "timespanDays": 30,
131
+ "verbose": false
132
+ }
133
+ ```
134
+
135
+ #### Usage Examples
136
+
137
+ ##### Basic Usage
138
+
139
+ ```typescript
140
+ import { azure, loadConfig } from "@pagopa/dx-savemoney";
141
+
142
+ // Load from config file
143
+ const config = await loadConfig("./config.json");
144
+ await azure.analyzeAzureResources(config, "table");
145
+ ```
146
+
147
+ ##### Custom Configuration
148
+
149
+ ```typescript
150
+ import { azure } from "@pagopa/dx-savemoney";
151
+ import type { AzureConfig } from "@pagopa/dx-savemoney";
152
+
153
+ const config: AzureConfig = {
154
+ tenantId: "your-tenant-id",
155
+ subscriptionIds: ["sub-id-1", "sub-id-2"],
156
+ preferredLocation: "italynorth",
157
+ timespanDays: 30,
158
+ verbose: true,
159
+ };
160
+
161
+ await azure.analyzeAzureResources(config, "json");
162
+ ```
163
+
164
+ ##### Generate Detailed Report
165
+
166
+ ```typescript
167
+ import { azure, loadConfig } from "@pagopa/dx-savemoney";
168
+
169
+ const config = await loadConfig();
170
+ // Generate detailed JSON with full resource metadata
171
+ await azure.analyzeAzureResources(config, "detailed-json");
172
+ ```
173
+
174
+ ##### Using Environment Variables
175
+
176
+ ```typescript
177
+ import { loadConfig, azure } from "@pagopa/dx-savemoney";
178
+
179
+ // Set environment variables
180
+ // ARM_TENANT_ID=xxx
181
+ // ARM_SUBSCRIPTION_ID=sub1,sub2
182
+
183
+ const config = await loadConfig(); // Will read from env vars
184
+ await azure.analyzeAzureResources(config, "json");
185
+ ```
186
+
187
+ ## AWS (Coming Soon)
188
+
189
+ AWS support is planned for future releases with similar capabilities:
190
+
191
+ - Multi-account analysis
192
+ - Resource-specific detection algorithms
193
+ - Flexible reporting formats
194
+ - AWS-specific configuration options
195
+
196
+ The API will follow a similar pattern:
197
+
198
+ ```typescript
199
+ import { aws, loadAwsConfig } from "@pagopa/dx-savemoney";
200
+
201
+ const config = await loadAwsConfig("./aws-config.json");
202
+ await aws.analyzeAwsResources(config, "table");
203
+ ```
204
+
205
+ ## Development
206
+
207
+ ### Type Checking
208
+
209
+ ```bash
210
+ pnpm typecheck
211
+ ```
212
+
213
+ ### Linting
214
+
215
+ ```bash
216
+ pnpm lint # Auto-fix issues
217
+ pnpm lint:check # Check without fixing
218
+ ```
219
+
220
+ ### Testing
221
+
222
+ ```bash
223
+ pnpm test # Run tests
224
+ pnpm test:watch # Watch mode
225
+ pnpm test:coverage # With coverage report
226
+ ```
227
+
228
+ ### Formatting
229
+
230
+ ```bash
231
+ pnpm format # Format code
232
+ pnpm format:check # Check formatting
233
+ ```
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Azure resource analyzer - Main orchestration logic
3
+ */
4
+ import { WebSiteManagementClient } from "@azure/arm-appservice";
5
+ import { ComputeManagementClient } from "@azure/arm-compute";
6
+ import { MonitorClient } from "@azure/arm-monitor";
7
+ import { NetworkManagementClient } from "@azure/arm-network";
8
+ import * as armResources from "@azure/arm-resources";
9
+ import type { AzureConfig } from "./types.js";
10
+ import { type AnalysisResult } from "../types.js";
11
+ /**
12
+ * Analyzes resources in multiple Azure subscriptions and generates a report.
13
+ *
14
+ * @param config - Azure configuration with subscription IDs and settings
15
+ * @param format - Output format (table, json, or detailed-json)
16
+ */
17
+ export declare function analyzeAzureResources(config: AzureConfig, format: "detailed-json" | "json" | "table"): Promise<void>;
18
+ /**
19
+ * Analyzes a single Azure resource based on its type.
20
+ *
21
+ * @param resource - The Azure resource to analyze
22
+ * @param monitorClient - Azure Monitor client for metrics
23
+ * @param computeClient - Azure Compute client
24
+ * @param networkClient - Azure Network client
25
+ * @param webSiteClient - Azure Web Site client
26
+ * @param preferredLocation - Preferred Azure location
27
+ * @param timespanDays - Number of days to analyze metrics
28
+ * @param verbose - Whether verbose logging is enabled
29
+ * @returns Analysis result with cost risk and reason
30
+ */
31
+ export declare function analyzeResource(resource: armResources.GenericResource, monitorClient: MonitorClient, computeClient: ComputeManagementClient, networkClient: NetworkManagementClient, webSiteClient: WebSiteManagementClient, preferredLocation: string, timespanDays: number, verbose?: boolean): Promise<AnalysisResult>;
32
+ //# sourceMappingURL=analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/azure/analyzer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAIrD,OAAO,KAAK,EAAE,WAAW,EAA+B,MAAM,YAAY,CAAC;AAE3E,OAAO,EAAE,KAAK,cAAc,EAAgB,MAAM,aAAa,CAAC;AAYhE;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,OAAO,iBA8D3C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,aAAa,EAC5B,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,uBAAuB,EACtC,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,MAAM,EACpB,OAAO,UAAQ,GACd,OAAO,CAAC,cAAc,CAAC,CA4FzB"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Azure resource analyzer - Main orchestration logic
3
+ */
4
+ import { WebSiteManagementClient } from "@azure/arm-appservice";
5
+ import { ComputeManagementClient } from "@azure/arm-compute";
6
+ import { MonitorClient } from "@azure/arm-monitor";
7
+ import { NetworkManagementClient } from "@azure/arm-network";
8
+ import * as armResources from "@azure/arm-resources";
9
+ import { DefaultAzureCredential } from "@azure/identity";
10
+ import { getLogger } from "@logtape/logtape";
11
+ import { mergeResults } from "../types.js";
12
+ import { generateReport } from "./report.js";
13
+ import { analyzeAppServicePlan, analyzeDisk, analyzeNic, analyzePrivateEndpoint, analyzePublicIp, analyzeStorageAccount, analyzeVM, } from "./resources/index.js";
14
+ /**
15
+ * Analyzes resources in multiple Azure subscriptions and generates a report.
16
+ *
17
+ * @param config - Azure configuration with subscription IDs and settings
18
+ * @param format - Output format (table, json, or detailed-json)
19
+ */
20
+ export async function analyzeAzureResources(config, format) {
21
+ const logger = getLogger(["savemoney", "azure"]);
22
+ const credential = new DefaultAzureCredential();
23
+ const allReports = [];
24
+ for (const subscriptionId of config.subscriptionIds) {
25
+ logger.info(`Analyzing subscription: ${subscriptionId}`);
26
+ const resourceClient = new armResources.ResourceManagementClient(credential, subscriptionId.trim());
27
+ const monitorClient = new MonitorClient(credential, subscriptionId.trim());
28
+ const computeClient = new ComputeManagementClient(credential, subscriptionId.trim());
29
+ const networkClient = new NetworkManagementClient(credential, subscriptionId.trim());
30
+ const webSiteClient = new WebSiteManagementClient(credential, subscriptionId.trim());
31
+ // Use the async iterator to avoid memory explosion for large environments
32
+ for await (const resource of resourceClient.resources.list()) {
33
+ const { costRisk, reason, suspectedUnused } = await analyzeResource(resource, monitorClient, computeClient, networkClient, webSiteClient, config.preferredLocation, config.timespanDays, config.verbose || false);
34
+ if (suspectedUnused) {
35
+ allReports.push({
36
+ analysis: {
37
+ costRisk,
38
+ reason: reason || "No specific findings.",
39
+ suspectedUnused,
40
+ },
41
+ resource: resource,
42
+ });
43
+ }
44
+ }
45
+ }
46
+ // Sort to make the output more readable
47
+ allReports.sort((a, b) => {
48
+ if (a.analysis.costRisk === b.analysis.costRisk)
49
+ return (a.resource.name ?? "").localeCompare(b.resource.name ?? "");
50
+ const order = { high: 0, low: 2, medium: 1 };
51
+ return order[a.analysis.costRisk] - order[b.analysis.costRisk];
52
+ });
53
+ await generateReport(allReports, format);
54
+ }
55
+ /**
56
+ * Analyzes a single Azure resource based on its type.
57
+ *
58
+ * @param resource - The Azure resource to analyze
59
+ * @param monitorClient - Azure Monitor client for metrics
60
+ * @param computeClient - Azure Compute client
61
+ * @param networkClient - Azure Network client
62
+ * @param webSiteClient - Azure Web Site client
63
+ * @param preferredLocation - Preferred Azure location
64
+ * @param timespanDays - Number of days to analyze metrics
65
+ * @param verbose - Whether verbose logging is enabled
66
+ * @returns Analysis result with cost risk and reason
67
+ */
68
+ export async function analyzeResource(resource, monitorClient, computeClient, networkClient, webSiteClient, preferredLocation, timespanDays, verbose = false) {
69
+ const type = resource.type?.toLowerCase() || "";
70
+ let result = {
71
+ costRisk: "low",
72
+ reason: "",
73
+ suspectedUnused: false,
74
+ };
75
+ // Generic check: lack of tags is a common sign of unmanaged resources.
76
+ if (!resource.tags || Object.keys(resource.tags).length === 0) {
77
+ result.suspectedUnused = true;
78
+ result.reason += "No tags found. ";
79
+ }
80
+ // Route to type-specific analysis hooks
81
+ switch (type) {
82
+ case "microsoft.compute/disks": {
83
+ const diskResult = await analyzeDisk(resource, computeClient, verbose);
84
+ result = mergeResults(result, diskResult);
85
+ break;
86
+ }
87
+ case "microsoft.compute/virtualmachines": {
88
+ const vmResult = await analyzeVM(resource, monitorClient, computeClient, timespanDays, verbose);
89
+ result = mergeResults(result, vmResult);
90
+ break;
91
+ }
92
+ case "microsoft.network/networkinterfaces": {
93
+ const nicResult = await analyzeNic(resource, networkClient, verbose);
94
+ result = mergeResults(result, nicResult);
95
+ break;
96
+ }
97
+ case "microsoft.network/privateendpoints": {
98
+ const peResult = await analyzePrivateEndpoint(resource, networkClient, verbose);
99
+ result = mergeResults(result, peResult);
100
+ break;
101
+ }
102
+ case "microsoft.network/publicipaddresses": {
103
+ const pipResult = await analyzePublicIp(resource, networkClient, monitorClient, timespanDays, verbose);
104
+ result = mergeResults(result, pipResult);
105
+ break;
106
+ }
107
+ case "microsoft.storage/storageaccounts": {
108
+ const storageResult = await analyzeStorageAccount(resource, monitorClient, timespanDays, verbose);
109
+ result = mergeResults(result, storageResult);
110
+ break;
111
+ }
112
+ case "microsoft.web/serverfarms": {
113
+ const aspResult = await analyzeAppServicePlan(resource, webSiteClient, monitorClient, timespanDays, verbose);
114
+ result = mergeResults(result, aspResult);
115
+ break;
116
+ }
117
+ default:
118
+ result.reason += "No specific analysis for this resource type. ";
119
+ break;
120
+ }
121
+ // Generic check for location
122
+ if (resource.location &&
123
+ !resource.location.toLowerCase().includes(preferredLocation.toLowerCase())) {
124
+ result.reason += `Resource not in preferred location (${preferredLocation}). `;
125
+ }
126
+ return { ...result, reason: result.reason.trim() };
127
+ }
128
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../src/azure/analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Azure configuration loading utilities
3
+ */
4
+ import type { AzureConfig } from "./types.js";
5
+ /**
6
+ * Loads Azure configuration from file, environment variables, or interactive prompts.
7
+ *
8
+ * @param configPath - Optional path to JSON configuration file
9
+ * @returns Azure configuration object with subscription IDs and settings
10
+ */
11
+ export declare function loadAzureConfig(configPath?: string): Promise<AzureConfig>;
12
+ /**
13
+ * Prompts user for input via stdin.
14
+ *
15
+ * @param question - The question to display to the user
16
+ * @returns User's input as a string
17
+ */
18
+ export declare function prompt(question: string): Promise<string>;
19
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CA2CtB;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW9D"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Azure configuration loading utilities
3
+ */
4
+ import { getLogger } from "@logtape/logtape";
5
+ import * as fs from "fs";
6
+ import * as readline from "readline";
7
+ /**
8
+ * Loads Azure configuration from file, environment variables, or interactive prompts.
9
+ *
10
+ * @param configPath - Optional path to JSON configuration file
11
+ * @returns Azure configuration object with subscription IDs and settings
12
+ */
13
+ export async function loadAzureConfig(configPath) {
14
+ if (configPath && fs.existsSync(configPath)) {
15
+ try {
16
+ const configContent = fs.readFileSync(configPath, "utf-8");
17
+ const config = JSON.parse(configContent);
18
+ if (!config.tenantId || !config.subscriptionIds) {
19
+ throw new Error("Config file must contain 'tenantId' and 'subscriptionIds'");
20
+ }
21
+ return {
22
+ ...config,
23
+ preferredLocation: config.preferredLocation || "italynorth",
24
+ subscriptionIds: config.subscriptionIds,
25
+ tenantId: config.tenantId,
26
+ timespanDays: config.timespanDays || 30,
27
+ };
28
+ }
29
+ catch (error) {
30
+ throw new Error(`Failed to load config file: ${error instanceof Error ? error.message : error}`);
31
+ }
32
+ }
33
+ const logger = getLogger(["savemoney", "azure", "config"]);
34
+ logger.info("Configuration file not found. Checking environment variables...");
35
+ const tenantId = process.env.ARM_TENANT_ID || (await prompt("Enter Tenant ID: "));
36
+ const subscriptionIds = process.env.ARM_SUBSCRIPTION_ID
37
+ ? process.env.ARM_SUBSCRIPTION_ID.split(",")
38
+ : (await prompt("Enter Subscription IDs (comma-separated): ")).split(",");
39
+ return {
40
+ preferredLocation: "italynorth",
41
+ subscriptionIds,
42
+ tenantId,
43
+ timespanDays: 30,
44
+ };
45
+ }
46
+ /**
47
+ * Prompts user for input via stdin.
48
+ *
49
+ * @param question - The question to display to the user
50
+ * @returns User's input as a string
51
+ */
52
+ export async function prompt(question) {
53
+ const rl = readline.createInterface({
54
+ input: process.stdin,
55
+ output: process.stdout,
56
+ });
57
+ return new Promise((resolve) => rl.question(question, (answer) => {
58
+ rl.close();
59
+ resolve(answer);
60
+ }));
61
+ }
62
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/azure/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAIrC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAmB;IAEnB,IAAI,UAAU,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAyB,CAAC;YAEjE,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;gBAChD,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,GAAG,MAAM;gBACT,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,YAAY;gBAC3D,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE;aACxC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAChF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC3D,MAAM,CAAC,IAAI,CACT,iEAAiE,CAClE,CAAC;IAEF,MAAM,QAAQ,GACZ,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACnE,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB;QACrD,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC;QAC5C,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,4CAA4C,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE5E,OAAO;QACL,iBAAiB,EAAE,YAAY;QAC/B,eAAe;QACf,QAAQ;QACR,YAAY,EAAE,EAAE;KACjB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,QAAgB;IAC3C,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;QAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Azure module - Entry point
3
+ *
4
+ * This module contains all Azure-specific logic for analyzing resources.
5
+ */
6
+ export * from "./analyzer.js";
7
+ export * from "./config.js";
8
+ export * from "./types.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/azure/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Azure module - Entry point
3
+ *
4
+ * This module contains all Azure-specific logic for analyzing resources.
5
+ */
6
+ export * from "./analyzer.js";
7
+ export * from "./config.js";
8
+ export * from "./types.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/azure/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Azure report generation
3
+ */
4
+ import type { AzureDetailedResourceReport } from "./types.js";
5
+ /**
6
+ * Generates a report from Azure resource analysis in the specified format.
7
+ *
8
+ * @param report - Array of detailed resource reports
9
+ * @param format - Output format (table, json, or detailed-json)
10
+ */
11
+ export declare function generateReport(report: AzureDetailedResourceReport[], format: "detailed-json" | "json" | "table"): Promise<void>;
12
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/azure/report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,2BAA2B,EAE5B,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,2BAA2B,EAAE,EACrC,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,OAAO,iBAkC3C"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Azure report generation
3
+ */
4
+ /**
5
+ * Generates a report from Azure resource analysis in the specified format.
6
+ *
7
+ * @param report - Array of detailed resource reports
8
+ * @param format - Output format (table, json, or detailed-json)
9
+ */
10
+ export async function generateReport(report, format) {
11
+ if (format === "detailed-json") {
12
+ console.log(JSON.stringify(report, null, 2));
13
+ return;
14
+ }
15
+ // For other formats, we extract the summary data.
16
+ const summaryReport = report.map((r) => ({
17
+ costRisk: r.analysis.costRisk,
18
+ location: r.resource.location ?? "",
19
+ name: r.resource.name ?? "unknown",
20
+ reason: r.analysis.reason,
21
+ resourceGroup: r.resource.id?.split("/")[4],
22
+ subscriptionId: r.resource.id?.split("/")[2] ?? "unknown",
23
+ suspectedUnused: r.analysis.suspectedUnused,
24
+ type: r.resource.type ?? "unknown",
25
+ }));
26
+ if (format === "json") {
27
+ console.log(JSON.stringify(summaryReport, null, 2));
28
+ }
29
+ else {
30
+ console.table(summaryReport.map((r) => ({
31
+ Name: r.name,
32
+ Reason: r.reason,
33
+ "Resource Group": r.resourceGroup || "N/A",
34
+ Risk: r.costRisk,
35
+ Type: r.type,
36
+ Unused: r.suspectedUnused ? "Yes" : "No",
37
+ })), ["Name", "Type", "Resource Group", "Risk", "Unused", "Reason"]);
38
+ }
39
+ }
40
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/azure/report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAqC,EACrC,MAA0C;IAE1C,IAAI,MAAM,KAAK,eAAe,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,kDAAkD;IAClD,MAAM,aAAa,GAA0B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9D,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ;QAC7B,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE;QACnC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,SAAS;QAClC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM;QACzB,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3C,cAAc,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS;QACzD,eAAe,EAAE,CAAC,CAAC,QAAQ,CAAC,eAAe;QAC3C,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,SAAS;KACnC,CAAC,CAAC,CAAC;IAEJ,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CACX,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,gBAAgB,EAAE,CAAC,CAAC,aAAa,IAAI,KAAK;YAC1C,IAAI,EAAE,CAAC,CAAC,QAAQ;YAChB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;SACzC,CAAC,CAAC,EACH,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAC/D,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Azure App Service Plan analysis
3
+ */
4
+ import type { WebSiteManagementClient } from "@azure/arm-appservice";
5
+ import type { MonitorClient } from "@azure/arm-monitor";
6
+ import * as armResources from "@azure/arm-resources";
7
+ import type { AnalysisResult } from "../../types.js";
8
+ /**
9
+ * Analyzes an Azure App Service Plan for potential cost optimization.
10
+ *
11
+ * @param resource - The Azure resource object
12
+ * @param webSiteClient - Azure Web Site client for App Service Plan details
13
+ * @param monitorClient - Azure Monitor client for metrics
14
+ * @param timespanDays - Number of days to analyze metrics
15
+ * @returns Analysis result with cost risk and reason
16
+ */
17
+ export declare function analyzeAppServicePlan(resource: armResources.GenericResource, webSiteClient: WebSiteManagementClient, monitorClient: MonitorClient, timespanDays: number, verbose?: boolean): Promise<AnalysisResult>;
18
+ //# sourceMappingURL=app-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-service.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/app-service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AASrD;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,MAAM,EACpB,OAAO,UAAQ,GACd,OAAO,CAAC,cAAc,CAAC,CAmFzB"}