@pagopa/dx-savemoney 0.3.3 → 0.5.0

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 (174) hide show
  1. package/README.md +12 -0
  2. package/dist/__tests__/finding.test.js +22 -0
  3. package/dist/__tests__/finding.test.js.map +1 -1
  4. package/dist/azure/__tests__/analyzer-tags.test.js +28 -1
  5. package/dist/azure/__tests__/analyzer-tags.test.js.map +1 -1
  6. package/dist/azure/__tests__/config.test.js +31 -1
  7. package/dist/azure/__tests__/config.test.js.map +1 -1
  8. package/dist/azure/__tests__/report.test.js +71 -0
  9. package/dist/azure/__tests__/report.test.js.map +1 -1
  10. package/dist/azure/analyzer.d.ts +8 -1
  11. package/dist/azure/analyzer.d.ts.map +1 -1
  12. package/dist/azure/analyzer.js +57 -41
  13. package/dist/azure/analyzer.js.map +1 -1
  14. package/dist/azure/analyzers/registry.d.ts.map +1 -1
  15. package/dist/azure/analyzers/registry.js +4 -4
  16. package/dist/azure/analyzers/registry.js.map +1 -1
  17. package/dist/azure/analyzers/types.d.ts +6 -0
  18. package/dist/azure/analyzers/types.d.ts.map +1 -1
  19. package/dist/azure/config.d.ts.map +1 -1
  20. package/dist/azure/config.js +1 -5
  21. package/dist/azure/config.js.map +1 -1
  22. package/dist/azure/pricing/__tests__/cache.test.d.ts +8 -0
  23. package/dist/azure/pricing/__tests__/cache.test.d.ts.map +1 -0
  24. package/dist/azure/pricing/__tests__/cache.test.js +56 -0
  25. package/dist/azure/pricing/__tests__/cache.test.js.map +1 -0
  26. package/dist/azure/pricing/__tests__/client.test.d.ts +9 -0
  27. package/dist/azure/pricing/__tests__/client.test.d.ts.map +1 -0
  28. package/dist/azure/pricing/__tests__/client.test.js +181 -0
  29. package/dist/azure/pricing/__tests__/client.test.js.map +1 -0
  30. package/dist/azure/pricing/__tests__/pricing-service.test.d.ts +6 -0
  31. package/dist/azure/pricing/__tests__/pricing-service.test.d.ts.map +1 -0
  32. package/dist/azure/pricing/__tests__/pricing-service.test.js +283 -0
  33. package/dist/azure/pricing/__tests__/pricing-service.test.js.map +1 -0
  34. package/dist/azure/pricing/cache.d.ts +45 -0
  35. package/dist/azure/pricing/cache.d.ts.map +1 -0
  36. package/dist/azure/pricing/cache.js +79 -0
  37. package/dist/azure/pricing/cache.js.map +1 -0
  38. package/dist/azure/pricing/client.d.ts +67 -0
  39. package/dist/azure/pricing/client.d.ts.map +1 -0
  40. package/dist/azure/pricing/client.js +96 -0
  41. package/dist/azure/pricing/client.js.map +1 -0
  42. package/dist/azure/pricing/index.d.ts +6 -0
  43. package/dist/azure/pricing/index.d.ts.map +1 -0
  44. package/dist/azure/pricing/index.js +6 -0
  45. package/dist/azure/pricing/index.js.map +1 -0
  46. package/dist/azure/pricing/pricing-service.d.ts +46 -0
  47. package/dist/azure/pricing/pricing-service.d.ts.map +1 -0
  48. package/dist/azure/pricing/pricing-service.js +84 -0
  49. package/dist/azure/pricing/pricing-service.js.map +1 -0
  50. package/dist/azure/pricing/resolvers/__tests__/app-service.test.d.ts +5 -0
  51. package/dist/azure/pricing/resolvers/__tests__/app-service.test.d.ts.map +1 -0
  52. package/dist/azure/pricing/resolvers/__tests__/app-service.test.js +137 -0
  53. package/dist/azure/pricing/resolvers/__tests__/app-service.test.js.map +1 -0
  54. package/dist/azure/pricing/resolvers/__tests__/disk.test.d.ts +5 -0
  55. package/dist/azure/pricing/resolvers/__tests__/disk.test.d.ts.map +1 -0
  56. package/dist/azure/pricing/resolvers/__tests__/disk.test.js +160 -0
  57. package/dist/azure/pricing/resolvers/__tests__/disk.test.js.map +1 -0
  58. package/dist/azure/pricing/resolvers/__tests__/public-ip.test.d.ts +5 -0
  59. package/dist/azure/pricing/resolvers/__tests__/public-ip.test.d.ts.map +1 -0
  60. package/dist/azure/pricing/resolvers/__tests__/public-ip.test.js +113 -0
  61. package/dist/azure/pricing/resolvers/__tests__/public-ip.test.js.map +1 -0
  62. package/dist/azure/pricing/resolvers/__tests__/vm.test.d.ts +9 -0
  63. package/dist/azure/pricing/resolvers/__tests__/vm.test.d.ts.map +1 -0
  64. package/dist/azure/pricing/resolvers/__tests__/vm.test.js +204 -0
  65. package/dist/azure/pricing/resolvers/__tests__/vm.test.js.map +1 -0
  66. package/dist/azure/pricing/resolvers/app-service.d.ts +27 -0
  67. package/dist/azure/pricing/resolvers/app-service.d.ts.map +1 -0
  68. package/dist/azure/pricing/resolvers/app-service.js +67 -0
  69. package/dist/azure/pricing/resolvers/app-service.js.map +1 -0
  70. package/dist/azure/pricing/resolvers/disk.d.ts +59 -0
  71. package/dist/azure/pricing/resolvers/disk.d.ts.map +1 -0
  72. package/dist/azure/pricing/resolvers/disk.js +140 -0
  73. package/dist/azure/pricing/resolvers/disk.js.map +1 -0
  74. package/dist/azure/pricing/resolvers/index.d.ts +12 -0
  75. package/dist/azure/pricing/resolvers/index.d.ts.map +1 -0
  76. package/dist/azure/pricing/resolvers/index.js +8 -0
  77. package/dist/azure/pricing/resolvers/index.js.map +1 -0
  78. package/dist/azure/pricing/resolvers/public-ip.d.ts +32 -0
  79. package/dist/azure/pricing/resolvers/public-ip.d.ts.map +1 -0
  80. package/dist/azure/pricing/resolvers/public-ip.js +56 -0
  81. package/dist/azure/pricing/resolvers/public-ip.js.map +1 -0
  82. package/dist/azure/pricing/resolvers/vm.d.ts +51 -0
  83. package/dist/azure/pricing/resolvers/vm.d.ts.map +1 -0
  84. package/dist/azure/pricing/resolvers/vm.js +89 -0
  85. package/dist/azure/pricing/resolvers/vm.js.map +1 -0
  86. package/dist/azure/pricing/schema.d.ts +88 -0
  87. package/dist/azure/pricing/schema.d.ts.map +1 -0
  88. package/dist/azure/pricing/schema.js +64 -0
  89. package/dist/azure/pricing/schema.js.map +1 -0
  90. package/dist/azure/pricing/test-cache-dir.d.ts +3 -0
  91. package/dist/azure/pricing/test-cache-dir.d.ts.map +1 -0
  92. package/dist/azure/pricing/test-cache-dir.js +17 -0
  93. package/dist/azure/pricing/test-cache-dir.js.map +1 -0
  94. package/dist/azure/report.d.ts.map +1 -1
  95. package/dist/azure/report.js +57 -18
  96. package/dist/azure/report.js.map +1 -1
  97. package/dist/azure/resources/__tests__/app-service.test.d.ts +5 -0
  98. package/dist/azure/resources/__tests__/app-service.test.d.ts.map +1 -0
  99. package/dist/azure/resources/__tests__/app-service.test.js +105 -0
  100. package/dist/azure/resources/__tests__/app-service.test.js.map +1 -0
  101. package/dist/azure/resources/app-service.d.ts +8 -3
  102. package/dist/azure/resources/app-service.d.ts.map +1 -1
  103. package/dist/azure/resources/app-service.js +35 -2
  104. package/dist/azure/resources/app-service.js.map +1 -1
  105. package/dist/azure/resources/disk.d.ts +3 -1
  106. package/dist/azure/resources/disk.d.ts.map +1 -1
  107. package/dist/azure/resources/disk.js +35 -3
  108. package/dist/azure/resources/disk.js.map +1 -1
  109. package/dist/azure/resources/public-ip.d.ts +3 -1
  110. package/dist/azure/resources/public-ip.d.ts.map +1 -1
  111. package/dist/azure/resources/public-ip.js +41 -3
  112. package/dist/azure/resources/public-ip.js.map +1 -1
  113. package/dist/azure/resources/vm.d.ts +4 -1
  114. package/dist/azure/resources/vm.d.ts.map +1 -1
  115. package/dist/azure/resources/vm.js +49 -6
  116. package/dist/azure/resources/vm.js.map +1 -1
  117. package/dist/azure/types.d.ts +18 -3
  118. package/dist/azure/types.d.ts.map +1 -1
  119. package/dist/finding.d.ts +3 -3
  120. package/dist/finding.d.ts.map +1 -1
  121. package/dist/finding.js +7 -0
  122. package/dist/finding.js.map +1 -1
  123. package/dist/index.d.ts +3 -2
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +1 -0
  126. package/dist/index.js.map +1 -1
  127. package/dist/schema.d.ts +5 -1
  128. package/dist/schema.d.ts.map +1 -1
  129. package/dist/schema.js +5 -3
  130. package/dist/schema.js.map +1 -1
  131. package/dist/types.d.ts +8 -0
  132. package/dist/types.d.ts.map +1 -1
  133. package/dist/types.js +2 -0
  134. package/dist/types.js.map +1 -1
  135. package/package.json +1 -1
  136. package/src/__tests__/finding.test.ts +24 -0
  137. package/src/azure/__tests__/analyzer-tags.test.ts +36 -1
  138. package/src/azure/__tests__/config.test.ts +43 -1
  139. package/src/azure/__tests__/fixtures/full-override.yaml +2 -0
  140. package/src/azure/__tests__/report.test.ts +78 -0
  141. package/src/azure/analyzer.ts +70 -52
  142. package/src/azure/analyzers/registry.ts +8 -2
  143. package/src/azure/analyzers/types.ts +6 -0
  144. package/src/azure/config.ts +1 -5
  145. package/src/azure/pricing/__tests__/cache.test.ts +67 -0
  146. package/src/azure/pricing/__tests__/client.test.ts +212 -0
  147. package/src/azure/pricing/__tests__/fixtures/vm-b2s-westeurope.json +51 -0
  148. package/src/azure/pricing/__tests__/pricing-service.test.ts +315 -0
  149. package/src/azure/pricing/cache.ts +92 -0
  150. package/src/azure/pricing/client.ts +137 -0
  151. package/src/azure/pricing/index.ts +6 -0
  152. package/src/azure/pricing/pricing-service.ts +125 -0
  153. package/src/azure/pricing/resolvers/__tests__/app-service.test.ts +154 -0
  154. package/src/azure/pricing/resolvers/__tests__/disk.test.ts +189 -0
  155. package/src/azure/pricing/resolvers/__tests__/public-ip.test.ts +130 -0
  156. package/src/azure/pricing/resolvers/__tests__/vm.test.ts +232 -0
  157. package/src/azure/pricing/resolvers/app-service.ts +102 -0
  158. package/src/azure/pricing/resolvers/disk.ts +170 -0
  159. package/src/azure/pricing/resolvers/index.ts +31 -0
  160. package/src/azure/pricing/resolvers/public-ip.ts +87 -0
  161. package/src/azure/pricing/resolvers/vm.ts +125 -0
  162. package/src/azure/pricing/schema.ts +69 -0
  163. package/src/azure/pricing/test-cache-dir.ts +26 -0
  164. package/src/azure/report.ts +86 -21
  165. package/src/azure/resources/__tests__/app-service.test.ts +165 -0
  166. package/src/azure/resources/app-service.ts +65 -4
  167. package/src/azure/resources/disk.ts +46 -4
  168. package/src/azure/resources/public-ip.ts +61 -2
  169. package/src/azure/resources/vm.ts +89 -11
  170. package/src/azure/types.ts +19 -3
  171. package/src/finding.ts +10 -3
  172. package/src/index.ts +4 -3
  173. package/src/schema.ts +7 -3
  174. package/src/types.ts +12 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Tests for analyzeAppServicePlan() pricing enrichment.
3
+ */
4
+ import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { DEFAULT_THRESHOLDS } from "../../../types.js";
6
+ vi.mock("../../utils.js", () => ({
7
+ getMetric: vi.fn(),
8
+ verboseLog: vi.fn(),
9
+ verboseLogAnalysisResult: vi.fn(),
10
+ verboseLogResourceStart: vi.fn(),
11
+ }));
12
+ import { getMetric } from "../../utils.js";
13
+ import { analyzeAppServicePlan } from "../app-service.js";
14
+ const RESOURCE = {
15
+ id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Web/serverfarms/plan1",
16
+ location: "westeurope",
17
+ name: "plan1",
18
+ type: "Microsoft.Web/serverfarms",
19
+ };
20
+ const mockGetMetric = vi.mocked(getMetric);
21
+ function makeMonitorClient() {
22
+ return {
23
+ metrics: { list: vi.fn() },
24
+ };
25
+ }
26
+ function makePricingService() {
27
+ return {
28
+ resolveAppServicePlan: vi.fn().mockResolvedValue({
29
+ amount: 121.72,
30
+ currency: "EUR",
31
+ }),
32
+ };
33
+ }
34
+ function makeWebSiteClient(planDetails) {
35
+ const client = {
36
+ appServicePlans: {
37
+ get: vi.fn().mockResolvedValue({
38
+ location: "westeurope",
39
+ ...planDetails,
40
+ }),
41
+ },
42
+ };
43
+ return client;
44
+ }
45
+ describe("analyzeAppServicePlan", () => {
46
+ beforeEach(() => {
47
+ mockGetMetric.mockReset();
48
+ mockGetMetric.mockResolvedValue(50);
49
+ });
50
+ it("enriches suspected unused plans with App Service Plan pricing", async () => {
51
+ const pricing = makePricingService();
52
+ const result = await analyzeAppServicePlan(RESOURCE, makeWebSiteClient({
53
+ numberOfSites: 0,
54
+ reserved: true,
55
+ sku: {
56
+ capacity: 2,
57
+ name: "S1",
58
+ tier: "Standard",
59
+ },
60
+ }), makeMonitorClient(), 30, DEFAULT_THRESHOLDS, false, undefined, pricing);
61
+ expect(result.estimatedMonthlySavings).toEqual({
62
+ amount: 121.72,
63
+ currency: "EUR",
64
+ });
65
+ expect(pricing.resolveAppServicePlan).toHaveBeenCalledWith({
66
+ armRegionName: "westeurope",
67
+ os: "linux",
68
+ skuName: "S1",
69
+ workerCount: 2,
70
+ });
71
+ });
72
+ it("does not price Premium underutilization as full removable cost", async () => {
73
+ mockGetMetric.mockResolvedValue(1);
74
+ const pricing = makePricingService();
75
+ const result = await analyzeAppServicePlan(RESOURCE, makeWebSiteClient({
76
+ numberOfSites: 2,
77
+ reserved: false,
78
+ sku: {
79
+ capacity: 3,
80
+ name: "P1v3",
81
+ tier: "PremiumV3",
82
+ },
83
+ }), makeMonitorClient(), 30, DEFAULT_THRESHOLDS, false, undefined, pricing);
84
+ expect(result.suspectedUnused).toBe(true);
85
+ expect(result.reason).toContain("Premium tier with low resource utilization");
86
+ expect(result.estimatedMonthlySavings).toBeUndefined();
87
+ expect(pricing.resolveAppServicePlan).not.toHaveBeenCalled();
88
+ });
89
+ it("does not treat missing site count as an empty plan", async () => {
90
+ mockGetMetric.mockResolvedValue(50);
91
+ const pricing = makePricingService();
92
+ const result = await analyzeAppServicePlan(RESOURCE, makeWebSiteClient({
93
+ reserved: true,
94
+ sku: {
95
+ capacity: 2,
96
+ name: "S1",
97
+ tier: "Standard",
98
+ },
99
+ }), makeMonitorClient(), 30, DEFAULT_THRESHOLDS, false, undefined, pricing);
100
+ expect(result.suspectedUnused).toBe(false);
101
+ expect(result.estimatedMonthlySavings).toBeUndefined();
102
+ expect(pricing.resolveAppServicePlan).not.toHaveBeenCalled();
103
+ });
104
+ });
105
+ //# sourceMappingURL=app-service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-service.test.js","sourceRoot":"","sources":["../../../../src/azure/resources/__tests__/app-service.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAK9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/B,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE;IAClB,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;IACnB,wBAAwB,EAAE,EAAE,CAAC,EAAE,EAAE;IACjC,uBAAuB,EAAE,EAAE,CAAC,EAAE,EAAE;CACjC,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,QAAQ,GAAoB;IAChC,EAAE,EAAE,kFAAkF;IACtF,QAAQ,EAAE,YAAY;IACtB,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,2BAA2B;CAClC,CAAC;AAEF,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAE3C,SAAS,iBAAiB;IACxB,OAAO;QACL,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;KACC,CAAC;AAChC,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO;QACL,qBAAqB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC;YAC/C,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,KAAK;SAChB,CAAC;KACH,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,WAQ1B;IACC,MAAM,MAAM,GAAG;QACb,eAAe,EAAE;YACf,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC;gBAC7B,QAAQ,EAAE,YAAY;gBACtB,GAAG,WAAW;aACf,CAAC;SACH;KACiC,CAAC;IACrC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,UAAU,CAAC,GAAG,EAAE;QACd,aAAa,CAAC,SAAS,EAAE,CAAC;QAC1B,aAAa,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;QAErC,MAAM,MAAM,GAAG,MAAM,qBAAqB,CACxC,QAAQ,EACR,iBAAiB,CAAC;YAChB,aAAa,EAAE,CAAC;YAChB,QAAQ,EAAE,IAAI;YACd,GAAG,EAAE;gBACH,QAAQ,EAAE,CAAC;gBACX,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,UAAU;aACjB;SACF,CAAC,EACF,iBAAiB,EAAE,EACnB,EAAE,EACF,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,OAAO,CACR,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,OAAO,CAAC;YAC7C,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,KAAK;SAChB,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,oBAAoB,CAAC;YACzD,aAAa,EAAE,YAAY;YAC3B,EAAE,EAAE,OAAO;YACX,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,CAAC;SACf,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;QAErC,MAAM,MAAM,GAAG,MAAM,qBAAqB,CACxC,QAAQ,EACR,iBAAiB,CAAC;YAChB,aAAa,EAAE,CAAC;YAChB,QAAQ,EAAE,KAAK;YACf,GAAG,EAAE;gBACH,QAAQ,EAAE,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,WAAW;aAClB;SACF,CAAC,EACF,iBAAiB,EAAE,EACnB,EAAE,EACF,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,OAAO,CACR,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAC7B,4CAA4C,CAC7C,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,aAAa,EAAE,CAAC;QACvD,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,aAAa,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;QAErC,MAAM,MAAM,GAAG,MAAM,qBAAqB,CACxC,QAAQ,EACR,iBAAiB,CAAC;YAChB,QAAQ,EAAE,IAAI;YACd,GAAG,EAAE;gBACH,QAAQ,EAAE,CAAC;gBACX,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,UAAU;aACjB;SACF,CAAC,EACF,iBAAiB,EAAE,EACnB,EAAE,EACF,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,OAAO,CACR,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,aAAa,EAAE,CAAC;QACvD,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -2,10 +2,14 @@
2
2
  * Azure App Service Plan analysis
3
3
  */
4
4
  import type { WebSiteManagementClient } from "@azure/arm-appservice";
5
- import type { MonitorClient } from "@azure/arm-monitor";
6
5
  import * as armResources from "@azure/arm-resources";
7
6
  import type { AnalysisResult, Thresholds } from "../../types.js";
8
- import { type MetricsCache } from "../utils.js";
7
+ import type { PricingService } from "../pricing/pricing-service.js";
8
+ import { type MetricsCache, type MonitorClientLike } from "../utils.js";
9
+ export type AppServicePlanClientLike = {
10
+ appServicePlans: Pick<WebSiteManagementClient["appServicePlans"], "get">;
11
+ };
12
+ type AppServicePlanPricing = Pick<PricingService, "resolveAppServicePlan">;
9
13
  /**
10
14
  * Analyzes an Azure App Service Plan for potential cost optimization.
11
15
  *
@@ -15,5 +19,6 @@ import { type MetricsCache } from "../utils.js";
15
19
  * @param timespanDays - Number of days to analyze metrics
16
20
  * @returns Analysis result with cost risk and reason
17
21
  */
18
- export declare function analyzeAppServicePlan(resource: armResources.GenericResource, webSiteClient: WebSiteManagementClient, monitorClient: MonitorClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean, cache?: MetricsCache): Promise<AnalysisResult>;
22
+ export declare function analyzeAppServicePlan(resource: armResources.GenericResource, webSiteClient: AppServicePlanClientLike, monitorClient: MonitorClientLike, timespanDays: number, thresholds?: Thresholds, verbose?: boolean, cache?: MetricsCache, pricing?: AppServicePlanPricing): Promise<AnalysisResult>;
23
+ export {};
19
24
  //# sourceMappingURL=app-service.d.ts.map
@@ -1 +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,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CA2FzB"}
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;AAErE,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAIvB,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,EAAE,IAAI,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,CAAC;CAC1E,CAAC;AAEF,KAAK,qBAAqB,GAAG,IAAI,CAAC,cAAc,EAAE,uBAAuB,CAAC,CAAC;AAE3E;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,wBAAwB,EACvC,aAAa,EAAE,iBAAiB,EAChC,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,cAAc,CAAC,CAwGzB"}
@@ -13,7 +13,7 @@ import { getMetric, verboseLog, verboseLogAnalysisResult, verboseLogResourceStar
13
13
  * @param timespanDays - Number of days to analyze metrics
14
14
  * @returns Analysis result with cost risk and reason
15
15
  */
16
- export async function analyzeAppServicePlan(resource, webSiteClient, monitorClient, timespanDays, thresholds = DEFAULT_THRESHOLDS, verbose = false, cache) {
16
+ export async function analyzeAppServicePlan(resource, webSiteClient, monitorClient, timespanDays, thresholds = DEFAULT_THRESHOLDS, verbose = false, cache, pricing) {
17
17
  verboseLogResourceStart(verbose, resource.name || "unknown", "App Service Plan (microsoft.web/serverfarms)");
18
18
  verboseLog(verbose, "Resource details:", resource);
19
19
  const costRisk = "high";
@@ -33,8 +33,9 @@ export async function analyzeAppServicePlan(resource, webSiteClient, monitorClie
33
33
  // Get detailed App Service Plan information
34
34
  const planDetails = await webSiteClient.appServicePlans.get(resourceGroupName, planName);
35
35
  verboseLog(verbose, "App Service Plan API details:", planDetails);
36
+ const hasNoApps = planDetails.numberOfSites === 0;
36
37
  // Check if the plan has no apps
37
- if (!planDetails.numberOfSites || planDetails.numberOfSites === 0) {
38
+ if (hasNoApps) {
38
39
  reason += "App Service Plan has no apps deployed. ";
39
40
  }
40
41
  // Check CPU and Memory metrics
@@ -54,6 +55,10 @@ export async function analyzeAppServicePlan(resource, webSiteClient, monitorClie
54
55
  cpuPercentage < thresholds.appService.premiumCpuPercent) {
55
56
  reason += "Premium tier with low resource utilization. ";
56
57
  }
58
+ const suspectedUnused = reason.length > 0;
59
+ const result = await enrichWithPricing({ costRisk, reason: reason.trim(), suspectedUnused }, planDetails, resource, hasNoApps, pricing);
60
+ verboseLogAnalysisResult(verbose, result);
61
+ return result;
57
62
  }
58
63
  catch (error) {
59
64
  const logger = getLogger(["savemoney", "azure"]);
@@ -65,4 +70,32 @@ export async function analyzeAppServicePlan(resource, webSiteClient, monitorClie
65
70
  verboseLogAnalysisResult(verbose, result);
66
71
  return result;
67
72
  }
73
+ async function enrichWithPricing(result, planDetails, resource, hasNoApps, pricing) {
74
+ // Underutilized plans need right-sizing analysis, not a full removable
75
+ // cost-at-risk.
76
+ if (!pricing || !result.suspectedUnused || !hasNoApps) {
77
+ return result;
78
+ }
79
+ try {
80
+ const skuName = planDetails.sku?.name;
81
+ const armRegionName = planDetails.location ?? resource.location;
82
+ if (!skuName || !armRegionName) {
83
+ return result;
84
+ }
85
+ const estimatedMonthlySavings = await pricing.resolveAppServicePlan({
86
+ armRegionName,
87
+ os: planDetails.reserved === true ? "linux" : "windows",
88
+ skuName,
89
+ workerCount: planDetails.sku?.capacity ?? planDetails.numberOfWorkers,
90
+ });
91
+ return estimatedMonthlySavings
92
+ ? { ...result, estimatedMonthlySavings }
93
+ : result;
94
+ }
95
+ catch (error) {
96
+ const logger = getLogger(["savemoney", "azure"]);
97
+ logger.warn(`Failed to enrich App Service Plan ${resource.name ?? "unknown"} with pricing: ${error instanceof Error ? error.message : error}`);
98
+ return result;
99
+ }
100
+ }
68
101
  //# sourceMappingURL=app-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-service.js","sourceRoot":"","sources":["../../../src/azure/resources/app-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-service.js","sourceRoot":"","sources":["../../../src/azure/resources/app-service.ts"],"names":[],"mappings":"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"}
@@ -4,13 +4,15 @@
4
4
  import type { ComputeManagementClient } from "@azure/arm-compute";
5
5
  import * as armResources from "@azure/arm-resources";
6
6
  import type { AnalysisResult } from "../../types.js";
7
+ import type { PricingService } from "../pricing/pricing-service.js";
7
8
  /**
8
9
  * Analyzes an Azure Managed Disk for potential cost optimization.
9
10
  *
10
11
  * @param resource - The Azure resource object
11
12
  * @param computeClient - Azure Compute client for disk details
12
13
  * @param verbose - Whether verbose logging is enabled
14
+ * @param pricing - Optional pricing service for monetary enrichment
13
15
  * @returns Analysis result with cost risk and reason
14
16
  */
15
- export declare function analyzeDisk(resource: armResources.GenericResource, computeClient: ComputeManagementClient, verbose?: boolean): Promise<AnalysisResult>;
17
+ export declare function analyzeDisk(resource: armResources.GenericResource, computeClient: ComputeManagementClient, verbose?: boolean, pricing?: PricingService): Promise<AnalysisResult>;
16
18
  //# sourceMappingURL=disk.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"disk.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/disk.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAQrD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,OAAO,UAAQ,GACd,OAAO,CAAC,cAAc,CAAC,CA0DzB"}
1
+ {"version":3,"file":"disk.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/disk.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAQ,MAAM,oBAAoB,CAAC;AAExE,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAIrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAUpE;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,OAAO,UAAQ,EACf,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CA2DzB"}
@@ -2,6 +2,7 @@
2
2
  * Azure Managed Disk analysis
3
3
  */
4
4
  import { getLogger } from "@logtape/logtape";
5
+ import { DiskSkuSchema } from "../pricing/resolvers/index.js";
5
6
  import { verboseLog, verboseLogAnalysisResult, verboseLogResourceStart, } from "../utils.js";
6
7
  /**
7
8
  * Analyzes an Azure Managed Disk for potential cost optimization.
@@ -9,9 +10,10 @@ import { verboseLog, verboseLogAnalysisResult, verboseLogResourceStart, } from "
9
10
  * @param resource - The Azure resource object
10
11
  * @param computeClient - Azure Compute client for disk details
11
12
  * @param verbose - Whether verbose logging is enabled
13
+ * @param pricing - Optional pricing service for monetary enrichment
12
14
  * @returns Analysis result with cost risk and reason
13
15
  */
14
- export async function analyzeDisk(resource, computeClient, verbose = false) {
16
+ export async function analyzeDisk(resource, computeClient, verbose = false, pricing) {
15
17
  verboseLogResourceStart(verbose, resource.name || "unknown", "Managed Disk (microsoft.compute/disks)");
16
18
  verboseLog(verbose, "Resource details:", resource);
17
19
  const costRisk = "medium";
@@ -38,8 +40,9 @@ export async function analyzeDisk(resource, computeClient, verbose = false) {
38
40
  reason: "Disk is unattached. ",
39
41
  suspectedUnused: true,
40
42
  };
41
- verboseLogAnalysisResult(verbose, result);
42
- return result;
43
+ const enriched = await enrichWithPricing(result, diskDetails, pricing);
44
+ verboseLogAnalysisResult(verbose, enriched);
45
+ return enriched;
43
46
  }
44
47
  }
45
48
  catch (error) {
@@ -53,4 +56,33 @@ export async function analyzeDisk(resource, computeClient, verbose = false) {
53
56
  verboseLogAnalysisResult(verbose, result);
54
57
  return result;
55
58
  }
59
+ /**
60
+ * Best-effort: looks up the monthly price for the disk's `(sku, size,
61
+ * region)` triple and attaches it as `estimatedMonthlySavings`. Any
62
+ * failure (unsupported SKU, missing fields, resolver error) leaves the
63
+ * original result unchanged.
64
+ */
65
+ async function enrichWithPricing(result, diskDetails, pricing) {
66
+ if (!pricing)
67
+ return result;
68
+ const sku = toSupportedSku(diskDetails.sku?.name);
69
+ const diskSizeGiB = diskDetails.diskSizeGB;
70
+ const armRegionName = diskDetails.location;
71
+ if (!sku || !diskSizeGiB || !armRegionName)
72
+ return result;
73
+ const money = await pricing.resolveDisk({
74
+ armRegionName,
75
+ diskSizeGiB,
76
+ sku,
77
+ });
78
+ return money ? { ...result, estimatedMonthlySavings: money } : result;
79
+ }
80
+ /**
81
+ * Narrows the SDK's `sku.name` string to the resolver-supported subset
82
+ * (excludes `UltraSSD_LRS` and any future tiers we don't price yet).
83
+ */
84
+ function toSupportedSku(name) {
85
+ const parsed = DiskSkuSchema.safeParse(name);
86
+ return parsed.success ? parsed.data : undefined;
87
+ }
56
88
  //# sourceMappingURL=disk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"disk.js","sourceRoot":"","sources":["../../../src/azure/resources/disk.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EACL,UAAU,EACV,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAErB;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAsC,EACtC,aAAsC,EACtC,OAAO,GAAG,KAAK;IAEf,uBAAuB,CACrB,OAAO,EACP,QAAQ,CAAC,IAAI,IAAI,SAAS,EAC1B,wCAAwC,CACzC,CAAC;IACF,UAAU,CAAC,OAAO,EAAE,mBAAmB,EAAE,QAAQ,CAAC,CAAC;IAEnD,MAAM,QAAQ,GAA8B,QAAQ,CAAC;IAErD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO;YACL,QAAQ;YACR,MAAM,EAAE,yBAAyB;YACjC,eAAe,EAAE,KAAK;SACvB,CAAC;IACJ,CAAC;IAED,wDAAwD;IACxD,MAAM,aAAa,GAAG,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,iBAAiB,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IAElC,IAAI,CAAC;QACH,wDAAwD;QACxD,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,GAAG,CAC/C,iBAAiB,EACjB,QAAQ,CACT,CAAC;QAEF,UAAU,CAAC,OAAO,EAAE,mBAAmB,EAAE,WAAW,CAAC,CAAC;QAEtD,8BAA8B;QAC9B,IACE,WAAW,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,YAAY;YACrD,CAAC,WAAW,CAAC,SAAS,EACtB,CAAC;YACD,MAAM,MAAM,GAAG;gBACb,QAAQ;gBACR,MAAM,EAAE,sBAAsB;gBAC9B,eAAe,EAAE,IAAI;aACtB,CAAC;YACF,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC1C,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CACT,kCAAkC,QAAQ,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAChG,CAAC;QACF,oDAAoD;QACpD,wDAAwD;QACxD,+DAA+D;IACjE,CAAC;IAED,MAAM,MAAM,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IAChE,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"disk.js","sourceRoot":"","sources":["../../../src/azure/resources/disk.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import type { MonitorClient } from "@azure/arm-monitor";
5
5
  import type { NetworkManagementClient } from "@azure/arm-network";
6
6
  import * as armResources from "@azure/arm-resources";
7
7
  import type { AnalysisResult, Thresholds } from "../../types.js";
8
+ import type { PricingService } from "../pricing/pricing-service.js";
8
9
  import { type MetricsCache } from "../utils.js";
9
10
  /**
10
11
  * Analyzes an Azure Public IP for potential cost optimization.
@@ -13,7 +14,8 @@ import { type MetricsCache } from "../utils.js";
13
14
  * @param networkClient - Azure Network client for Public IP details
14
15
  * @param monitorClient - Azure Monitor client for metrics
15
16
  * @param timespanDays - Number of days to analyze metrics
17
+ * @param pricing - Optional pricing facade for estimated monthly cost
16
18
  * @returns Analysis result with cost risk and reason
17
19
  */
18
- export declare function analyzePublicIp(resource: armResources.GenericResource, networkClient: NetworkManagementClient, monitorClient: MonitorClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean, cache?: MetricsCache): Promise<AnalysisResult>;
20
+ export declare function analyzePublicIp(resource: armResources.GenericResource, networkClient: NetworkManagementClient, monitorClient: MonitorClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean, cache?: MetricsCache, pricing?: PricingService): Promise<AnalysisResult>;
19
21
  //# sourceMappingURL=public-ip.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public-ip.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/public-ip.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CAuEzB"}
1
+ {"version":3,"file":"public-ip.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/public-ip.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,uBAAuB,EACtC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CAmFzB"}
@@ -11,9 +11,10 @@ import { getMetric, verboseLog, verboseLogAnalysisResult, verboseLogResourceStar
11
11
  * @param networkClient - Azure Network client for Public IP details
12
12
  * @param monitorClient - Azure Monitor client for metrics
13
13
  * @param timespanDays - Number of days to analyze metrics
14
+ * @param pricing - Optional pricing facade for estimated monthly cost
14
15
  * @returns Analysis result with cost risk and reason
15
16
  */
16
- export async function analyzePublicIp(resource, networkClient, monitorClient, timespanDays, thresholds = DEFAULT_THRESHOLDS, verbose = false, cache) {
17
+ export async function analyzePublicIp(resource, networkClient, monitorClient, timespanDays, thresholds = DEFAULT_THRESHOLDS, verbose = false, cache, pricing) {
17
18
  verboseLogResourceStart(verbose, resource.name || "unknown", "Public IP (microsoft.network/publicipaddresses)");
18
19
  verboseLog(verbose, "Resource details:", resource);
19
20
  const costRisk = "medium";
@@ -29,9 +30,12 @@ export async function analyzePublicIp(resource, networkClient, monitorClient, ti
29
30
  const resourceParts = resource.id.split("/");
30
31
  const resourceGroupName = resourceParts[4];
31
32
  const publicIpName = resourceParts[8];
33
+ // Capture details outside the try so the pricing enrichment below can
34
+ // reuse them without an extra API round-trip.
35
+ let publicIpDetails;
32
36
  try {
33
37
  // Get detailed Public IP information
34
- const publicIpDetails = await networkClient.publicIPAddresses.get(resourceGroupName, publicIpName);
38
+ publicIpDetails = await networkClient.publicIPAddresses.get(resourceGroupName, publicIpName);
35
39
  verboseLog(verbose, "Public IP API details:", publicIpDetails);
36
40
  // Check if Public IP is not associated with any resource
37
41
  if (!publicIpDetails.ipConfiguration && !publicIpDetails.natGateway) {
@@ -54,8 +58,42 @@ export async function analyzePublicIp(resource, networkClient, monitorClient, ti
54
58
  reason += "Could not retrieve detailed Public IP information. ";
55
59
  }
56
60
  const suspectedUnused = reason.length > 0;
57
- const result = { costRisk, reason: reason.trim(), suspectedUnused };
61
+ const baseResult = { costRisk, reason: reason.trim(), suspectedUnused };
62
+ const result = await enrichWithPricing(baseResult, resource, publicIpDetails, pricing);
58
63
  verboseLogAnalysisResult(verbose, result);
59
64
  return result;
60
65
  }
66
+ /**
67
+ * Best-effort enrichment of a Public IP analysis result with the
68
+ * estimated monthly cost recoverable by removing the resource.
69
+ */
70
+ async function enrichWithPricing(result, resource, publicIpDetails, pricing) {
71
+ if (!pricing || !result.suspectedUnused || !publicIpDetails) {
72
+ return result;
73
+ }
74
+ try {
75
+ const skuName = publicIpDetails.sku?.name?.toLowerCase();
76
+ const allocation = publicIpDetails.publicIPAllocationMethod?.toLowerCase();
77
+ const armRegionName = publicIpDetails.location ?? resource.location;
78
+ if (!armRegionName ||
79
+ (skuName !== "basic" && skuName !== "standard") ||
80
+ (allocation !== "static" && allocation !== "dynamic")) {
81
+ return result;
82
+ }
83
+ const estimatedMonthlySavings = await pricing.resolvePublicIp({
84
+ allocation,
85
+ armRegionName,
86
+ sku: skuName,
87
+ });
88
+ if (!estimatedMonthlySavings) {
89
+ return result;
90
+ }
91
+ return { ...result, estimatedMonthlySavings };
92
+ }
93
+ catch (error) {
94
+ const logger = getLogger(["savemoney", "azure"]);
95
+ logger.warn(`Failed to enrich Public IP with pricing: ${error instanceof Error ? error.message : error}`);
96
+ return result;
97
+ }
98
+ }
61
99
  //# sourceMappingURL=public-ip.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"public-ip.js","sourceRoot":"","sources":["../../../src/azure/resources/public-ip.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"public-ip.js","sourceRoot":"","sources":["../../../src/azure/resources/public-ip.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import type { ComputeManagementClient } from "@azure/arm-compute";
5
5
  import type { MonitorClient } from "@azure/arm-monitor";
6
6
  import * as armResources from "@azure/arm-resources";
7
7
  import type { AnalysisResult, Thresholds } from "../../types.js";
8
+ import type { PricingService } from "../pricing/pricing-service.js";
8
9
  import { type MetricsCache } from "../utils.js";
9
10
  /**
10
11
  * Analyzes an Azure Virtual Machine for potential cost optimization.
@@ -14,7 +15,9 @@ import { type MetricsCache } from "../utils.js";
14
15
  * @param computeClient - Azure Compute client for VM details
15
16
  * @param timespanDays - Number of days to analyze metrics
16
17
  * @param verbose - Whether verbose logging is enabled
18
+ * @param cache - Run-scoped metrics cache
19
+ * @param pricing - Optional pricing facade for estimated monthly cost
17
20
  * @returns Analysis result with cost risk and reason
18
21
  */
19
- export declare function analyzeVM(resource: armResources.GenericResource, monitorClient: MonitorClient, computeClient: ComputeManagementClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean, cache?: MetricsCache): Promise<AnalysisResult>;
22
+ export declare function analyzeVM(resource: armResources.GenericResource, monitorClient: MonitorClient, computeClient: ComputeManagementClient, timespanDays: number, thresholds?: Thresholds, verbose?: boolean, cache?: MetricsCache, pricing?: PricingService): Promise<AnalysisResult>;
20
23
  //# sourceMappingURL=vm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vm.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/vm.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,aAAa,EAC5B,aAAa,EAAE,uBAAuB,EACtC,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CA6FzB"}
1
+ {"version":3,"file":"vm.d.ts","sourceRoot":"","sources":["../../../src/azure/resources/vm.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,OAAO,EAEL,KAAK,YAAY,EAIlB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,YAAY,CAAC,eAAe,EACtC,aAAa,EAAE,aAAa,EAC5B,aAAa,EAAE,uBAAuB,EACtC,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,UAA+B,EAC3C,OAAO,UAAQ,EACf,KAAK,CAAC,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CAkHzB"}
@@ -12,9 +12,11 @@ import { getMetric, verboseLog, verboseLogAnalysisResult, verboseLogResourceStar
12
12
  * @param computeClient - Azure Compute client for VM details
13
13
  * @param timespanDays - Number of days to analyze metrics
14
14
  * @param verbose - Whether verbose logging is enabled
15
+ * @param cache - Run-scoped metrics cache
16
+ * @param pricing - Optional pricing facade for estimated monthly cost
15
17
  * @returns Analysis result with cost risk and reason
16
18
  */
17
- export async function analyzeVM(resource, monitorClient, computeClient, timespanDays, thresholds = DEFAULT_THRESHOLDS, verbose = false, cache) {
19
+ export async function analyzeVM(resource, monitorClient, computeClient, timespanDays, thresholds = DEFAULT_THRESHOLDS, verbose = false, cache, pricing) {
18
20
  verboseLogResourceStart(verbose, resource.name || "unknown", "Virtual Machine (microsoft.compute/virtualmachines)");
19
21
  verboseLog(verbose, "Resource details:", resource);
20
22
  const costRisk = "high";
@@ -37,20 +39,20 @@ export async function analyzeVM(resource, monitorClient, computeClient, timespan
37
39
  // Check power state from instance view
38
40
  const vmStatus = instanceView.statuses?.find((s) => s.code?.startsWith("PowerState/"));
39
41
  if (vmStatus?.code === "PowerState/deallocated") {
40
- const result = {
42
+ const result = await enrichWithPricing({
41
43
  costRisk,
42
44
  reason: "VM is deallocated. ",
43
45
  suspectedUnused: true,
44
- };
46
+ }, resource, computeClient, resourceGroupName, vmName, pricing);
45
47
  verboseLogAnalysisResult(verbose, result);
46
48
  return result;
47
49
  }
48
50
  if (vmStatus?.code === "PowerState/stopped") {
49
- const result = {
51
+ const result = await enrichWithPricing({
50
52
  costRisk,
51
53
  reason: "VM is stopped. ",
52
54
  suspectedUnused: true,
53
- };
55
+ }, resource, computeClient, resourceGroupName, vmName, pricing);
54
56
  verboseLogAnalysisResult(verbose, result);
55
57
  return result;
56
58
  }
@@ -69,8 +71,49 @@ export async function analyzeVM(resource, monitorClient, computeClient, timespan
69
71
  if (networkIn !== null && networkIn < thresholds.vm.networkInBytesPerDay) {
70
72
  reason += `Low network traffic (${(networkIn / 1024 / 1024).toFixed(2)} MB/day avg). `;
71
73
  }
72
- const result = { costRisk, reason, suspectedUnused: reason.length > 0 };
74
+ const result = await enrichWithPricing({ costRisk, reason, suspectedUnused: reason.length > 0 }, resource, computeClient, resourceGroupName, vmName, pricing);
73
75
  verboseLogAnalysisResult(verbose, result);
74
76
  return result;
75
77
  }
78
+ /**
79
+ * Best-effort enrichment of a VM analysis result with the estimated
80
+ * monthly cost recoverable by removing/right-sizing the resource.
81
+ *
82
+ * No-op when:
83
+ * - the result is not flagged as suspected unused
84
+ * - no pricing facade was provided
85
+ * - the VM SKU or region cannot be determined
86
+ * - the Retail Prices lookup returns no matching meter
87
+ *
88
+ * Any error along the way is logged at warn level and swallowed so the
89
+ * main analysis never fails because of a pricing problem.
90
+ */
91
+ async function enrichWithPricing(result, resource, computeClient, resourceGroupName, vmName, pricing) {
92
+ if (!pricing || !result.suspectedUnused) {
93
+ return result;
94
+ }
95
+ try {
96
+ const details = await computeClient.virtualMachines.get(resourceGroupName, vmName);
97
+ const armSkuName = details.hardwareProfile?.vmSize;
98
+ const armRegionName = details.location ?? resource.location;
99
+ if (!armSkuName || !armRegionName) {
100
+ return result;
101
+ }
102
+ const os = details.storageProfile?.osDisk?.osType?.toLowerCase();
103
+ const estimatedMonthlySavings = await pricing.resolveVm({
104
+ armRegionName,
105
+ armSkuName,
106
+ os: os === "windows" ? "windows" : "linux",
107
+ });
108
+ if (!estimatedMonthlySavings) {
109
+ return result;
110
+ }
111
+ return { ...result, estimatedMonthlySavings };
112
+ }
113
+ catch (error) {
114
+ const logger = getLogger(["savemoney", "azure"]);
115
+ logger.warn(`Failed to enrich VM ${vmName} with pricing: ${error instanceof Error ? error.message : error}`);
116
+ return result;
117
+ }
118
+ }
76
119
  //# sourceMappingURL=vm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vm.js","sourceRoot":"","sources":["../../../src/azure/resources/vm.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vm.js","sourceRoot":"","sources":["../../../src/azure/resources/vm.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type * as armResources from "@azure/arm-resources";
5
5
  import type { Finding } from "../finding.js";
6
+ import type { Config } from "../schema.js";
6
7
  import type { AnalysisResult, BaseConfig, CostRisk, Thresholds } from "../types.js";
7
8
  /**
8
9
  * Azure configuration extending base config
@@ -19,14 +20,21 @@ export type AzureConfig = BaseConfig & {
19
20
  * If omitted, all resources are analyzed.
20
21
  */
21
22
  filterTags?: Map<string, string>;
23
+ /**
24
+ * Runtime-only pricing enrichment switch. This is intentionally not exposed
25
+ * in YAML config: the Retail Prices lookup uses internal defaults unless the
26
+ * CLI disables it for offline/debug runs.
27
+ */
28
+ pricing?: PricingConfig;
22
29
  /**
23
30
  * Which finding sources to include in the run.
24
31
  * - `"custom"` → enables the per-resource analyzer plugins
25
32
  * - `"advisor"` → enables the Azure Advisor subscription-level analyzer
26
33
  *
27
- * Defaults to `["advisor", "custom"]` when omitted (i.e. all sources).
34
+ * Config loading applies the `["advisor", "custom"]` default, so this list
35
+ * is always defined and contains at least one source.
28
36
  */
29
- sources?: AzureSource[];
37
+ sources: Config["azure"]["sources"];
30
38
  subscriptionIds: string[];
31
39
  /**
32
40
  * Analysis thresholds. Defaults from DEFAULT_THRESHOLDS are used when not provided.
@@ -71,5 +79,12 @@ export type AzureResourceReport = {
71
79
  * Narrowed from `FindingSource` to exclude "aws", which is not a valid
72
80
  * filter for Azure runs and would silently produce an empty report.
73
81
  */
74
- export type AzureSource = "advisor" | "custom";
82
+ export type AzureSource = Config["azure"]["sources"][number];
83
+ export type PricingConfig = {
84
+ /**
85
+ * Runtime switch used by the CLI `--no-pricing` flag. Pricing otherwise
86
+ * stays enabled with internal defaults for currency and cache settings.
87
+ */
88
+ enabled?: boolean;
89
+ };
75
90
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/azure/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,QAAQ,EAAE,YAAY,CAAC,eAAe,CAAC;CACxC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/azure/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC;IACpC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,QAAQ,EAAE,YAAY,CAAC,eAAe,CAAC;CACxC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC"}