[PYTHON] Lambda x Amazon SNS vous informera quotidiennement de votre facture AWS par e-mail

introduction

Vous pouvez définir des alertes de facturation sur CloudWatch, mais en tant que personne très inquiète, j'aimerais vérifier le montant de la facturation du début du mois à la veille par e-mail tous les jours. Il y avait de nombreuses façons de notifier les canaux Slack à l'aide d'AWS Lambda et des webhooks, mais il n'y avait pas beaucoup de façons de notifier par e-mail, donc je les ai résumées.

Aperçu

Compréhension des termes

En particulier, les termes qui apparaissent dans Amazon SNS et leurs relations étaient compliqués, je vais donc les résumer très grossièrement.

Pour une meilleure compréhension, la section terminologie de l'article suivant a été très utile, il est donc recommandé de la lire attentivement à l'avance.

Connaissances de base pour l'envoi de notifications push sur Amazon SNS | UNITRUST

Méthode de réglage

Activer Cost Explorer

Si vous n'avez pas activé Cost Explorer, activez-le depuis Mon tableau de bord de facturation (https://console.aws.amazon.com/billing/home#/costexplorer).

1.Cost Explorer.png

Créer un sujet SNS

Accédez à l'écran de service d'Amazon SNS.

Dans le menu "Sujet", appuyez sur "Créer un sujet". 2-1.トピックメニュー.png

Entrez "Nom" et "Nom d'affichage" et appuyez sur "Créer un sujet".

Créer un abonnement

Créez un abonnement (+ point de terminaison).

Cliquez sur "Créer un abonnement".

Entrez les éléments suivants et appuyez sur "Créer un abonnement".

nom de l'article Valeur d'entrée / valeur de sélection
Sujet ARN ARN du sujet que vous avez noté
protocole Email
point final Adresse e-mail reçue

3-2.サブスクリプションの作成.png

Approbation d'abonnement

Un e-mail de confirmation sera envoyé à l'adresse e-mail spécifiée pour le point de terminaison avec le sujet «Notification AWS - Confirmation d'abonnement», alors appuyez sur «Confirmer l'abonnement».

4-1.認証メール.png 4-2.認証メール.png

Le statut de l'abonnement associé au sujet sera "Confirmé". 4-3.認証メール.png

Créer une fonction Lambda

Vous avez créé un point de terminaison d'abonnement à une rubrique.

Lorsque vous envoyez un message à l'ARN d'une rubrique, le message est remis au point de terminaison (adresse e-mail) associé à cette rubrique. Ensuite, créez une fonction Lambda qui générera un message à émettre vers l'ARN du sujet. Tout d'abord, appuyez sur «Créer une fonction» dans le tableau de bord AWS Lambda.

5-1.Lambda関数.png

Confirmez que l'option est "Créer à partir de zéro", entrez ce qui suit dans "Informations de base" et appuyez sur "Créer une fonction".

nom de l'article Valeur d'entrée / valeur de sélection
Nom de la fonction sendCost (avec n'importe quel nom que vous aimez)
Durée Python 3.7
Nom de rôle SNSServiceRoleForLambda (avec n'importe quel nom que vous aimez)
Modèle de politique Politique de publication Amazon SNS

5-2.Lambda関数.png

Test de fonction Lambda

Ensuite, nous écrirons le code pour acquérir les informations de facturation, mais nous allons d'abord écrire le processus pour émettre un message de test pour confirmer les paramètres jusqu'à présent. Entrez le code suivant dans le champ "Code de fonction" en bas après avoir créé la fonction.

lambda_function.py


import boto3

def lambda_handler(event, context):
	sns = boto3.client('sns')
	subject = 'L'objet de l'e-mail de test de Lambda.'
	message = 'Il s'agit du corps de l'e-mail de test de Lambda.'

	response = sns.publish(
    	TopicArn = 'arn:aws:sns:*:*:*',
    	Subject = subject,
    	Message = message
	)

	return response

Et, en réalité, il sera exécuté périodiquement avec un déclencheur, mais je vais essayer de l'envoyer manuellement.

Après avoir appuyé sur "Enregistrer" en haut à droite de l'écran, appuyez sur "Test", entrez un nom approprié dans "Nom de l'événement", et appuyez sur "Créer". D'autres peuvent être laissés à leurs valeurs par défaut.

5-3.Lambda関数.png

Lorsque vous revenez à l'écran d'origine et cliquez à nouveau sur "Test" en haut à droite, la fonction sera exécutée et le courrier devrait être arrivé à l'adresse électronique de réception spécifiée. 5-4.Lambda関数.png

Si vous ne le recevez pas, vérifiez si un message d'erreur s'affiche sur la console (Résultats de l'exécution) en bas du champ de saisie du code et si l'ARN saisi est correct.

Création de code pour la notification des informations de facturation

Enfin, j'écrirai le code pour obtenir le montant de facturation de Cost Explorer et le notifierai sur Amazon SNS. Pour TopicArn, définissez l'ARN qui a été réservé lors de la création de la rubrique SNS comme auparavant.

** * Comme cela sera décrit plus loin, une erreur se produira même si vous testez sans effectuer de réglages supplémentaires! ** **

lambda_function.py


import boto3
from datetime import datetime, timedelta, date

def lambda_handler(event, context):
    ce = boto3.client('ce')
    sns = boto3.client('sns')

    #Obtenez la facture totale pour ce mois
    total_billing = get_total_billing(ce)
    #Obtenez le montant total de la facturation pour ce mois (pour chaque service)
    service_billings = get_service_billings(ce)

    #Générer des messages à publier sur des rubriques Amazon SNS
    (subject, message) = get_message(total_billing, service_billings)

    response = sns.publish(
        TopicArn = 'arn:aws:sns:*:*:*',
        Subject = subject,
        Message = message
    )

    return response

def get_total_billing(ce):
    (start_date, end_date) = get_total_cost_date_range()

    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': start_date,
            'End': end_date
        },
        Granularity='MONTHLY',
        Metrics=[
            'AmortizedCost'
        ]
    )

    return {
        'start': response['ResultsByTime'][0]['TimePeriod']['Start'],
        'end': response['ResultsByTime'][0]['TimePeriod']['End'],
        'billing': response['ResultsByTime'][0]['Total']['AmortizedCost']['Amount'],
    }

def get_service_billings(ce):
    (start_date, end_date) = get_total_cost_date_range()

    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': start_date,
            'End': end_date
        },
        Granularity='MONTHLY',
        Metrics=[
            'AmortizedCost'
        ],
        GroupBy=[
            {
                'Type': 'DIMENSION',
                'Key': 'SERVICE'
            }
        ]
    )

    billings = []

    for item in response['ResultsByTime'][0]['Groups']:
        billings.append({
            'service_name': item['Keys'][0],
            'billing': item['Metrics']['AmortizedCost']['Amount']
        })

    return billings


def get_total_cost_date_range():
    start_date = date.today().replace(day=1).isoformat()
    end_date = date.today().isoformat()

    # get_cost_and_usage()Étant donné que la même date ne peut pas être spécifiée pour le début et la fin de, si aujourd'hui est le 1er, elle sera comprise entre "1er du mois dernier et 1er de ce mois (aujourd'hui)"
    if start_date == end_date:
        end_of_month = datetime.strptime(start_date, '%Y-%m-%d') + timedelta(days=-1)
        begin_of_month = end_of_month.replace(day=1)
        return begin_of_month.date().isoformat(), end_date
    return start_date, end_date


def get_message(total_billing, service_billings):
    start = datetime.strptime(total_billing['start'], '%Y-%m-%d').strftime('%Y/%m/%d')

    #La date de fin n'étant pas incluse dans le résultat, elle devrait être la veille à l'écran
    end_today = datetime.strptime(total_billing['end'], '%Y-%m-%d')
    end_yesterday = (end_today - timedelta(days=1)).strftime('%Y/%m/%d')

    total = round(float(total_billing['billing']), 2)
    subject = f'{start}~{end_yesterday}Montant facturé:${total:.2f}'

    message = []
    message.append('[Panne]')
    for item in service_billings:
        service_name = item['service_name']
        billing = round(float(item['billing']), 2)

        if billing == 0.0:
            #S'il n'y a pas de demande, le détail ne sera pas affiché
            continue
        message.append(f'・{service_name}: ${billing:.2f}')

    return subject, '\n'.join(message)

Je pense que c'est complet, mais ** le rôle attribué à Lamda n'a pas l'autorisation d'accéder à Cost Explorer **, donc l'erreur suivante se produit.

"errorMessage": "An error occurred (AccessDeniedException) when calling the GetCostAndUsage operation: User: arn:aws:sts::251745928455:assumed-role/SNSServiceRoleForLambda/sendCost is not authorized to perform: ce:GetCostAndUsage on resource: arn:aws:ce:us-east-1:251745928455:/GetCostAndUsage"

Par conséquent, sur l'écran de gestion IAM, associez une stratégie pouvant accéder à Cost Explorer au rôle.

Créer et attacher des politiques

Tout d'abord, affichez Liste des stratégies sur l'écran de gestion IAM et appuyez sur «Créer une stratégie».

6-1.ポリシー作成.png

Entrez les éléments suivants et appuyez sur "Confirmer la politique".

nom de l'article Valeur d'entrée / valeur de sélection
un service Cost Explorer Service
action Recherchez "Get Cost And Usage" et vérifiez-le

6-2ポリシー作成.png

Sur l'écran de confirmation de la politique, entrez «Nom» et appuyez sur «Créer une politique».

6-3.ポリシー作成.png

Accédez à l'écran Liste des rôles (https://console.aws.amazon.com/iam/home#/roles) et sélectionnez le rôle que vous avez attribué à Lambda.

6-4.ポリシー作成.png

Cliquez sur «Joindre la politique».

6-5.ポリシー作成.png

Dans "Filtre de politique", entrez le nom que vous avez défini lors de la création de la politique et de la recherche ("AmazonCostExplorerGetCostAccess" dans l'exemple de cet article), vérifiez les résultats et appuyez sur "Joindre la politique".

6-6.ポリシー作成.png

Exécution de la fonction Lambda

Maintenant que la fonction peut être exécutée normalement, cliquez sur "Test" en haut à droite de l'écran de réglage de la fonction créée.

7-1.Lambda関数.png

Si tout est configuré correctement, vous devriez recevoir un e-mail comme celui ci-dessous.

7-2.メール.png

Paramètres de déclenchement

Enfin, définissez un déclencheur pour vous avertir par e-mail à une heure fixe chaque jour. Appuyez sur "Add Trigger" sur le côté gauche de l'écran de réglage des fonctions. 8-1.トリガーの設定.png

Réglez comme suit et appuyez sur "Ajouter".

nom de l'article Valeur d'entrée / valeur de sélection
Sélectionnez un déclencheur CloudWatch Events/EventBridge
règle 新規règleの作成
Nom de la règle sendDailyCost (de manière appropriée)
Type de règle Formule de planification
Formule de planification cron(0 14 ? * * *)
Activer le déclencheur Vérifier

Cette fois, je l'ai mis à 23h00. En guise de mise en garde, puisque l'heure est définie en UTC, définissez l'heure ** qui est obtenue en soustrayant 9 heures de ** JST (heure standard japonaise) **. 8-2.トリガーの設定.png

Après cela, veuillez vous assurer que vous recevez l'e-mail à l'heure spécifiée chaque jour.

Vous pouvez désormais dormir l'esprit tranquille tous les jours!

Informations de référence / citation

Recommended Posts

Lambda x Amazon SNS vous informera quotidiennement de votre facture AWS par e-mail
Amazon SNS → AWS Lambda → Slack → Exécuter des commandes AWS sur AWS Chatbot
Exécutez Python selon la planification sur AWS Lambda
[Python] Exécutez Headless Chrome sur AWS Lambda
Exécutez régulièrement des programmes Python sur AWS Lambda
Version Amazon API Gateway et AWS Lambda Python