Want to know what your daily spend is on AWS but don’t want to have to log into the console every day?
Aws costs to slack is what you need!

This project is built from the simple Lambda Scheduler and the slack webhook integration.
First up we need to create the webhook.
I first create a new channel in my slack workspace. Then following the slack instructions on creating a webhook https://api.slack.com/messaging/webhooks

On following the slack instructions for creating a webhook, you will end up with a webhook URL. You can also test the webhook with the sample curl request.
Now in the CDK project we are going to first create the infrastructure. This will first contain a Lambda function and passed into this will be the webhook.
const SLACK_CHANNEL = "https://hooks.slack.com/services/###########/###########/########################"; // Modify this with your webhook or use a .env file
// Function called by schedular check the billing and send a message to slack
const triggeredEventFn = new lambdaNode.NodejsFunction(this, 'SlackBudgetTriggerEventHandler', {
runtime: lambda.Runtime.NODEJS_14_X,
entry: 'lambda/writeBudgetToSlack.ts',
handler: 'main',
timeout: Duration.seconds(3),
logRetention: 14,
bundling: {
externalModules: [
],
nodeModules: [
'got',
'moment'
],
minify: true
},
environment: {
"SLACK_CHANNEL": SLACK_CHANNEL,
region: process.env.CDK_DEFAULT_REGION!
}
});
This lambda needs permissions to get the cost and usage for your account. This is added with an inline policy.
// Add permissions for get-cost-and-usage
const changeCronRatePolicy = new iam.PolicyStatement({
actions: [
'ce:Get*',
'ce:Describe*',
],
resources: ['*'],
});
triggeredEventFn.role?.attachInlinePolicy(new iam.Policy(this, 'Cost-And-Usage-Policy', {
statements:[changeCronRatePolicy]
}));
Finally, we need to hook up the lambda to an event bridge schedular. This is set to run every 1 day.
// Schedular from event bridge
const eventBridgeSchedular = new events.Rule(this, 'SlackBudgetEventBridgeSchedular', {
ruleName: 'slackBudgetEventBridgeSchedular',
schedule: events.Schedule.rate(Duration.days(1))
});
// Add the lambda as a target of the schedular event
eventBridgeSchedular.addTarget(new targets.LambdaFunction(triggeredEventFn));
The lambda code is fairly simple and only needs to do 2 tasks, get the daily cost usage and send a message to slack.
I built a very small class to start with to send the message to slack, it uses got to make a POST request.
import got from 'got';
export class SlackClass {
async SendToSlack(msg: string) {
// Post request to get the token
try {
const bodyVal = {
text: msg
};
const options = {
body: JSON.stringify(bodyVal)
};
const send = await got.post(process.env.SLACK_CHANNEL!, options);
return send;
} catch (error) {
console.log('error', error);
return 'error'
}
}
}
The lambda function then uses this class to send the message to slack.
import { Context, Callback } from 'aws-lambda';
import { CostExplorer } from 'aws-sdk';
import { SlackClass } from './SlackClass';
const moment = require('moment');
export async function main(event: any, _context: Context, callback: Callback) {
var date = new Date();
var StartDate = moment(date).add(-1, 'd').format('YYYY-MM-DD');
var EndDate = moment(date).format('YYYY-MM-DD');
var StartDateReadable = moment(date).add(-1, 'd').format('MMMM Do, YYYY');
const costs = new CostExplorer({region: 'us-east-1'});
const params: CostExplorer.Types.GetCostAndUsageRequest = {
Granularity: 'DAILY',
TimePeriod: {
End: EndDate,
Start: StartDate
},
Metrics: ['UNBLENDED_COST'],
};
const usage = await costs.getCostAndUsage(params).promise();
console.log('usage',usage);
const data: any = usage.$response.data;
// const UnblendedCost = data.ResultsByTime[0].Total.UnblendedCost;
const slack = new SlackClass();
const send = await slack.SendToSlack(`Daily Spend: $ ${data.ResultsByTime[0].Total.UnblendedCost.Amount}`);
callback(null);
}

Checkout the Git Repo for the working project.
Categories: AWS
Leave a Reply