@roberta.soliman/n8n-nodes-aws-cost-explorer 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.
- package/README.md +76 -0
- package/dist/credentials/AwsCostExplorerApi.credentials.d.ts +5 -0
- package/dist/credentials/AwsCostExplorerApi.credentials.js +579 -0
- package/dist/nodes/AwsCostExplorer/AwsCostExplorer.node.d.ts +5 -0
- package/dist/nodes/AwsCostExplorer/AwsCostExplorer.node.js +579 -0
- package/dist/nodes/AwsCostExplorer/awscostexplorer.svg +0 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# n8n-nodes-aws-cost-explorer
|
|
2
|
+
|
|
3
|
+
This is an n8n community node that lets you retrieve cost and usage data from AWS Cost Explorer in your n8n workflows.
|
|
4
|
+
|
|
5
|
+
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
|
|
10
|
+
|
|
11
|
+
To install via n8n settings:
|
|
12
|
+
1. Go to Settings > Community Nodes
|
|
13
|
+
2. Click "Install Community Node"
|
|
14
|
+
3. Enter `@roberta.soliman/n8n-nodes-aws-cost-explorer`
|
|
15
|
+
4. Click Install
|
|
16
|
+
|
|
17
|
+
## Publish (maintainer)
|
|
18
|
+
|
|
19
|
+
1. Create an [npm access token](https://www.npmjs.com/settings/~your-user/tokens) (type **Automation**).
|
|
20
|
+
2. In GitHub: **Settings → Secrets and variables → Actions** → add `NPM_TOKEN` with that token.
|
|
21
|
+
3. Bump `version` in `package.json`, commit, push.
|
|
22
|
+
4. Create a [GitHub Release](https://github.com/robertasolimandonofreo/n8n-aws-cost-explorer/releases/new) (tag `v0.1.2` matching the version in `package.json`).
|
|
23
|
+
|
|
24
|
+
The workflow [publish-npm.yml](.github/workflows/publish-npm.yml) runs on release publish and pushes the package to npm. You can also run it manually from **Actions → Publish to npm → Run workflow**.
|
|
25
|
+
|
|
26
|
+
## Prerequisites
|
|
27
|
+
|
|
28
|
+
You need:
|
|
29
|
+
- AWS account with Cost Explorer enabled
|
|
30
|
+
- AWS IAM user/role with Cost Explorer permissions
|
|
31
|
+
- AWS Access Key ID and Secret Access Key
|
|
32
|
+
|
|
33
|
+
## Credentials
|
|
34
|
+
|
|
35
|
+
This node requires AWS Cost Explorer API credentials:
|
|
36
|
+
- **AWS Access Key ID**: Your AWS access key
|
|
37
|
+
- **AWS Secret Access Key**: Your AWS secret key
|
|
38
|
+
- **Region**: AWS region (usually us-east-1 for Cost Explorer)
|
|
39
|
+
|
|
40
|
+
Required IAM permissions:
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"Version": "2012-10-17",
|
|
44
|
+
"Statement": [
|
|
45
|
+
{
|
|
46
|
+
"Effect": "Allow",
|
|
47
|
+
"Action": [
|
|
48
|
+
"ce:GetCostAndUsage",
|
|
49
|
+
"ce:GetDimensionValues"
|
|
50
|
+
],
|
|
51
|
+
"Resource": "*"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Operations
|
|
58
|
+
|
|
59
|
+
### Cost and Usage
|
|
60
|
+
- **Get**: Retrieve cost and usage data for a specified time period
|
|
61
|
+
|
|
62
|
+
### Dimension Values
|
|
63
|
+
- **Get**: Get available values for AWS cost dimensions (Service, Account, Instance Type, Region)
|
|
64
|
+
|
|
65
|
+
## Usage Example
|
|
66
|
+
|
|
67
|
+
1. Add AWS Cost Explorer node to your workflow
|
|
68
|
+
2. Configure credentials
|
|
69
|
+
3. Select "Cost and Usage" > "Get"
|
|
70
|
+
4. Set start/end dates (YYYY-MM-DD format)
|
|
71
|
+
5. Choose granularity (Daily/Monthly/Hourly)
|
|
72
|
+
6. Select metrics (Blended Cost, Unblended Cost, Usage Quantity)
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
[MIT](https://github.com/n8n-io/n8n-nodes-starter/blob/master/LICENSE.md)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class AwsCostExplorer implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.AwsCostExplorer = void 0;
|
|
27
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
28
|
+
class AwsCostExplorer {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.description = {
|
|
31
|
+
displayName: 'AWS Cost Explorer',
|
|
32
|
+
name: 'awsCostExplorer',
|
|
33
|
+
icon: 'file:awscostexplorer.svg',
|
|
34
|
+
group: ['transform'],
|
|
35
|
+
version: 1,
|
|
36
|
+
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
|
37
|
+
description: 'Get cost and usage data from AWS Cost Explorer',
|
|
38
|
+
defaults: {
|
|
39
|
+
name: 'AWS Cost Explorer',
|
|
40
|
+
},
|
|
41
|
+
inputs: ['main'],
|
|
42
|
+
outputs: ['main'],
|
|
43
|
+
credentials: [
|
|
44
|
+
{
|
|
45
|
+
name: 'awsCostExplorerApi',
|
|
46
|
+
required: true,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
properties: [
|
|
50
|
+
// ----------------------------------------------------------------
|
|
51
|
+
// Resource
|
|
52
|
+
// ----------------------------------------------------------------
|
|
53
|
+
{
|
|
54
|
+
displayName: 'Resource',
|
|
55
|
+
name: 'resource',
|
|
56
|
+
type: 'options',
|
|
57
|
+
noDataExpression: true,
|
|
58
|
+
options: [
|
|
59
|
+
{ name: 'Cost and Usage', value: 'costAndUsage' },
|
|
60
|
+
{ name: 'Cost Forecast', value: 'costForecast' },
|
|
61
|
+
{ name: 'Dimension Values', value: 'dimensionValues' },
|
|
62
|
+
{ name: 'Reserved Instances', value: 'reservedInstances' },
|
|
63
|
+
{ name: 'Savings Plans', value: 'savingsPlans' },
|
|
64
|
+
],
|
|
65
|
+
default: 'costAndUsage',
|
|
66
|
+
},
|
|
67
|
+
// ----------------------------------------------------------------
|
|
68
|
+
// Operation — Cost and Usage
|
|
69
|
+
// ----------------------------------------------------------------
|
|
70
|
+
{
|
|
71
|
+
displayName: 'Operation',
|
|
72
|
+
name: 'operation',
|
|
73
|
+
type: 'options',
|
|
74
|
+
noDataExpression: true,
|
|
75
|
+
displayOptions: { show: { resource: ['costAndUsage'] } },
|
|
76
|
+
options: [
|
|
77
|
+
{ name: 'Get', value: 'get', description: 'Get cost and usage data', action: 'Get cost and usage data' },
|
|
78
|
+
],
|
|
79
|
+
default: 'get',
|
|
80
|
+
},
|
|
81
|
+
// ----------------------------------------------------------------
|
|
82
|
+
// Operation — Cost Forecast
|
|
83
|
+
// ----------------------------------------------------------------
|
|
84
|
+
{
|
|
85
|
+
displayName: 'Operation',
|
|
86
|
+
name: 'operation',
|
|
87
|
+
type: 'options',
|
|
88
|
+
noDataExpression: true,
|
|
89
|
+
displayOptions: { show: { resource: ['costForecast'] } },
|
|
90
|
+
options: [
|
|
91
|
+
{ name: 'Get', value: 'get', description: 'Get cost forecast', action: 'Get cost forecast' },
|
|
92
|
+
],
|
|
93
|
+
default: 'get',
|
|
94
|
+
},
|
|
95
|
+
// ----------------------------------------------------------------
|
|
96
|
+
// Operation — Dimension Values
|
|
97
|
+
// ----------------------------------------------------------------
|
|
98
|
+
{
|
|
99
|
+
displayName: 'Operation',
|
|
100
|
+
name: 'operation',
|
|
101
|
+
type: 'options',
|
|
102
|
+
noDataExpression: true,
|
|
103
|
+
displayOptions: { show: { resource: ['dimensionValues'] } },
|
|
104
|
+
options: [
|
|
105
|
+
{ name: 'Get', value: 'get', description: 'Get dimension values', action: 'Get dimension values' },
|
|
106
|
+
],
|
|
107
|
+
default: 'get',
|
|
108
|
+
},
|
|
109
|
+
// ----------------------------------------------------------------
|
|
110
|
+
// Operation — Reserved Instances
|
|
111
|
+
// ----------------------------------------------------------------
|
|
112
|
+
{
|
|
113
|
+
displayName: 'Operation',
|
|
114
|
+
name: 'operation',
|
|
115
|
+
type: 'options',
|
|
116
|
+
noDataExpression: true,
|
|
117
|
+
displayOptions: { show: { resource: ['reservedInstances'] } },
|
|
118
|
+
options: [
|
|
119
|
+
{ name: 'Get Utilization', value: 'getUtilization', description: 'Get RI utilization and unused hours', action: 'Get RI utilization' },
|
|
120
|
+
{ name: 'Get Coverage', value: 'getCoverage', description: 'Get percentage of usage covered by RIs', action: 'Get RI coverage' },
|
|
121
|
+
],
|
|
122
|
+
default: 'getUtilization',
|
|
123
|
+
},
|
|
124
|
+
// ----------------------------------------------------------------
|
|
125
|
+
// Operation — Savings Plans
|
|
126
|
+
// ----------------------------------------------------------------
|
|
127
|
+
{
|
|
128
|
+
displayName: 'Operation',
|
|
129
|
+
name: 'operation',
|
|
130
|
+
type: 'options',
|
|
131
|
+
noDataExpression: true,
|
|
132
|
+
displayOptions: { show: { resource: ['savingsPlans'] } },
|
|
133
|
+
options: [
|
|
134
|
+
{ name: 'Get Utilization', value: 'getUtilization', description: 'Get SP utilization and unused commitment', action: 'Get SP utilization' },
|
|
135
|
+
{ name: 'Get Coverage', value: 'getCoverage', description: 'Get percentage of usage covered by SPs', action: 'Get SP coverage' },
|
|
136
|
+
],
|
|
137
|
+
default: 'getUtilization',
|
|
138
|
+
},
|
|
139
|
+
// ----------------------------------------------------------------
|
|
140
|
+
// Shared: Start Date / End Date
|
|
141
|
+
// ----------------------------------------------------------------
|
|
142
|
+
{
|
|
143
|
+
displayName: 'Start Date',
|
|
144
|
+
name: 'startDate',
|
|
145
|
+
type: 'string',
|
|
146
|
+
displayOptions: {
|
|
147
|
+
show: {
|
|
148
|
+
resource: ['costAndUsage', 'costForecast', 'dimensionValues', 'reservedInstances', 'savingsPlans'],
|
|
149
|
+
operation: ['get', 'getUtilization', 'getCoverage'],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
default: '',
|
|
153
|
+
placeholder: '2024-01-01',
|
|
154
|
+
description: 'Start date in YYYY-MM-DD format',
|
|
155
|
+
required: true,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
displayName: 'End Date',
|
|
159
|
+
name: 'endDate',
|
|
160
|
+
type: 'string',
|
|
161
|
+
displayOptions: {
|
|
162
|
+
show: {
|
|
163
|
+
resource: ['costAndUsage', 'costForecast', 'dimensionValues', 'reservedInstances', 'savingsPlans'],
|
|
164
|
+
operation: ['get', 'getUtilization', 'getCoverage'],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
default: '',
|
|
168
|
+
placeholder: '2024-01-31',
|
|
169
|
+
description: 'End date in YYYY-MM-DD format (exclusive)',
|
|
170
|
+
required: true,
|
|
171
|
+
},
|
|
172
|
+
// ----------------------------------------------------------------
|
|
173
|
+
// Cost and Usage: Granularity
|
|
174
|
+
// ----------------------------------------------------------------
|
|
175
|
+
{
|
|
176
|
+
displayName: 'Granularity',
|
|
177
|
+
name: 'granularity',
|
|
178
|
+
type: 'options',
|
|
179
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
180
|
+
options: [
|
|
181
|
+
{ name: 'Daily', value: 'DAILY' },
|
|
182
|
+
{ name: 'Monthly', value: 'MONTHLY' },
|
|
183
|
+
{ name: 'Hourly', value: 'HOURLY' },
|
|
184
|
+
],
|
|
185
|
+
default: 'MONTHLY',
|
|
186
|
+
},
|
|
187
|
+
// ----------------------------------------------------------------
|
|
188
|
+
// Cost Forecast: Granularity
|
|
189
|
+
// ----------------------------------------------------------------
|
|
190
|
+
{
|
|
191
|
+
displayName: 'Granularity',
|
|
192
|
+
name: 'granularity',
|
|
193
|
+
type: 'options',
|
|
194
|
+
displayOptions: { show: { resource: ['costForecast'], operation: ['get'] } },
|
|
195
|
+
options: [
|
|
196
|
+
{ name: 'Daily', value: 'DAILY' },
|
|
197
|
+
{ name: 'Monthly', value: 'MONTHLY' },
|
|
198
|
+
],
|
|
199
|
+
default: 'MONTHLY',
|
|
200
|
+
},
|
|
201
|
+
// ----------------------------------------------------------------
|
|
202
|
+
// RI / SP: Granularity
|
|
203
|
+
// ----------------------------------------------------------------
|
|
204
|
+
{
|
|
205
|
+
displayName: 'Granularity',
|
|
206
|
+
name: 'granularity',
|
|
207
|
+
type: 'options',
|
|
208
|
+
displayOptions: {
|
|
209
|
+
show: {
|
|
210
|
+
resource: ['reservedInstances', 'savingsPlans'],
|
|
211
|
+
operation: ['getUtilization', 'getCoverage'],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
options: [
|
|
215
|
+
{ name: 'Daily', value: 'DAILY' },
|
|
216
|
+
{ name: 'Monthly', value: 'MONTHLY' },
|
|
217
|
+
],
|
|
218
|
+
default: 'MONTHLY',
|
|
219
|
+
},
|
|
220
|
+
// ----------------------------------------------------------------
|
|
221
|
+
// Cost and Usage: Metrics
|
|
222
|
+
// ----------------------------------------------------------------
|
|
223
|
+
{
|
|
224
|
+
displayName: 'Metrics',
|
|
225
|
+
name: 'metrics',
|
|
226
|
+
type: 'multiOptions',
|
|
227
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
228
|
+
options: [
|
|
229
|
+
{ name: 'Amortized Cost', value: 'AmortizedCost' },
|
|
230
|
+
{ name: 'Blended Cost', value: 'BlendedCost' },
|
|
231
|
+
{ name: 'Net Amortized Cost', value: 'NetAmortizedCost' },
|
|
232
|
+
{ name: 'Net Unblended Cost', value: 'NetUnblendedCost' },
|
|
233
|
+
{ name: 'Unblended Cost', value: 'UnblendedCost' },
|
|
234
|
+
{ name: 'Usage Quantity', value: 'UsageQuantity' },
|
|
235
|
+
{ name: 'Normalized Usage Amount', value: 'NormalizedUsageAmount' },
|
|
236
|
+
],
|
|
237
|
+
default: ['UnblendedCost'],
|
|
238
|
+
description: 'Which cost metrics to return. Use Unblended Cost for most cases.',
|
|
239
|
+
},
|
|
240
|
+
// ----------------------------------------------------------------
|
|
241
|
+
// Cost Forecast: Metric (single)
|
|
242
|
+
// ----------------------------------------------------------------
|
|
243
|
+
{
|
|
244
|
+
displayName: 'Metric',
|
|
245
|
+
name: 'forecastMetric',
|
|
246
|
+
type: 'options',
|
|
247
|
+
displayOptions: { show: { resource: ['costForecast'], operation: ['get'] } },
|
|
248
|
+
options: [
|
|
249
|
+
{ name: 'Amortized Cost', value: 'AMORTIZED_COST' },
|
|
250
|
+
{ name: 'Blended Cost', value: 'BLENDED_COST' },
|
|
251
|
+
{ name: 'Net Amortized Cost', value: 'NET_AMORTIZED_COST' },
|
|
252
|
+
{ name: 'Net Unblended Cost', value: 'NET_UNBLENDED_COST' },
|
|
253
|
+
{ name: 'Unblended Cost', value: 'UNBLENDED_COST' },
|
|
254
|
+
],
|
|
255
|
+
default: 'UNBLENDED_COST',
|
|
256
|
+
},
|
|
257
|
+
// ----------------------------------------------------------------
|
|
258
|
+
// Cost and Usage: Group By
|
|
259
|
+
// ----------------------------------------------------------------
|
|
260
|
+
{
|
|
261
|
+
displayName: 'Group By',
|
|
262
|
+
name: 'groupBy',
|
|
263
|
+
type: 'options',
|
|
264
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
265
|
+
options: [
|
|
266
|
+
{ name: 'None', value: 'none' },
|
|
267
|
+
{ name: 'Service', value: 'SERVICE' },
|
|
268
|
+
{ name: 'Linked Account', value: 'LINKED_ACCOUNT' },
|
|
269
|
+
{ name: 'Region', value: 'REGION' },
|
|
270
|
+
{ name: 'Purchase Type', value: 'PURCHASE_TYPE' },
|
|
271
|
+
{ name: 'Instance Type', value: 'INSTANCE_TYPE' },
|
|
272
|
+
{ name: 'Usage Type', value: 'USAGE_TYPE' },
|
|
273
|
+
{ name: 'Tag', value: 'TAG' },
|
|
274
|
+
],
|
|
275
|
+
default: 'none',
|
|
276
|
+
description: 'Dimension to group costs by',
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
displayName: 'Tag Key',
|
|
280
|
+
name: 'tagKey',
|
|
281
|
+
type: 'string',
|
|
282
|
+
displayOptions: {
|
|
283
|
+
show: {
|
|
284
|
+
resource: ['costAndUsage'],
|
|
285
|
+
operation: ['get'],
|
|
286
|
+
groupBy: ['TAG'],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
default: '',
|
|
290
|
+
placeholder: 'Environment',
|
|
291
|
+
description: 'Tag key to group costs by (required when Group By = Tag)',
|
|
292
|
+
required: true,
|
|
293
|
+
},
|
|
294
|
+
// ----------------------------------------------------------------
|
|
295
|
+
// Cost and Usage: Secondary Group By
|
|
296
|
+
// ----------------------------------------------------------------
|
|
297
|
+
{
|
|
298
|
+
displayName: 'Secondary Group By',
|
|
299
|
+
name: 'groupBySecondary',
|
|
300
|
+
type: 'options',
|
|
301
|
+
displayOptions: {
|
|
302
|
+
show: {
|
|
303
|
+
resource: ['costAndUsage'],
|
|
304
|
+
operation: ['get'],
|
|
305
|
+
},
|
|
306
|
+
hide: {
|
|
307
|
+
groupBy: ['none'],
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
options: [
|
|
311
|
+
{ name: 'None', value: 'none' },
|
|
312
|
+
{ name: 'Service', value: 'SERVICE' },
|
|
313
|
+
{ name: 'Linked Account', value: 'LINKED_ACCOUNT' },
|
|
314
|
+
{ name: 'Region', value: 'REGION' },
|
|
315
|
+
{ name: 'Purchase Type', value: 'PURCHASE_TYPE' },
|
|
316
|
+
],
|
|
317
|
+
default: 'none',
|
|
318
|
+
description: 'Add a second grouping dimension (max 2 GroupBy supported by AWS)',
|
|
319
|
+
},
|
|
320
|
+
// ----------------------------------------------------------------
|
|
321
|
+
// Cost and Usage: Filters
|
|
322
|
+
// ----------------------------------------------------------------
|
|
323
|
+
{
|
|
324
|
+
displayName: 'Filter by Service',
|
|
325
|
+
name: 'serviceFilter',
|
|
326
|
+
type: 'string',
|
|
327
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
328
|
+
default: '',
|
|
329
|
+
placeholder: 'Amazon EC2',
|
|
330
|
+
description: 'Filter by a specific AWS service name. Leave empty for all services.',
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
displayName: 'Filter by Linked Account',
|
|
334
|
+
name: 'linkedAccountFilter',
|
|
335
|
+
type: 'string',
|
|
336
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
337
|
+
default: '',
|
|
338
|
+
placeholder: '123456789012',
|
|
339
|
+
description: 'Filter by a specific linked account ID. Leave empty for all accounts.',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
displayName: 'Filter by Region',
|
|
343
|
+
name: 'regionFilter',
|
|
344
|
+
type: 'string',
|
|
345
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
346
|
+
default: '',
|
|
347
|
+
placeholder: 'us-east-1',
|
|
348
|
+
description: 'Filter by a specific AWS region. Leave empty for all regions.',
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
displayName: 'Exclude Credits',
|
|
352
|
+
name: 'excludeCredits',
|
|
353
|
+
type: 'boolean',
|
|
354
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
355
|
+
default: false,
|
|
356
|
+
description: 'Whether to exclude credits, refunds and discounts from the response',
|
|
357
|
+
},
|
|
358
|
+
// ----------------------------------------------------------------
|
|
359
|
+
// Dimension Values: Dimension
|
|
360
|
+
// ----------------------------------------------------------------
|
|
361
|
+
{
|
|
362
|
+
displayName: 'Dimension',
|
|
363
|
+
name: 'dimension',
|
|
364
|
+
type: 'options',
|
|
365
|
+
displayOptions: { show: { resource: ['dimensionValues'], operation: ['get'] } },
|
|
366
|
+
options: [
|
|
367
|
+
{ name: 'AZ', value: 'AZ' },
|
|
368
|
+
{ name: 'Instance Type', value: 'INSTANCE_TYPE' },
|
|
369
|
+
{ name: 'Legal Entity Name', value: 'LEGAL_ENTITY_NAME' },
|
|
370
|
+
{ name: 'Linked Account', value: 'LINKED_ACCOUNT' },
|
|
371
|
+
{ name: 'Operating System', value: 'OPERATING_SYSTEM' },
|
|
372
|
+
{ name: 'Operation', value: 'OPERATION' },
|
|
373
|
+
{ name: 'Platform', value: 'PLATFORM' },
|
|
374
|
+
{ name: 'Purchase Type', value: 'PURCHASE_TYPE' },
|
|
375
|
+
{ name: 'Region', value: 'REGION' },
|
|
376
|
+
{ name: 'Service', value: 'SERVICE' },
|
|
377
|
+
{ name: 'Usage Type', value: 'USAGE_TYPE' },
|
|
378
|
+
{ name: 'Usage Type Group', value: 'USAGE_TYPE_GROUP' },
|
|
379
|
+
],
|
|
380
|
+
default: 'SERVICE',
|
|
381
|
+
required: true,
|
|
382
|
+
},
|
|
383
|
+
// ----------------------------------------------------------------
|
|
384
|
+
// RI: Group By Service
|
|
385
|
+
// ----------------------------------------------------------------
|
|
386
|
+
{
|
|
387
|
+
displayName: 'Group By Service',
|
|
388
|
+
name: 'riGroupByService',
|
|
389
|
+
type: 'boolean',
|
|
390
|
+
displayOptions: {
|
|
391
|
+
show: {
|
|
392
|
+
resource: ['reservedInstances'],
|
|
393
|
+
operation: ['getUtilization', 'getCoverage'],
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
default: true,
|
|
397
|
+
description: 'Whether to break down RI utilization/coverage by AWS service',
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
async execute() {
|
|
403
|
+
const items = this.getInputData();
|
|
404
|
+
const returnData = [];
|
|
405
|
+
const credentials = await this.getCredentials('awsCostExplorerApi');
|
|
406
|
+
const { CostExplorerClient, GetCostAndUsageCommand, GetCostForecastCommand, GetDimensionValuesCommand, GetReservationUtilizationCommand, GetReservationCoverageCommand, GetSavingsPlansUtilizationCommand, GetSavingsPlansCoverageCommand, } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-cost-explorer')));
|
|
407
|
+
const client = new CostExplorerClient({
|
|
408
|
+
region: credentials.region || 'us-east-1',
|
|
409
|
+
credentials: {
|
|
410
|
+
accessKeyId: credentials.accessKeyId,
|
|
411
|
+
secretAccessKey: credentials.secretAccessKey,
|
|
412
|
+
...(credentials.sessionToken ? { sessionToken: credentials.sessionToken } : {}),
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
for (let i = 0; i < items.length; i++) {
|
|
416
|
+
try {
|
|
417
|
+
const resource = this.getNodeParameter('resource', i);
|
|
418
|
+
const operation = this.getNodeParameter('operation', i);
|
|
419
|
+
const startDate = this.getNodeParameter('startDate', i, '');
|
|
420
|
+
const endDate = this.getNodeParameter('endDate', i, '');
|
|
421
|
+
// ----------------------------------------------------------------
|
|
422
|
+
// Cost and Usage
|
|
423
|
+
// ----------------------------------------------------------------
|
|
424
|
+
if (resource === 'costAndUsage' && operation === 'get') {
|
|
425
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
426
|
+
const metrics = this.getNodeParameter('metrics', i);
|
|
427
|
+
const groupBy = this.getNodeParameter('groupBy', i);
|
|
428
|
+
const groupBySecondary = this.getNodeParameter('groupBySecondary', i, 'none');
|
|
429
|
+
const tagKey = this.getNodeParameter('tagKey', i, '');
|
|
430
|
+
const serviceFilter = this.getNodeParameter('serviceFilter', i, '');
|
|
431
|
+
const linkedAccountFilter = this.getNodeParameter('linkedAccountFilter', i, '');
|
|
432
|
+
const regionFilter = this.getNodeParameter('regionFilter', i, '');
|
|
433
|
+
const excludeCredits = this.getNodeParameter('excludeCredits', i, false);
|
|
434
|
+
const params = {
|
|
435
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
436
|
+
Granularity: granularity,
|
|
437
|
+
Metrics: metrics,
|
|
438
|
+
};
|
|
439
|
+
// Group By
|
|
440
|
+
const groupByList = [];
|
|
441
|
+
if (groupBy !== 'none') {
|
|
442
|
+
groupByList.push({
|
|
443
|
+
Type: groupBy === 'TAG' ? 'TAG' : 'DIMENSION',
|
|
444
|
+
Key: groupBy === 'TAG' ? tagKey : groupBy,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (groupBySecondary !== 'none') {
|
|
448
|
+
groupByList.push({ Type: 'DIMENSION', Key: groupBySecondary });
|
|
449
|
+
}
|
|
450
|
+
if (groupByList.length > 0) {
|
|
451
|
+
params.GroupBy = groupByList;
|
|
452
|
+
}
|
|
453
|
+
// Filters — AND together when multiple are set
|
|
454
|
+
const filterConditions = [];
|
|
455
|
+
if (serviceFilter === null || serviceFilter === void 0 ? void 0 : serviceFilter.trim()) {
|
|
456
|
+
filterConditions.push({
|
|
457
|
+
Dimensions: { Key: 'SERVICE', Values: [serviceFilter.trim()] },
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
if (linkedAccountFilter === null || linkedAccountFilter === void 0 ? void 0 : linkedAccountFilter.trim()) {
|
|
461
|
+
filterConditions.push({
|
|
462
|
+
Dimensions: { Key: 'LINKED_ACCOUNT', Values: [linkedAccountFilter.trim()] },
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
if (regionFilter === null || regionFilter === void 0 ? void 0 : regionFilter.trim()) {
|
|
466
|
+
filterConditions.push({
|
|
467
|
+
Dimensions: { Key: 'REGION', Values: [regionFilter.trim()] },
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
if (excludeCredits) {
|
|
471
|
+
filterConditions.push({
|
|
472
|
+
Not: {
|
|
473
|
+
Dimensions: {
|
|
474
|
+
Key: 'RECORD_TYPE',
|
|
475
|
+
Values: ['Credit', 'Refund', 'Discount'],
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
if (filterConditions.length === 1) {
|
|
481
|
+
params.Filter = filterConditions[0];
|
|
482
|
+
}
|
|
483
|
+
else if (filterConditions.length > 1) {
|
|
484
|
+
params.Filter = { And: filterConditions };
|
|
485
|
+
}
|
|
486
|
+
const response = await client.send(new GetCostAndUsageCommand(params));
|
|
487
|
+
returnData.push(response);
|
|
488
|
+
}
|
|
489
|
+
// ----------------------------------------------------------------
|
|
490
|
+
// Cost Forecast
|
|
491
|
+
// ----------------------------------------------------------------
|
|
492
|
+
if (resource === 'costForecast' && operation === 'get') {
|
|
493
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
494
|
+
const forecastMetric = this.getNodeParameter('forecastMetric', i);
|
|
495
|
+
const response = await client.send(new GetCostForecastCommand({
|
|
496
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
497
|
+
Granularity: granularity,
|
|
498
|
+
Metric: forecastMetric,
|
|
499
|
+
}));
|
|
500
|
+
returnData.push(response);
|
|
501
|
+
}
|
|
502
|
+
// ----------------------------------------------------------------
|
|
503
|
+
// Dimension Values
|
|
504
|
+
// ----------------------------------------------------------------
|
|
505
|
+
if (resource === 'dimensionValues' && operation === 'get') {
|
|
506
|
+
const dimension = this.getNodeParameter('dimension', i);
|
|
507
|
+
const response = await client.send(new GetDimensionValuesCommand({
|
|
508
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
509
|
+
Dimension: dimension,
|
|
510
|
+
}));
|
|
511
|
+
returnData.push(response);
|
|
512
|
+
}
|
|
513
|
+
// ----------------------------------------------------------------
|
|
514
|
+
// Reserved Instances — Utilization
|
|
515
|
+
// ----------------------------------------------------------------
|
|
516
|
+
if (resource === 'reservedInstances' && operation === 'getUtilization') {
|
|
517
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
518
|
+
const riGroupByService = this.getNodeParameter('riGroupByService', i, true);
|
|
519
|
+
const params = {
|
|
520
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
521
|
+
Granularity: granularity,
|
|
522
|
+
};
|
|
523
|
+
if (riGroupByService) {
|
|
524
|
+
params.GroupBy = [{ Type: 'DIMENSION', Key: 'SERVICE' }];
|
|
525
|
+
}
|
|
526
|
+
const response = await client.send(new GetReservationUtilizationCommand(params));
|
|
527
|
+
returnData.push(response);
|
|
528
|
+
}
|
|
529
|
+
// ----------------------------------------------------------------
|
|
530
|
+
// Reserved Instances — Coverage
|
|
531
|
+
// ----------------------------------------------------------------
|
|
532
|
+
if (resource === 'reservedInstances' && operation === 'getCoverage') {
|
|
533
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
534
|
+
const riGroupByService = this.getNodeParameter('riGroupByService', i, true);
|
|
535
|
+
const params = {
|
|
536
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
537
|
+
Granularity: granularity,
|
|
538
|
+
};
|
|
539
|
+
if (riGroupByService) {
|
|
540
|
+
params.GroupBy = [{ Type: 'DIMENSION', Key: 'SERVICE' }];
|
|
541
|
+
}
|
|
542
|
+
const response = await client.send(new GetReservationCoverageCommand(params));
|
|
543
|
+
returnData.push(response);
|
|
544
|
+
}
|
|
545
|
+
// ----------------------------------------------------------------
|
|
546
|
+
// Savings Plans — Utilization
|
|
547
|
+
// ----------------------------------------------------------------
|
|
548
|
+
if (resource === 'savingsPlans' && operation === 'getUtilization') {
|
|
549
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
550
|
+
const response = await client.send(new GetSavingsPlansUtilizationCommand({
|
|
551
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
552
|
+
Granularity: granularity,
|
|
553
|
+
}));
|
|
554
|
+
returnData.push(response);
|
|
555
|
+
}
|
|
556
|
+
// ----------------------------------------------------------------
|
|
557
|
+
// Savings Plans — Coverage
|
|
558
|
+
// ----------------------------------------------------------------
|
|
559
|
+
if (resource === 'savingsPlans' && operation === 'getCoverage') {
|
|
560
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
561
|
+
const response = await client.send(new GetSavingsPlansCoverageCommand({
|
|
562
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
563
|
+
Granularity: granularity,
|
|
564
|
+
}));
|
|
565
|
+
returnData.push(response);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
catch (error) {
|
|
569
|
+
if (this.continueOnFail()) {
|
|
570
|
+
returnData.push({ error: error.message });
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return [this.helpers.returnJsonArray(returnData)];
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
exports.AwsCostExplorer = AwsCostExplorer;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class AwsCostExplorer implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.AwsCostExplorer = void 0;
|
|
27
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
28
|
+
class AwsCostExplorer {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.description = {
|
|
31
|
+
displayName: 'AWS Cost Explorer',
|
|
32
|
+
name: 'awsCostExplorer',
|
|
33
|
+
icon: 'file:awscostexplorer.svg',
|
|
34
|
+
group: ['transform'],
|
|
35
|
+
version: 1,
|
|
36
|
+
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
|
37
|
+
description: 'Get cost and usage data from AWS Cost Explorer',
|
|
38
|
+
defaults: {
|
|
39
|
+
name: 'AWS Cost Explorer',
|
|
40
|
+
},
|
|
41
|
+
inputs: ['main'],
|
|
42
|
+
outputs: ['main'],
|
|
43
|
+
credentials: [
|
|
44
|
+
{
|
|
45
|
+
name: 'awsCostExplorerApi',
|
|
46
|
+
required: true,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
properties: [
|
|
50
|
+
// ----------------------------------------------------------------
|
|
51
|
+
// Resource
|
|
52
|
+
// ----------------------------------------------------------------
|
|
53
|
+
{
|
|
54
|
+
displayName: 'Resource',
|
|
55
|
+
name: 'resource',
|
|
56
|
+
type: 'options',
|
|
57
|
+
noDataExpression: true,
|
|
58
|
+
options: [
|
|
59
|
+
{ name: 'Cost and Usage', value: 'costAndUsage' },
|
|
60
|
+
{ name: 'Cost Forecast', value: 'costForecast' },
|
|
61
|
+
{ name: 'Dimension Values', value: 'dimensionValues' },
|
|
62
|
+
{ name: 'Reserved Instances', value: 'reservedInstances' },
|
|
63
|
+
{ name: 'Savings Plans', value: 'savingsPlans' },
|
|
64
|
+
],
|
|
65
|
+
default: 'costAndUsage',
|
|
66
|
+
},
|
|
67
|
+
// ----------------------------------------------------------------
|
|
68
|
+
// Operation — Cost and Usage
|
|
69
|
+
// ----------------------------------------------------------------
|
|
70
|
+
{
|
|
71
|
+
displayName: 'Operation',
|
|
72
|
+
name: 'operation',
|
|
73
|
+
type: 'options',
|
|
74
|
+
noDataExpression: true,
|
|
75
|
+
displayOptions: { show: { resource: ['costAndUsage'] } },
|
|
76
|
+
options: [
|
|
77
|
+
{ name: 'Get', value: 'get', description: 'Get cost and usage data', action: 'Get cost and usage data' },
|
|
78
|
+
],
|
|
79
|
+
default: 'get',
|
|
80
|
+
},
|
|
81
|
+
// ----------------------------------------------------------------
|
|
82
|
+
// Operation — Cost Forecast
|
|
83
|
+
// ----------------------------------------------------------------
|
|
84
|
+
{
|
|
85
|
+
displayName: 'Operation',
|
|
86
|
+
name: 'operation',
|
|
87
|
+
type: 'options',
|
|
88
|
+
noDataExpression: true,
|
|
89
|
+
displayOptions: { show: { resource: ['costForecast'] } },
|
|
90
|
+
options: [
|
|
91
|
+
{ name: 'Get', value: 'get', description: 'Get cost forecast', action: 'Get cost forecast' },
|
|
92
|
+
],
|
|
93
|
+
default: 'get',
|
|
94
|
+
},
|
|
95
|
+
// ----------------------------------------------------------------
|
|
96
|
+
// Operation — Dimension Values
|
|
97
|
+
// ----------------------------------------------------------------
|
|
98
|
+
{
|
|
99
|
+
displayName: 'Operation',
|
|
100
|
+
name: 'operation',
|
|
101
|
+
type: 'options',
|
|
102
|
+
noDataExpression: true,
|
|
103
|
+
displayOptions: { show: { resource: ['dimensionValues'] } },
|
|
104
|
+
options: [
|
|
105
|
+
{ name: 'Get', value: 'get', description: 'Get dimension values', action: 'Get dimension values' },
|
|
106
|
+
],
|
|
107
|
+
default: 'get',
|
|
108
|
+
},
|
|
109
|
+
// ----------------------------------------------------------------
|
|
110
|
+
// Operation — Reserved Instances
|
|
111
|
+
// ----------------------------------------------------------------
|
|
112
|
+
{
|
|
113
|
+
displayName: 'Operation',
|
|
114
|
+
name: 'operation',
|
|
115
|
+
type: 'options',
|
|
116
|
+
noDataExpression: true,
|
|
117
|
+
displayOptions: { show: { resource: ['reservedInstances'] } },
|
|
118
|
+
options: [
|
|
119
|
+
{ name: 'Get Utilization', value: 'getUtilization', description: 'Get RI utilization and unused hours', action: 'Get RI utilization' },
|
|
120
|
+
{ name: 'Get Coverage', value: 'getCoverage', description: 'Get percentage of usage covered by RIs', action: 'Get RI coverage' },
|
|
121
|
+
],
|
|
122
|
+
default: 'getUtilization',
|
|
123
|
+
},
|
|
124
|
+
// ----------------------------------------------------------------
|
|
125
|
+
// Operation — Savings Plans
|
|
126
|
+
// ----------------------------------------------------------------
|
|
127
|
+
{
|
|
128
|
+
displayName: 'Operation',
|
|
129
|
+
name: 'operation',
|
|
130
|
+
type: 'options',
|
|
131
|
+
noDataExpression: true,
|
|
132
|
+
displayOptions: { show: { resource: ['savingsPlans'] } },
|
|
133
|
+
options: [
|
|
134
|
+
{ name: 'Get Utilization', value: 'getUtilization', description: 'Get SP utilization and unused commitment', action: 'Get SP utilization' },
|
|
135
|
+
{ name: 'Get Coverage', value: 'getCoverage', description: 'Get percentage of usage covered by SPs', action: 'Get SP coverage' },
|
|
136
|
+
],
|
|
137
|
+
default: 'getUtilization',
|
|
138
|
+
},
|
|
139
|
+
// ----------------------------------------------------------------
|
|
140
|
+
// Shared: Start Date / End Date
|
|
141
|
+
// ----------------------------------------------------------------
|
|
142
|
+
{
|
|
143
|
+
displayName: 'Start Date',
|
|
144
|
+
name: 'startDate',
|
|
145
|
+
type: 'string',
|
|
146
|
+
displayOptions: {
|
|
147
|
+
show: {
|
|
148
|
+
resource: ['costAndUsage', 'costForecast', 'dimensionValues', 'reservedInstances', 'savingsPlans'],
|
|
149
|
+
operation: ['get', 'getUtilization', 'getCoverage'],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
default: '',
|
|
153
|
+
placeholder: '2024-01-01',
|
|
154
|
+
description: 'Start date in YYYY-MM-DD format',
|
|
155
|
+
required: true,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
displayName: 'End Date',
|
|
159
|
+
name: 'endDate',
|
|
160
|
+
type: 'string',
|
|
161
|
+
displayOptions: {
|
|
162
|
+
show: {
|
|
163
|
+
resource: ['costAndUsage', 'costForecast', 'dimensionValues', 'reservedInstances', 'savingsPlans'],
|
|
164
|
+
operation: ['get', 'getUtilization', 'getCoverage'],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
default: '',
|
|
168
|
+
placeholder: '2024-01-31',
|
|
169
|
+
description: 'End date in YYYY-MM-DD format (exclusive)',
|
|
170
|
+
required: true,
|
|
171
|
+
},
|
|
172
|
+
// ----------------------------------------------------------------
|
|
173
|
+
// Cost and Usage: Granularity
|
|
174
|
+
// ----------------------------------------------------------------
|
|
175
|
+
{
|
|
176
|
+
displayName: 'Granularity',
|
|
177
|
+
name: 'granularity',
|
|
178
|
+
type: 'options',
|
|
179
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
180
|
+
options: [
|
|
181
|
+
{ name: 'Daily', value: 'DAILY' },
|
|
182
|
+
{ name: 'Monthly', value: 'MONTHLY' },
|
|
183
|
+
{ name: 'Hourly', value: 'HOURLY' },
|
|
184
|
+
],
|
|
185
|
+
default: 'MONTHLY',
|
|
186
|
+
},
|
|
187
|
+
// ----------------------------------------------------------------
|
|
188
|
+
// Cost Forecast: Granularity
|
|
189
|
+
// ----------------------------------------------------------------
|
|
190
|
+
{
|
|
191
|
+
displayName: 'Granularity',
|
|
192
|
+
name: 'granularity',
|
|
193
|
+
type: 'options',
|
|
194
|
+
displayOptions: { show: { resource: ['costForecast'], operation: ['get'] } },
|
|
195
|
+
options: [
|
|
196
|
+
{ name: 'Daily', value: 'DAILY' },
|
|
197
|
+
{ name: 'Monthly', value: 'MONTHLY' },
|
|
198
|
+
],
|
|
199
|
+
default: 'MONTHLY',
|
|
200
|
+
},
|
|
201
|
+
// ----------------------------------------------------------------
|
|
202
|
+
// RI / SP: Granularity
|
|
203
|
+
// ----------------------------------------------------------------
|
|
204
|
+
{
|
|
205
|
+
displayName: 'Granularity',
|
|
206
|
+
name: 'granularity',
|
|
207
|
+
type: 'options',
|
|
208
|
+
displayOptions: {
|
|
209
|
+
show: {
|
|
210
|
+
resource: ['reservedInstances', 'savingsPlans'],
|
|
211
|
+
operation: ['getUtilization', 'getCoverage'],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
options: [
|
|
215
|
+
{ name: 'Daily', value: 'DAILY' },
|
|
216
|
+
{ name: 'Monthly', value: 'MONTHLY' },
|
|
217
|
+
],
|
|
218
|
+
default: 'MONTHLY',
|
|
219
|
+
},
|
|
220
|
+
// ----------------------------------------------------------------
|
|
221
|
+
// Cost and Usage: Metrics
|
|
222
|
+
// ----------------------------------------------------------------
|
|
223
|
+
{
|
|
224
|
+
displayName: 'Metrics',
|
|
225
|
+
name: 'metrics',
|
|
226
|
+
type: 'multiOptions',
|
|
227
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
228
|
+
options: [
|
|
229
|
+
{ name: 'Amortized Cost', value: 'AmortizedCost' },
|
|
230
|
+
{ name: 'Blended Cost', value: 'BlendedCost' },
|
|
231
|
+
{ name: 'Net Amortized Cost', value: 'NetAmortizedCost' },
|
|
232
|
+
{ name: 'Net Unblended Cost', value: 'NetUnblendedCost' },
|
|
233
|
+
{ name: 'Unblended Cost', value: 'UnblendedCost' },
|
|
234
|
+
{ name: 'Usage Quantity', value: 'UsageQuantity' },
|
|
235
|
+
{ name: 'Normalized Usage Amount', value: 'NormalizedUsageAmount' },
|
|
236
|
+
],
|
|
237
|
+
default: ['UnblendedCost'],
|
|
238
|
+
description: 'Which cost metrics to return. Use Unblended Cost for most cases.',
|
|
239
|
+
},
|
|
240
|
+
// ----------------------------------------------------------------
|
|
241
|
+
// Cost Forecast: Metric (single)
|
|
242
|
+
// ----------------------------------------------------------------
|
|
243
|
+
{
|
|
244
|
+
displayName: 'Metric',
|
|
245
|
+
name: 'forecastMetric',
|
|
246
|
+
type: 'options',
|
|
247
|
+
displayOptions: { show: { resource: ['costForecast'], operation: ['get'] } },
|
|
248
|
+
options: [
|
|
249
|
+
{ name: 'Amortized Cost', value: 'AMORTIZED_COST' },
|
|
250
|
+
{ name: 'Blended Cost', value: 'BLENDED_COST' },
|
|
251
|
+
{ name: 'Net Amortized Cost', value: 'NET_AMORTIZED_COST' },
|
|
252
|
+
{ name: 'Net Unblended Cost', value: 'NET_UNBLENDED_COST' },
|
|
253
|
+
{ name: 'Unblended Cost', value: 'UNBLENDED_COST' },
|
|
254
|
+
],
|
|
255
|
+
default: 'UNBLENDED_COST',
|
|
256
|
+
},
|
|
257
|
+
// ----------------------------------------------------------------
|
|
258
|
+
// Cost and Usage: Group By
|
|
259
|
+
// ----------------------------------------------------------------
|
|
260
|
+
{
|
|
261
|
+
displayName: 'Group By',
|
|
262
|
+
name: 'groupBy',
|
|
263
|
+
type: 'options',
|
|
264
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
265
|
+
options: [
|
|
266
|
+
{ name: 'None', value: 'none' },
|
|
267
|
+
{ name: 'Service', value: 'SERVICE' },
|
|
268
|
+
{ name: 'Linked Account', value: 'LINKED_ACCOUNT' },
|
|
269
|
+
{ name: 'Region', value: 'REGION' },
|
|
270
|
+
{ name: 'Purchase Type', value: 'PURCHASE_TYPE' },
|
|
271
|
+
{ name: 'Instance Type', value: 'INSTANCE_TYPE' },
|
|
272
|
+
{ name: 'Usage Type', value: 'USAGE_TYPE' },
|
|
273
|
+
{ name: 'Tag', value: 'TAG' },
|
|
274
|
+
],
|
|
275
|
+
default: 'none',
|
|
276
|
+
description: 'Dimension to group costs by',
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
displayName: 'Tag Key',
|
|
280
|
+
name: 'tagKey',
|
|
281
|
+
type: 'string',
|
|
282
|
+
displayOptions: {
|
|
283
|
+
show: {
|
|
284
|
+
resource: ['costAndUsage'],
|
|
285
|
+
operation: ['get'],
|
|
286
|
+
groupBy: ['TAG'],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
default: '',
|
|
290
|
+
placeholder: 'Environment',
|
|
291
|
+
description: 'Tag key to group costs by (required when Group By = Tag)',
|
|
292
|
+
required: true,
|
|
293
|
+
},
|
|
294
|
+
// ----------------------------------------------------------------
|
|
295
|
+
// Cost and Usage: Secondary Group By
|
|
296
|
+
// ----------------------------------------------------------------
|
|
297
|
+
{
|
|
298
|
+
displayName: 'Secondary Group By',
|
|
299
|
+
name: 'groupBySecondary',
|
|
300
|
+
type: 'options',
|
|
301
|
+
displayOptions: {
|
|
302
|
+
show: {
|
|
303
|
+
resource: ['costAndUsage'],
|
|
304
|
+
operation: ['get'],
|
|
305
|
+
},
|
|
306
|
+
hide: {
|
|
307
|
+
groupBy: ['none'],
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
options: [
|
|
311
|
+
{ name: 'None', value: 'none' },
|
|
312
|
+
{ name: 'Service', value: 'SERVICE' },
|
|
313
|
+
{ name: 'Linked Account', value: 'LINKED_ACCOUNT' },
|
|
314
|
+
{ name: 'Region', value: 'REGION' },
|
|
315
|
+
{ name: 'Purchase Type', value: 'PURCHASE_TYPE' },
|
|
316
|
+
],
|
|
317
|
+
default: 'none',
|
|
318
|
+
description: 'Add a second grouping dimension (max 2 GroupBy supported by AWS)',
|
|
319
|
+
},
|
|
320
|
+
// ----------------------------------------------------------------
|
|
321
|
+
// Cost and Usage: Filters
|
|
322
|
+
// ----------------------------------------------------------------
|
|
323
|
+
{
|
|
324
|
+
displayName: 'Filter by Service',
|
|
325
|
+
name: 'serviceFilter',
|
|
326
|
+
type: 'string',
|
|
327
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
328
|
+
default: '',
|
|
329
|
+
placeholder: 'Amazon EC2',
|
|
330
|
+
description: 'Filter by a specific AWS service name. Leave empty for all services.',
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
displayName: 'Filter by Linked Account',
|
|
334
|
+
name: 'linkedAccountFilter',
|
|
335
|
+
type: 'string',
|
|
336
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
337
|
+
default: '',
|
|
338
|
+
placeholder: '123456789012',
|
|
339
|
+
description: 'Filter by a specific linked account ID. Leave empty for all accounts.',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
displayName: 'Filter by Region',
|
|
343
|
+
name: 'regionFilter',
|
|
344
|
+
type: 'string',
|
|
345
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
346
|
+
default: '',
|
|
347
|
+
placeholder: 'us-east-1',
|
|
348
|
+
description: 'Filter by a specific AWS region. Leave empty for all regions.',
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
displayName: 'Exclude Credits',
|
|
352
|
+
name: 'excludeCredits',
|
|
353
|
+
type: 'boolean',
|
|
354
|
+
displayOptions: { show: { resource: ['costAndUsage'], operation: ['get'] } },
|
|
355
|
+
default: false,
|
|
356
|
+
description: 'Whether to exclude credits, refunds and discounts from the response',
|
|
357
|
+
},
|
|
358
|
+
// ----------------------------------------------------------------
|
|
359
|
+
// Dimension Values: Dimension
|
|
360
|
+
// ----------------------------------------------------------------
|
|
361
|
+
{
|
|
362
|
+
displayName: 'Dimension',
|
|
363
|
+
name: 'dimension',
|
|
364
|
+
type: 'options',
|
|
365
|
+
displayOptions: { show: { resource: ['dimensionValues'], operation: ['get'] } },
|
|
366
|
+
options: [
|
|
367
|
+
{ name: 'AZ', value: 'AZ' },
|
|
368
|
+
{ name: 'Instance Type', value: 'INSTANCE_TYPE' },
|
|
369
|
+
{ name: 'Legal Entity Name', value: 'LEGAL_ENTITY_NAME' },
|
|
370
|
+
{ name: 'Linked Account', value: 'LINKED_ACCOUNT' },
|
|
371
|
+
{ name: 'Operating System', value: 'OPERATING_SYSTEM' },
|
|
372
|
+
{ name: 'Operation', value: 'OPERATION' },
|
|
373
|
+
{ name: 'Platform', value: 'PLATFORM' },
|
|
374
|
+
{ name: 'Purchase Type', value: 'PURCHASE_TYPE' },
|
|
375
|
+
{ name: 'Region', value: 'REGION' },
|
|
376
|
+
{ name: 'Service', value: 'SERVICE' },
|
|
377
|
+
{ name: 'Usage Type', value: 'USAGE_TYPE' },
|
|
378
|
+
{ name: 'Usage Type Group', value: 'USAGE_TYPE_GROUP' },
|
|
379
|
+
],
|
|
380
|
+
default: 'SERVICE',
|
|
381
|
+
required: true,
|
|
382
|
+
},
|
|
383
|
+
// ----------------------------------------------------------------
|
|
384
|
+
// RI: Group By Service
|
|
385
|
+
// ----------------------------------------------------------------
|
|
386
|
+
{
|
|
387
|
+
displayName: 'Group By Service',
|
|
388
|
+
name: 'riGroupByService',
|
|
389
|
+
type: 'boolean',
|
|
390
|
+
displayOptions: {
|
|
391
|
+
show: {
|
|
392
|
+
resource: ['reservedInstances'],
|
|
393
|
+
operation: ['getUtilization', 'getCoverage'],
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
default: true,
|
|
397
|
+
description: 'Whether to break down RI utilization/coverage by AWS service',
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
async execute() {
|
|
403
|
+
const items = this.getInputData();
|
|
404
|
+
const returnData = [];
|
|
405
|
+
const credentials = await this.getCredentials('awsCostExplorerApi');
|
|
406
|
+
const { CostExplorerClient, GetCostAndUsageCommand, GetCostForecastCommand, GetDimensionValuesCommand, GetReservationUtilizationCommand, GetReservationCoverageCommand, GetSavingsPlansUtilizationCommand, GetSavingsPlansCoverageCommand, } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-cost-explorer')));
|
|
407
|
+
const client = new CostExplorerClient({
|
|
408
|
+
region: credentials.region || 'us-east-1',
|
|
409
|
+
credentials: {
|
|
410
|
+
accessKeyId: credentials.accessKeyId,
|
|
411
|
+
secretAccessKey: credentials.secretAccessKey,
|
|
412
|
+
...(credentials.sessionToken ? { sessionToken: credentials.sessionToken } : {}),
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
for (let i = 0; i < items.length; i++) {
|
|
416
|
+
try {
|
|
417
|
+
const resource = this.getNodeParameter('resource', i);
|
|
418
|
+
const operation = this.getNodeParameter('operation', i);
|
|
419
|
+
const startDate = this.getNodeParameter('startDate', i, '');
|
|
420
|
+
const endDate = this.getNodeParameter('endDate', i, '');
|
|
421
|
+
// ----------------------------------------------------------------
|
|
422
|
+
// Cost and Usage
|
|
423
|
+
// ----------------------------------------------------------------
|
|
424
|
+
if (resource === 'costAndUsage' && operation === 'get') {
|
|
425
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
426
|
+
const metrics = this.getNodeParameter('metrics', i);
|
|
427
|
+
const groupBy = this.getNodeParameter('groupBy', i);
|
|
428
|
+
const groupBySecondary = this.getNodeParameter('groupBySecondary', i, 'none');
|
|
429
|
+
const tagKey = this.getNodeParameter('tagKey', i, '');
|
|
430
|
+
const serviceFilter = this.getNodeParameter('serviceFilter', i, '');
|
|
431
|
+
const linkedAccountFilter = this.getNodeParameter('linkedAccountFilter', i, '');
|
|
432
|
+
const regionFilter = this.getNodeParameter('regionFilter', i, '');
|
|
433
|
+
const excludeCredits = this.getNodeParameter('excludeCredits', i, false);
|
|
434
|
+
const params = {
|
|
435
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
436
|
+
Granularity: granularity,
|
|
437
|
+
Metrics: metrics,
|
|
438
|
+
};
|
|
439
|
+
// Group By
|
|
440
|
+
const groupByList = [];
|
|
441
|
+
if (groupBy !== 'none') {
|
|
442
|
+
groupByList.push({
|
|
443
|
+
Type: groupBy === 'TAG' ? 'TAG' : 'DIMENSION',
|
|
444
|
+
Key: groupBy === 'TAG' ? tagKey : groupBy,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (groupBySecondary !== 'none') {
|
|
448
|
+
groupByList.push({ Type: 'DIMENSION', Key: groupBySecondary });
|
|
449
|
+
}
|
|
450
|
+
if (groupByList.length > 0) {
|
|
451
|
+
params.GroupBy = groupByList;
|
|
452
|
+
}
|
|
453
|
+
// Filters — AND together when multiple are set
|
|
454
|
+
const filterConditions = [];
|
|
455
|
+
if (serviceFilter === null || serviceFilter === void 0 ? void 0 : serviceFilter.trim()) {
|
|
456
|
+
filterConditions.push({
|
|
457
|
+
Dimensions: { Key: 'SERVICE', Values: [serviceFilter.trim()] },
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
if (linkedAccountFilter === null || linkedAccountFilter === void 0 ? void 0 : linkedAccountFilter.trim()) {
|
|
461
|
+
filterConditions.push({
|
|
462
|
+
Dimensions: { Key: 'LINKED_ACCOUNT', Values: [linkedAccountFilter.trim()] },
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
if (regionFilter === null || regionFilter === void 0 ? void 0 : regionFilter.trim()) {
|
|
466
|
+
filterConditions.push({
|
|
467
|
+
Dimensions: { Key: 'REGION', Values: [regionFilter.trim()] },
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
if (excludeCredits) {
|
|
471
|
+
filterConditions.push({
|
|
472
|
+
Not: {
|
|
473
|
+
Dimensions: {
|
|
474
|
+
Key: 'RECORD_TYPE',
|
|
475
|
+
Values: ['Credit', 'Refund', 'Discount'],
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
if (filterConditions.length === 1) {
|
|
481
|
+
params.Filter = filterConditions[0];
|
|
482
|
+
}
|
|
483
|
+
else if (filterConditions.length > 1) {
|
|
484
|
+
params.Filter = { And: filterConditions };
|
|
485
|
+
}
|
|
486
|
+
const response = await client.send(new GetCostAndUsageCommand(params));
|
|
487
|
+
returnData.push(response);
|
|
488
|
+
}
|
|
489
|
+
// ----------------------------------------------------------------
|
|
490
|
+
// Cost Forecast
|
|
491
|
+
// ----------------------------------------------------------------
|
|
492
|
+
if (resource === 'costForecast' && operation === 'get') {
|
|
493
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
494
|
+
const forecastMetric = this.getNodeParameter('forecastMetric', i);
|
|
495
|
+
const response = await client.send(new GetCostForecastCommand({
|
|
496
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
497
|
+
Granularity: granularity,
|
|
498
|
+
Metric: forecastMetric,
|
|
499
|
+
}));
|
|
500
|
+
returnData.push(response);
|
|
501
|
+
}
|
|
502
|
+
// ----------------------------------------------------------------
|
|
503
|
+
// Dimension Values
|
|
504
|
+
// ----------------------------------------------------------------
|
|
505
|
+
if (resource === 'dimensionValues' && operation === 'get') {
|
|
506
|
+
const dimension = this.getNodeParameter('dimension', i);
|
|
507
|
+
const response = await client.send(new GetDimensionValuesCommand({
|
|
508
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
509
|
+
Dimension: dimension,
|
|
510
|
+
}));
|
|
511
|
+
returnData.push(response);
|
|
512
|
+
}
|
|
513
|
+
// ----------------------------------------------------------------
|
|
514
|
+
// Reserved Instances — Utilization
|
|
515
|
+
// ----------------------------------------------------------------
|
|
516
|
+
if (resource === 'reservedInstances' && operation === 'getUtilization') {
|
|
517
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
518
|
+
const riGroupByService = this.getNodeParameter('riGroupByService', i, true);
|
|
519
|
+
const params = {
|
|
520
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
521
|
+
Granularity: granularity,
|
|
522
|
+
};
|
|
523
|
+
if (riGroupByService) {
|
|
524
|
+
params.GroupBy = [{ Type: 'DIMENSION', Key: 'SERVICE' }];
|
|
525
|
+
}
|
|
526
|
+
const response = await client.send(new GetReservationUtilizationCommand(params));
|
|
527
|
+
returnData.push(response);
|
|
528
|
+
}
|
|
529
|
+
// ----------------------------------------------------------------
|
|
530
|
+
// Reserved Instances — Coverage
|
|
531
|
+
// ----------------------------------------------------------------
|
|
532
|
+
if (resource === 'reservedInstances' && operation === 'getCoverage') {
|
|
533
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
534
|
+
const riGroupByService = this.getNodeParameter('riGroupByService', i, true);
|
|
535
|
+
const params = {
|
|
536
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
537
|
+
Granularity: granularity,
|
|
538
|
+
};
|
|
539
|
+
if (riGroupByService) {
|
|
540
|
+
params.GroupBy = [{ Type: 'DIMENSION', Key: 'SERVICE' }];
|
|
541
|
+
}
|
|
542
|
+
const response = await client.send(new GetReservationCoverageCommand(params));
|
|
543
|
+
returnData.push(response);
|
|
544
|
+
}
|
|
545
|
+
// ----------------------------------------------------------------
|
|
546
|
+
// Savings Plans — Utilization
|
|
547
|
+
// ----------------------------------------------------------------
|
|
548
|
+
if (resource === 'savingsPlans' && operation === 'getUtilization') {
|
|
549
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
550
|
+
const response = await client.send(new GetSavingsPlansUtilizationCommand({
|
|
551
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
552
|
+
Granularity: granularity,
|
|
553
|
+
}));
|
|
554
|
+
returnData.push(response);
|
|
555
|
+
}
|
|
556
|
+
// ----------------------------------------------------------------
|
|
557
|
+
// Savings Plans — Coverage
|
|
558
|
+
// ----------------------------------------------------------------
|
|
559
|
+
if (resource === 'savingsPlans' && operation === 'getCoverage') {
|
|
560
|
+
const granularity = this.getNodeParameter('granularity', i);
|
|
561
|
+
const response = await client.send(new GetSavingsPlansCoverageCommand({
|
|
562
|
+
TimePeriod: { Start: startDate, End: endDate },
|
|
563
|
+
Granularity: granularity,
|
|
564
|
+
}));
|
|
565
|
+
returnData.push(response);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
catch (error) {
|
|
569
|
+
if (this.continueOnFail()) {
|
|
570
|
+
returnData.push({ error: error.message });
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return [this.helpers.returnJsonArray(returnData)];
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
exports.AwsCostExplorer = AwsCostExplorer;
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@roberta.soliman/n8n-nodes-aws-cost-explorer",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "N8N node for AWS Cost Explorer to retrieve cost and usage data",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"aws",
|
|
8
|
+
"cost-explorer",
|
|
9
|
+
"billing",
|
|
10
|
+
"cost-management"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/robertasolimandonofreo/n8n-aws-cost-explorer",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "roberta.soliman"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/robertasolimandonofreo/n8n-aws-cost-explorer.git"
|
|
23
|
+
},
|
|
24
|
+
"main": "index.js",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc --noEmit false --skipLibCheck && gulp build:icons",
|
|
27
|
+
"dev": "tsc --watch",
|
|
28
|
+
"format": "prettier nodes credentials --write",
|
|
29
|
+
"lint": "eslint nodes credentials package.json",
|
|
30
|
+
"lintfix": "eslint nodes credentials package.json --fix",
|
|
31
|
+
"prepublishOnly": "npm run build"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"n8n": {
|
|
37
|
+
"n8nNodesApiVersion": 1,
|
|
38
|
+
"credentials": [
|
|
39
|
+
"dist/credentials/AwsCostExplorerApi.credentials.js"
|
|
40
|
+
],
|
|
41
|
+
"nodes": [
|
|
42
|
+
"dist/nodes/AwsCostExplorer/AwsCostExplorer.node.js"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^18.16.16",
|
|
47
|
+
"gulp": "^4.0.2",
|
|
48
|
+
"n8n-workflow": "*",
|
|
49
|
+
"prettier": "^2.7.1",
|
|
50
|
+
"typescript": "^4.8.4"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@aws-sdk/client-cost-explorer": "^3.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|