@ddegtyarev/aws-tools
Version:
This project contains AWS API integration tools for use in Vertex AI SDK.
135 lines (134 loc) • 5.81 kB
JavaScript
import { CostExplorerClient, GetCostAndUsageCommand } from '@aws-sdk/client-cost-explorer';
import { calculateDateRange, generateSimpleCostSummary } from '../utils/costUtils.js';
import { validateParameters } from '../utils/validation.js';
export const awsCostPerServicePerRegion = {
name: 'awsCostPerServicePerRegion',
description: 'Retrieve simplified AWS cost data grouped by service and region. Use this for basic cost analysis without complex filtering.',
inputSchema: {
type: 'object',
properties: {
lookBack: { type: 'number', description: 'Number of days (DAILY) or months (MONTHLY) to look back. Default: 30 for DAILY, 6 for MONTHLY' },
granularity: { type: 'string', enum: ['DAILY', 'MONTHLY'], description: 'Data granularity' },
},
required: ['granularity'],
},
outputSchema: {
type: 'object',
properties: {
summary: { type: 'string', description: 'Text summary of the cost data by service' },
datapoints: {
type: 'array',
items: {
type: 'object',
properties: {
date: { type: 'string' },
dimensions: { type: 'object' },
},
},
},
},
},
configSchema: {
type: 'object',
properties: {
credentials: {
type: 'object',
properties: {
accessKeyId: { type: 'string' },
secretAccessKey: { type: 'string' },
sessionToken: { type: 'string' },
},
required: ['accessKeyId', 'secretAccessKey'],
},
region: { type: 'string', description: 'AWS region' },
logger: { type: 'object' },
},
required: ['credentials', 'region'],
},
defaultConfig: {},
async invoke(input, config) {
const { logger } = config;
// Validate input and config against schemas
validateParameters(input, this.inputSchema, config, this.configSchema, logger);
const { lookBack, granularity } = input;
const { region } = config;
// Set default lookBack values
const defaultLookBack = granularity === 'DAILY' ? 30 : 6;
const actualLookBack = lookBack || defaultLookBack;
// Calculate date range based on lookBack and granularity
const { startDate, endDate } = calculateDateRange(actualLookBack, granularity);
logger?.debug('awsCostPerServicePerRegion input:', { ...input, calculatedStartDate: startDate, calculatedEndDate: endDate });
const costExplorerClient = new CostExplorerClient({ credentials: config.credentials });
// Simple filter to exclude credits and taxes
const record_type_filter = {
Not: {
Dimensions: {
Key: 'RECORD_TYPE',
Values: ['Credit', 'Tax', 'Enterprise Discount Program Discount']
}
}
};
const params = {
TimePeriod: {
Start: startDate,
End: endDate,
},
Granularity: granularity,
GroupBy: [
{ Type: 'DIMENSION', Key: 'SERVICE' },
{ Type: 'DIMENSION', Key: 'REGION' }
],
Metrics: ['AmortizedCost'],
Filter: record_type_filter
};
try {
let allResults = [];
let nextToken;
do {
const commandWithToken = new GetCostAndUsageCommand({
...params,
NextPageToken: nextToken
});
const data = await costExplorerClient.send(commandWithToken);
nextToken = data.NextPageToken;
// Process current page results
const pageResults = data.ResultsByTime?.map(result => {
const dimensions = {};
// Process grouped results
result.Groups?.forEach(group => {
if (group.Keys && group.Keys.length > 0 && group.Metrics) {
const service = group.Keys[0];
const region = group.Keys[1];
const metric = group.Metrics['AmortizedCost'];
if (service && region && metric && metric.Amount) {
const cost = parseFloat(metric.Amount);
// Filter out costs less than $0.01
if (cost >= 0.01) {
const key = `${service} (${region})`;
dimensions[key] = metric.Amount;
}
}
}
});
return {
date: result.TimePeriod?.Start,
dimensions,
};
}) || [];
allResults = allResults.concat(pageResults);
logger?.debug(`Fetched page with ${pageResults.length} results, nextToken: ${nextToken}`);
} while (nextToken);
const summary = generateSimpleCostSummary(allResults, granularity);
const output = {
summary,
datapoints: allResults,
};
logger?.debug(`awsCostPerServicePerRegion output:\n${output.summary}\n`, output.datapoints);
return output;
}
catch (error) {
logger?.error('Error getting cost per service per region:', error);
throw error;
}
},
};