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.
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 *
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.
Comments