top of page
Foto do escritorRafael Natali

Automatizando a criação de relatórios de custos da AWS

Este artigo descreve como automatizar a criação de relatórios da AWS, usando o serviço AWS Cost Report, e enviá-los por email para destinatários específicos.

AWS - Cost Report via Email

Infelizmente, o AWS Cost Explorer não possui uma capacidade integrada para enviar relatórios por e-mail. Como resultado, são necessários serviços e funcionalidades alternativos para criar e transmitir esses relatórios.


Nesta solução, um script em Python executado no AWS Lambda gera o relatório. O relatório é posteriormente armazenado em um S3 bucket e enviado para meu e-mail. Um AWS EventBridge Scheduler dispara a função Lambda toda segunda-feira às 7h.


AWS IAM roles e policies são formuladas para gerenciar permissões tanto para o EventBridge quanto para o Lambda.


Resumo dos Serviços e Recursos da AWS


Os seguintes serviços e/ou recursos foram utilizados na solução:


Criar e enviar o relatório


Primeiramente, precisamos criar um bucket no S3. Este tópico armazenará o relatório de custos em formato Excel. Em seguida, crie e verifique o(s) e-mail(s) que receberá(ão) o relatório no SES. Depois, crie AWS IAM roles e policies da seguinte forma:


# AWS EventBridge Scheduler Role
# Permission to Invoke the Lambda Function

{
  "Version": "2012-10-17",
  "Statement": [
  {
    "Effect": "Allow",
    "Action": [
       "lambda:InvokeFunction"
    ],
    "Resource": [
      "arn:aws:lambda:<region>:<aws-account>:function:cost-report:*",
      "arn:aws:lambda:<region>:<aws-account>:function:cost-report"
    ]
  }
 ]
}

# AWS Lambda Role
# Permission to generate the Cost Report (ce), send email (see), and store the file in the S3 bucket (s3)
 
  {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ses:VerifyEmailIdentity",
                "ses:GetIdentityPolicies",
                "ses:SendRawEmail",
                "ses:GetIdentityMailFromDomainAttributes",
                "ce:GetCostAndUsage",
                "ses:SendBounce",
                "ses:GetIdentityVerificationAttributes",
                "ses:GetIdentityNotificationAttributes",
                "ses:SendEmail",
                "ses:SendTemplatedEmail",
                "ses:SendCustomVerificationEmail",
                "ses:SendBulkTemplatedEmail",
                "ses:ListVerifiedEmailAddresses",
                "ses:ListIdentities",
                "ses:VerifyEmailAddress"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::cost-report-<aws-account>-<region>/*"
            ]
        }
    ]
}

Configurar o EventBridge para acionar a função Lambda às segundas-feiras às 07h com a seguinte expressão crontab:

00 07 ? * MON *

AWS EventBridge

Finalmente, crie a função Lambda. O código a seguir criará um Relatório de Custos do mês corrente ("MONTH-TO-DATE") da sua conta da AWS:

import csv
import os
import boto3
import datetime
from botocore.exceptions import NoCredentialsError

def lambda_handler(event, context):
    
    # Set up the client
    ce = boto3.client('ce', region_name='us-east-1')

    # Set the time period for the report to the beginning of the current month
    now = datetime.datetime.now()
    start = datetime.datetime(now.year, now.month, 1)
    end = datetime.datetime.now()

    # Define the request
    request = {
        'TimePeriod': {
            'Start': start.strftime('%Y-%m-%d'),
            'End': end.strftime('%Y-%m-%d')
        },
        'Granularity': 'MONTHLY',
        'Metrics': ['UnblendedCost'],
        'GroupBy': [{'Type': 'DIMENSION', 'Key': 'SERVICE'}]
    }

    # Get the report
    result = ce.get_cost_and_usage(**request)
    
    # Extract the service names, cost amounts, units, and region
    service_names = []
    cost_amounts = []
    cost_units = []
    regions = []

    for row in result['ResultsByTime']:
        sorted_groups = sorted(row['Groups'], key=lambda x: float(x['Metrics']['UnblendedCost']['Amount']), reverse=True)
        for group in sorted_groups:
            service_names.append(group['Keys'][0])
            cost_amount = round(float(group['Metrics']['UnblendedCost']['Amount']), 2)
            cost_amounts.append(cost_amount)
            cost_units.append(group['Metrics']['UnblendedCost']['Unit'])
            regions.append(ce.meta.region_name)

    # Create a list of rows for the CSV file
    rows = zip(service_names, cost_amounts, cost_units, regions)

    # Specify the file path for the CSV file
    current_date = datetime.datetime.now().strftime("%Y-%m-%d")
    csv_file_path = f'/tmp/cost_report_{current_date}.csv'

    # Write the rows to the CSV file
    with open(csv_file_path, 'w', newline='') as csv_file:
        writer = csv.writer(csv_file)
        writer.writerow(['Service', 'Cost', 'Unit', 'Region'])  # Write the header
        writer.writerows(rows)  # Write the data rows

    # Upload the CSV file to S3 bucket
    bucket_name = '<your S3 bucket>'
    s3_key = f'cost_report_{current_date}.csv'

    s3_client = session.client('s3')
    try:
        s3_client.upload_file(csv_file_path, bucket_name, s3_key)
        print("CSV file uploaded successfully to S3 bucket.")
    except FileNotFoundError:
        print("The CSV file was not found.")
    except NoCredentialsError:
        print("AWS credentials not found.")
    
    # Send email
    # Specify the email recipient
    recipient_email = "<your verified email>"
    
    # Read the CSV file content
    with open(csv_file_path, 'rb') as csv_file:
        csv_content = csv_file.read()
    
    # Send the email with the CSV file attached using SES
    ses_client = boto3.client('ses', region_name='<your SES region>')
    
    # Specify the sender's email address
    sender_email = "<your verified email>"
    
    # Specify the email subject
    email_subject = "AWS Cost Report CSV"
    
    # Create the raw email content
    raw_email = (
        f"From: {sender_email}\n"
        f"To: {recipient_email}\n"
        f"Subject: {email_subject}\n"
        "MIME-Version: 1.0\n"
        "Content-Type: multipart/mixed; boundary=\"NextPart\"\n\n"
        "--NextPart\n"
        "Content-Type: text/plain\n\n"
        "Please find the cost report attached.\n\n"
        "--NextPart\n"
        "Content-Type: text/csv;\n"
        "Content-Disposition: attachment; filename=\"cost_report.csv\"\n\n"
        f"{csv_content.decode('utf-8')}\n"
        "--NextPart--"
    )
    
    try:
        response = ses_client.send_raw_email(
            Source=sender_email,
            RawMessage={
                'Data': raw_email.encode('utf-8')
            }
        )
        print("Email sent successfully.")
    except Exception as e:
        print(f"An error occurred while sending the email: {e}")

No horário agendado, você receberá um e-mail com o seu relatório de custos em anexo.






1 visualização0 comentário

Comments

Couldn’t Load Comments
It looks like there was a technical problem. Try reconnecting or refreshing the page.
bottom of page