Weekly Billing reports in AWS

1k Views Asked by At

I am looking to configure a weekly billing report in AWS. It should contain the basic information like for last 7 days, $x is charged for the account. If is shows the services also, it will be good, but main focus is on the charged amount. The weekly billed amount should be sent over mail or SMS to inform users about the charges. Eg: $10 has been charged to your AWS acc id xyz in last week.

Is there any inbuilt AWS service which can be used or we need to write a custom script? For now I have created a daily budget, using which I have set budget report to be sent every week. But that is not sufficient. Let me know if there is more sophisticated way present.

Thanks

2

There are 2 best solutions below

7
On

You could check out the AWS Cost Explorer and build your own, custom report, that might look like this:

enter image description here

Go to your AWS Account -> Billing -> Cost Explorer -> Launch Cost Explorer.

NOTE: If you're running it for the first time, you need to enable it and wait approx. 24 hours for the initial data.

enter image description here


As of now, there's no out-of-the-box solution from AWS that sends the report as an email, but anyone interested might want to explore this repository - AWS Cost Explorer Report.

0
On

To get a detailed billing report in AWS, you can use AWS lambda that integrates Cost explorer api and SES.

In this you can get a daily as well as weekly report (showing how much does each service cost) in which you can monitor the spikes in your billing. The architecture diagram for this solution is provided below: Architecture diagram

First add and verify the email in SES to send or get the alert emails. You can add the following Lambda code with Python runtime which is get triggered by an eventbridge scheduled cron expression of your desire. Here is the code:

import json
import boto3
import datetime
from botocore.exceptions import ClientError
import pandas as pd
 
def lambda_handler(event, context):
     
    billing_client = boto3.client('ce')
    # getting dates (yyyy-MM-dd) and converting to string 
    today = datetime.date.today()
    yesterday = today - datetime.timedelta(days = 1) 
    str_today = str(today) 
    str_yesterday = str(yesterday)
     
    # get total cost for the previous day
    response_total = billing_client.get_cost_and_usage( 
       TimePeriod={ 
         'Start': str_esterday,
         'End': str_today,
         #'Start': "2023-05-16",
        # 'End': "2023-05-17"
         },
       Granularity='DAILY', 
       Metrics=[ 'UnblendedCost',] 
    )
     
    total_cost = response_total["ResultsByTime"][0]['Total']['UnblendedCost']['Amount']
    print(total_cost)
    total_cost=float(total_cost)
    total_cost=round(total_cost, 3)
    total_cost = '$' + str(total_cost)
     
    # print the total cost
    print('Total cost for yesterday: ' + total_cost)
     
    # get detailed billing for individual resources
    response_detail = billing_client.get_cost_and_usage(
        TimePeriod={
           'Start': str_yesterday,
           'End': str_today,
        },
        Granularity='DAILY',
        Metrics=['UnblendedCost'],
        GroupBy=[
            {
                'Type': 'DIMENSION',
                'Key': 'SERVICE'
            },
            {
                'Type': 'DIMENSION',
                'Key': 'USAGE_TYPE'
            }
        ]
    )
 
    resources = {'Service':[],'Usage Type':[],'Cost':[]}
     
    for result in response_detail['ResultsByTime'][0]['Groups']:
        group_key = result['Keys']
        service = group_key[0]
        usage_type = group_key[1]
        cost = result['Metrics']['UnblendedCost']['Amount']
        cost=float(cost)
        cost=round(cost, 3)
         
        if cost > 0:
            cost = '$' + str(cost)
            resources['Service'].append(service)
            resources['Usage Type'].append(usage_type)
            resources['Cost'].append(cost)
             
    df = pd.DataFrame(resources)
    html_table = df.to_html(index=False)
             
    print(resources)        
     
    message = 'Cost of AWS training account for yesterday was' 
     
    html = """
            <html>
              <head>
                <style>
                  body {{
                    font-family: Arial, sans-serif;
                    color: white;
                    background-color: black;
                  }}
                  h2 {{
                    color: white;
                    font-size: 25px;
                    text-align: center;
                  }}
                  h1 {{
                    color: #333333;
                    font-size: 40px;
                    text-align: center;
                    background-color: yellow;
                  }}
                  p {{
                    color: white;
                    font-size: 30px;
                    line-height: 1.5;
                    margin-bottom: 20px;
                    text-align: center;
                  }}
                  p1 {{
                     font-size: 10px;
                     text-align: center;
                      margin-left: auto;
                     margin-right: auto;
                  }}
                </style>
              </head>
              <body>
                <p> Training Account report for the day {} </p>
                <h2> {} </h2>
                <h1> <strong> <em> {} </em></strong> </h1>
                <p1>{}</p1>
              </body>
            </html>
            """.format(str_yesterday,message,total_cost,html_table)
             
 
     
    ses_client = boto3.client('ses', region_name='us-east-1')
     
    message = {
        'Subject': {'Data': 'AWS training account cost report'},
        'Body': {'Html': {'Data': html}}
    }
     
    
    response = ses_client.send_email(
        Source="<SES_verified_identity>",
        Destination={'ToAddresses': [ "email-1","email-2"]},
        Message=message
    )
     
    if today.weekday() == 4:
        print('week')
        week = today - datetime.timedelta(days = 7) 
        str_week = str(week)
         
        response_total = billing_client.get_cost_and_usage( 
           TimePeriod={ 
             'Start': str_week, 
             'End': str_today }, 
           Granularity='MONTHLY', 
           Metrics=[ 'UnblendedCost',] 
        )
         
        print(response_total)
        length=len(response_total["ResultsByTime"])
        print(length)
         
        if (length==2):
            total_cost_1 = response_total["ResultsByTime"][0]['Total']['UnblendedCost']['Amount']
            total_cost_2 = response_total["ResultsByTime"][1]['Total']['UnblendedCost']['Amount']
            total_cost_1=float(total_cost_1)
            total_cost_2=float(total_cost_2)
            total_cost = total_cost_1+total_cost_2
            total_cost=round(total_cost, 3)
            total_cost = '$' + str(total_cost)
             
            # print the total cost
            print('Total cost for the week: ' + total_cost)
             
            # get detailed billing for individual resources
            response_detail = billing_client.get_cost_and_usage(
                TimePeriod={
                    'Start': str_week,
                    'End': str_today
                },
                Granularity='MONTHLY',
                Metrics=['UnblendedCost'],
                GroupBy=[
                    {
                        'Type': 'DIMENSION',
                        'Key': 'SERVICE'
                    },
                    {
                        'Type': 'DIMENSION',
                        'Key': 'USAGE_TYPE'
                    }
                ]
            )
             
            resources = {'Service':[],'Usage Type':[],'Cost':[]}
            resources_1 = {'Service':[],'Usage Type':[],'Cost':[]}
             
            for result in response_detail['ResultsByTime'][0]['Groups']:
                group_key = result['Keys']
                service = group_key[0]
                usage_type = group_key[1]
                cost = result['Metrics']['UnblendedCost']['Amount']
                cost=float(cost)
                cost=round(cost, 3)
                 
                if cost > 0:
                    cost = '$' + str(cost)
                    resources['Service'].append(service)
                    resources['Usage Type'].append(usage_type)
                    resources['Cost'].append(cost)
                     
            for result in response_detail['ResultsByTime'][1]['Groups']:
                group_key = result['Keys']
                service = group_key[0]
                usage_type = group_key[1]
                cost = result['Metrics']['UnblendedCost']['Amount']
                cost=float(cost)
                cost=round(cost, 3)
 
                if cost > 0:
                    cost = '$' + str(cost)
                    resources_1['Service'].append(service)
                    resources_1['Usage Type'].append(usage_type)
                    resources_1['Cost'].append(cost)
                     
            for key, value in resources_1.items():
                if key in resources:
                    resources[key] += value
                else:
                    resources[key] = value
        else:
            total_cost = response_total["ResultsByTime"][0]['Total']['UnblendedCost']['Amount']
            total_cost=float(total_cost)
            total_cost=round(total_cost, 3)
            total_cost = '$' + str(total_cost)
             
            # print the total cost
            print('Total cost for the week: ' + total_cost)
             
            # get detailed billing for individual resources
            response_detail = billing_client.get_cost_and_usage(
                TimePeriod={
                    'Start': str_week,
                    'End': str_today
                },
                Granularity='MONTHLY',
                Metrics=['UnblendedCost'],
                GroupBy=[
                    {
                        'Type': 'DIMENSION',
                        'Key': 'SERVICE'
                    },
                    {
                        'Type': 'DIMENSION',
                        'Key': 'USAGE_TYPE'
                    }
                ]
            )
             
            resources = {'Service':[],'Usage Type':[],'Cost':[]}
             
            for result in response_detail['ResultsByTime'][0]['Groups']:
                group_key = result['Keys']
                service = group_key[0]
                usage_type = group_key[1]
                cost = result['Metrics']['UnblendedCost']['Amount']
                cost=float(cost)
                cost=round(cost, 3)
                 
                if cost > 0:
                    cost = '$' + str(cost)
                    resources['Service'].append(service)
                    resources['Usage Type'].append(usage_type)
                    resources['Cost'].append(cost)
                     
        print(type(resources))
                 
        df = pd.DataFrame(resources)
        html_table = df.to_html(index=False)
                 
        print(resources)        
         
        message = 'Cost of AWS training account for the  was' 
         
        html = """
                <html>
                  <head>
                    <style>
                      body {{
                        font-family: Arial, sans-serif;
                        color: white;
                        background-color: black;
                      }}
                      h2 {{
                        color: white;
                        font-size: 25px;
                        text-align: center;
                      }}
                      h1 {{
                        color: #333333;
                        font-size: 40px;
                        text-align: center;
                        background-color: yellow;
                      }}
                      p {{
                        color: white;
                        font-size: 30px;
                        line-height: 1.5;
                        margin-bottom: 20px;
                        text-align: center;
                      }}
                      p1 {{
                         font-size: 10px;
                         text-align: center;
                          margin-left: auto;
                         margin-right: auto;
                      }}
                    </style>
                  </head>
                  <body>
                    <p> Training Account report for the week {} and {} </p>
                    <h2> {} </h2>
                    <h1> <strong> <em> {} </em></strong> </h1>
                    <p1>{}</p1>
                  </body>
                </html>
                """.format(str_week,str_today,message,total_cost,html_table)
                      
        ses_client = boto3.client('ses', region_name='us-east-1')
         
        message = {
            'Subject': {'Data': 'AWS training account cost report'},
            'Body': {'Html': {'Data': html}}
        }
         
        response = ses_client.send_email(
            Source='<SES-verified-identity>',
            Destination={'ToAddresses': [ 'email-1','email-2']},
            Message=message
        )
         
        print(response)

Make sure to add the necessary permissions to your Lambda role. For this setup you can refer to the following blogs:

https://medium.com/@TechStoryLines/generate-aws-cost-reports-automatically-using-the-cost-explorer-api-ef472fca4151

https://techstorylines.hashnode.dev/generate-aws-cost-reports-automatically-using-the-cost-explorer-api