# Project 13: Simplify AWS Cost Management: A Step-by-Step Guide to Automated Reports

### **Introduction:**

Uncover the secrets to effortless AWS financial management with our detailed guide, "Simplify AWS Cost Management: A Step-by-Step Guide to Automated Reports" Learn step-by-step how to set up a robust system for daily and monthly cost analyses, ensuring you gain valuable insights into your cloud expenses. Perfect for both beginners and seasoned AWS users, our guide simplifies the complexities of cost management, empowering you to optimize spending and maintain financial control over your AWS resources. Dive into the world of automated reports and discover a streamlined approach to understanding and managing your AWS costs. Read on to master your cloud finances with ease!

### **Technologies Used in This Project:**

1. **Amazon Simple Email Service (SES):** Utilized for reliable email communication, SES played a pivotal role in sending and receiving cost reports, fostering seamless information exchange.
    
2. **AWS Lambda:** The serverless computing powerhouse, Lambda, was employed to execute code in response to AWS Cost Explorer API requests, automating the generation of cost reports.
    
3. **AWS Identity and Access Management (IAM):** IAM was instrumental in defining roles and permissions, ensuring secure access to AWS services and resources for the Lambda function.
    
4. **AWS Cost Explorer API:** Leveraged to gather comprehensive insights into AWS costs and usage, the Cost Explorer API facilitated the extraction of detailed billing data for analysis.
    
5. **Amazon EventBridge:** Used to schedule and automate the periodic execution of the Lambda function, EventBridge provided a time-based trigger for generating daily and monthly cost reports.
    
6. **Python:** The coding language of choice, Python, enabled the implementation of logic for fetching cost data, formatting reports, and interacting with AWS services.
    

### **Project Overview:**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702900604694/d786af51-d1c2-48af-b767-0b66be693acc.png align="center")

### **Project:**

### **Step 1: Amazon SES Setup**

**Sender SES Setup:**

* Go to Amazon SES -&gt; Verified Identities.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702900813550/dd2716f1-e93f-4d3c-b4b4-8c0ea3fd69ef.png align="center")

* Click on "Create an identity."
    
* Choose "Email address" as the identity type, add your email, and click "Create identity."
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702900906390/b126fe06-da0b-4569-a295-ae8426cfdcaf.png align="center")

**Receiver SES Setup:**

* Repeat the process to create SES for the receiver.
    

**Verification:**

* Check your email for the verification link.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702901031661/a50ed525-ac69-4ec1-90dd-bc910895ffb3.png align="center")

* Activate your ability to receive or send emails.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702901144363/af7d67ca-ba60-466d-95ed-81bcf0ff4654.png align="center")

* Ensure the identity status is "Verified."
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702901604638/de1cc198-f293-4977-aff1-741a309738e1.png align="center")

### **Step 2: Lambda Function Setup**

1. **Create Lambda Function:**
    
    * Navigate to Lambda and click on "Create function."
        
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702901710802/a2d69ff1-4a5c-4c90-a1d9-e379fd669f00.png align="center")
    
    * Choose runtime as Python3.9 and create a new role with basic Lambda permissions.
        
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702901820397/06be9261-ab79-4c44-9635-67e3aaa71501.png align="center")
    
2. **IAM Role Permissions:**
    
    * Add necessary permissions to the Lambda function's IAM role.
        
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702901919425/a1002758-a911-4ca2-af75-25fbbb17becb.png align="center")
    
    * Required permissions: Cost-Explorer-GetUsage and AmazonSESFullAccess.
        
        ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702902238858/c787b38a-7cce-42fc-93dc-45066cbdd838.png align="center")
        
          
        Below inline policy for ce-getusage
        
    
    ```bash
    {
    	"Version": "2012-10-17",
    	"Statement": [
    		{
    			
    			"Effect": "Allow",
    			"Action": "ce:GetCostAndUsage",
    			"Resource": "*"
    		}
    	]
    }
    ```
    
    ### **Step 3: Library Package Setup**
    
    1. **Add Library Packages as Layer:**
        
        * Click on "Layers" in the Lambda function setup.
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702902729891/d0a89d5c-a8f8-4632-a6b0-31be5cb2f424.png align="center")
            
        
        * Add a layer with required library packages (boto3, DateTime, SDKPandas).
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702902818186/daed7c7a-f918-4689-a8e1-32ffa7d3566c.png align="center")
            
        * Now add Custom Datetime package
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702903200790/e740a7c1-eceb-482d-b9b8-f50adac1f037.png align="center")
            
            ```bash
            #Code you must run in local terminal to install and zip datetime library to upload 
            mkdir custome-datetime
            cd custome-datetime/
            pip install datetime -t .
            mkdir -p layer/python/lib/python3.9/site-packages/
            mv DateTime/DateTime.py layer/python/lib/python3.9/site-packages/
            zip -r DateTime.zip layer/
            ```
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702903519730/ac67391f-7740-4f55-8617-d21e8cd0162e.png align="center")
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702903548785/7a1e347a-1c9a-492a-ae40-23fee5b14e5a.png align="center")
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702903623631/201fa17d-09ea-4bf1-815a-4a28a3933316.png align="center")
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702903675545/c5f47da5-4b2f-4050-a354-60d42e9a989e.png align="center")
            
            Now Click on "Layers" in the Lambda function setup. choose custom layer and select just created layer.
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702903790990/160368e6-4dcf-4c24-9d5d-af421ade72b9.png align="center")
            
        
        ### **Step 4: Custom Python Code**
        
        1. **Integrate Python Code:**
            
            * Add Python code to the Lambda function to fetch Cost Usage from CostExplorer.
                
            * Update email addresses in the code where necessary.(You need to update at 2 places (Source and Destination).
                
            
            ```bash
            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_yesterday,
                     '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='Your Email id to send email',
                    Destination={'ToAddresses': [ 'your reciver email id']},
                    Message=message
                )
                
                 if response and response.get('ResponseMetadata', {}).get('HTTPStatusCode') == 200:
                    success_message = 'Email has been sent successfully'
                    print(success_message)
                    return success_message
                else:
                    error_message = 'Email sending failed. Check the SES response for details.'
                    print(error_message)
                    return error_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='Your Email id to send email',
                    Destination={'ToAddresses': [ 'your reciver email id']},
                    Message=message
                        )
            
                    if response and response.get('ResponseMetadata', {}).get('HTTPStatusCode') == 200:
                        success_message = 'Email has been sent successfully'
                        print(success_message)
                        return success_message
                    else:
                        error_message = 'Email sending failed. Check the SES response for details.'
                        print(error_message)
                        return error_message
            ```
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702904178288/12f4c23a-5919-4db6-89e2-4f84d7b149d8.png align="center")
            
            Now click on Deploy then Test. Create sample event and test it.
            
            If you see "Email has been sent successfully" in Response then Bingo You must get email with yesterday cost report.
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702904751882/d8cb80f1-b364-4f13-8b75-a58cadf3d055.png align="center")
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702904550510/5ada45e5-a8bc-496f-b4a4-de70a8c8decd.png align="center")
            
        
        ### **Step 5: Automation with EventBridge Rule**
        
        1. **Create EventBridge Rule:**
            
            * Set up an EventBridge rule to automate Lambda function execution.
                
            * Choose rule type as "Schedule" and set the trigger time.
                
                ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702904922715/4caaa066-9ed3-44b0-b090-72ae8c532146.png align="center")
                
        2. **Add Lambda as Target:**
            
            * Add the Lambda function as a target for the EventBridge rule.  
                
                ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702905191344/c6e3d143-726e-4a40-868f-9f06a86a58c3.png align="center")
                
                ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702905215617/3db779ba-2259-478f-a1da-95989c58e7ed.png align="center")
                
                Then Create it. Bingo you will get last month report on specified date and time each month.  
                
        
        ## **Conclusion:**
        
        Congratulations! You've successfully implemented a robust cost management solution on AWS. This guide provides detailed steps, ensuring a seamless setup of SES, Lambda function, and automation with EventBridge rules. Optimize your AWS cost management effortlessly with this comprehensive solution.  
          
        That was all for today's project. See you in another project.
