Links

Daily biling notification to slack

Tags: #aws #cloud #storage #S3bucket #slack #operations #automation
Author: Maxime Jublou​

Input

Install packages

!pip install boto3

Setup secret keys

Uncomment to setup.
#naas.secret.add(name="AWS_ACCESS_KEY_ID", secret="***")
#naas.secret.add(name="AWS_SECRET_ACCESS_KEY", secret="***")
#naas.secret.add(name="SLACK_TOKEN", secret="***")

Import libraries

import datetime
import boto3
import naas
import dateutil.relativedelta
import pandas as pd
import naas_drivers

Setup variables

# AWS account
AWS_ACCESS_KEY_ID = naas.secret.get(name="AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = naas.secret.get(name="AWS_SECRET_ACCESS_KEY")
​
# Slack
SLACK_TOKEN = naas.secret.get(name="SLACK_TOKEN")
SLACK_CHANNEL = "-aws-billing"

Constants

# Compute dates
def last_day_of_month(any_day):
# this will never fail
# get close to the end of the month for any day, and add 4 days 'over'
next_month = any_day.replace(day=28) + datetime.timedelta(days=4)
# subtract the number of remaining 'overage' days to get last day of current month, or said programattically said, the previous day of the first of next month
return next_month - datetime.timedelta(days=next_month.day)
​
today = datetime.date.today()
lastDay = last_day_of_month(today)
start_month_date = (today - dateutil.relativedelta.relativedelta(months=12))
​
start_date = "{}-{:02d}-{:02d}".format(today.year, today.month, 1)
end_date = "{}-{:02d}-{:02d}".format(today.year, today.month, today.day)
last_day = "{}-{:02d}-{:02d}".format(lastDay.year, lastDay.month, lastDay.day)

Connect to AWS

client = boto3.client('ce',
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY)

Model

Get current cost from AWS billing

result = client.get_cost_and_usage(
TimePeriod = {
'Start': start_date,
'End': end_date
},
Granularity = 'MONTHLY',
Filter = {
"Dimensions": {
"Key": "RECORD_TYPE",
"Values": ["Credit", "Refund"]
}
},
Metrics = ["BlendedCost"],
GroupBy = [
{
'Type': 'DIMENSION',
'Key': 'SERVICE'
},
{
'Type': 'DIMENSION',
'Key': 'USAGE_TYPE'
}
]
)

Transform current billing to dataframe

df_billing = pd.DataFrame()
​
for t in result["ResultsByTime"]:
for r in t["Groups"]:
dimension = r["Keys"][0]
usage_type = r["Keys"][1]
amount = r["Metrics"]["BlendedCost"]["Amount"]
period_start = t["TimePeriod"]["Start"]
period_end = t["TimePeriod"]["End"]
df_billing = df_billing.append({
"Dimension": dimension,
"UsageType": usage_type,
"Amount": amount,
"PeriodStart": period_start,
"PeriodEnd": period_end
}, ignore_index=True)
df_billing = df_billing.astype({'Amount': 'float'})
​
# Display result
df_billing.tail(5)

Get forecast from AWS

ce_forecast = client.get_cost_forecast(
TimePeriod={
'Start': end_date,
'End': last_day
},
Metric='BLENDED_COST',
Granularity='MONTHLY'
)

Output

Save current billing to csv

df_billing.to_csv('current_month_data.csv')
naas.asset.add(path='current_month_data.csv')

Data from AWS billing

current_amount = df_billing["Amount"].sum()
forecast = float(ce_forecast["Total"]["Amount"])
asset_link = "Copy URL from generate asset above"

Create message for slack

message = """
Hey there,
​
This is your daily AWS billing notification.
​
- Current spending: [*{:.2f}$*]
- Forecast left to spent: [*{:.2f}$*]
- End of month estimate: [*{:.2f}$*]
​
Download the detailed csv file {}
""".format(float(current_amount), float(forecast), float(current_amount - forecast), asset_link)
image_url = None # Set to None if you don't need it
print(message)

Send data to slack

naas_drivers.slack.connect(SLACK_TOKEN).send(SLACK_CHANNEL, message)

Scheduler

naas.scheduler.add(cron="0 9 * * *")