[PYTHON] Lambda x Amazon SNS benachrichtigt Sie täglich per E-Mail über Ihre AWS-Rechnung

Einführung

Sie können Abrechnungsbenachrichtigungen in CloudWatch festlegen, aber als sehr besorgte Person möchte ich den Abrechnungsbetrag von Anfang des Monats bis zum Vortag jeden Tag per E-Mail überprüfen. Es gab viele Möglichkeiten, Slack-Kanäle mit AWS Lambda und Webhooks zu benachrichtigen, aber es gab nicht viele Möglichkeiten, per E-Mail zu benachrichtigen, daher habe ich sie zusammengefasst.

Überblick

Begriffe verstehen

Insbesondere die Begriffe, die in Amazon SNS vorkommen, und ihre Beziehungen waren kompliziert, daher werde ich sie sehr grob zusammenfassen.

Für ein tieferes Verständnis war der Terminologieabschnitt des folgenden Artikels sehr hilfreich. Es wird daher empfohlen, ihn im Voraus sorgfältig zu lesen.

Grundkenntnisse zum Senden von Push-Benachrichtigungen unter Amazon SNS | UNITRUST

Einstellmethode

Aktivieren Sie den Kosten-Explorer

Wenn Sie den Kosten-Explorer nicht aktiviert haben, aktivieren Sie ihn über das Abrechnungs-Dashboard (https://console.aws.amazon.com/billing/home#/costexplorer).

1.Cost Explorer.png

Erstellen eines SNS-Themas

Wechseln Sie zum Servicebildschirm von Amazon SNS.

Drücken Sie im Menü "Thema" auf "Thema erstellen". 2-1.トピックメニュー.png

Geben Sie "Name" und "Anzeigename" ein und klicken Sie auf "Thema erstellen".

Erstellen Sie ein Abonnement

Erstellen Sie ein Abonnement (+ Endpunkt).

Klicken Sie auf "Abonnement erstellen".

Geben Sie die folgenden Elemente ein und klicken Sie auf "Abonnement erstellen".

Artikelname Eingabewert / Auswahlwert
Thema ARN ARN des Themas, das Sie aufgeschrieben haben
Protokoll Email
Endpunkt E-Mail-Adresse erhalten

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

Abonnementgenehmigung

Eine Bestätigungs-E-Mail wird an die für den Endpunkt angegebene E-Mail-Adresse mit dem Betreff "AWS-Benachrichtigung - Abonnementbestätigung" gesendet. Drücken Sie daher auf "Abonnement bestätigen".

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

Der Status des mit dem Thema verknüpften Abonnements lautet "Bestätigt". 4-3.認証メール.png

Erstellen einer Lambda-Funktion

Sie haben einen Themenabonnement-Endpunkt erstellt.

Wenn Sie eine Nachricht an die ARN eines Themas senden, wird die Nachricht an den Endpunkt (E-Mail-Adresse) gesendet, der diesem Thema zugeordnet ist. Erstellen Sie als Nächstes eine Lambda-Funktion, die eine Nachricht generiert, die an die ARN des Themas gesendet wird. Drücken Sie zunächst im AWS Lambda-Dashboard auf "Funktion erstellen".

5-1.Lambda関数.png

Vergewissern Sie sich, dass die Option "Von Grund auf neu erstellen" lautet, geben Sie unter "Grundlegende Informationen" Folgendes ein und klicken Sie auf "Funktion erstellen".

Artikelname Eingabewert / Auswahlwert
Funktionsname sendCost (mit einem beliebigen Namen)
Laufzeit Python 3.7
Rollenname SNSServiceRoleForLambda (mit einem beliebigen Namen)
Richtlinienvorlage Amazon SNS-Veröffentlichungsrichtlinie

5-2.Lambda関数.png

Lambda-Funktionstest

Als nächstes schreibe ich den Code, um die Rechnungsinformationen zu erhalten, aber zuerst schreibe ich den Prozess, um eine Testnachricht auszugeben, um die bisherigen Einstellungen zu bestätigen. Geben Sie nach dem Erstellen der Funktion den folgenden Code in das Feld "Funktionscode" unten ein.

lambda_function.py


import boto3

def lambda_handler(event, context):
	sns = boto3.client('sns')
	subject = 'Der Betreff der Test-E-Mail von Lambda.'
	message = 'Dies ist der Hauptteil der Test-E-Mail von Lambda.'

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

	return response

In Wirklichkeit wird es regelmäßig mit einem Auslöser ausgeführt, aber ich werde versuchen, es manuell zu senden.

Nachdem Sie oben rechts auf dem Bildschirm auf "Speichern" geklickt haben, drücken Sie "Test", geben Sie unter "Ereignisname" einen geeigneten Namen ein und drücken Sie auf "Erstellen". Andere können auf ihren Standardwerten belassen werden.

5-3.Lambda関数.png

Wenn Sie zum ursprünglichen Bildschirm zurückkehren und oben rechts erneut auf "Test" klicken, wird die Funktion ausgeführt und die E-Mail sollte an der angegebenen empfangenen E-Mail-Adresse angekommen sein. 5-4.Lambda関数.png

Wenn Sie es nicht erhalten, überprüfen Sie, ob auf der Konsole (Ausführungsergebnisse) unten im Codeeingabefeld eine Fehlermeldung angezeigt wird und ob die eingegebene ARN korrekt ist.

Codeerstellung für die Benachrichtigung über Rechnungsinformationen

Schließlich schreibe ich den Code, um den Rechnungsbetrag vom Kosten-Explorer zu erhalten und ihn bei Amazon SNS zu benachrichtigen. Legen Sie für "TopicArn" die ARN fest, die beim Erstellen des SNS-Themas wie zuvor gespeichert wurde.

** * Wie später beschrieben wird, tritt ein Fehler auf, auch wenn Sie ohne zusätzliche Einstellungen testen! ** ** **

lambda_function.py


import boto3
from datetime import datetime, timedelta, date

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

    #Holen Sie sich die Gesamtrechnung für diesen Monat
    total_billing = get_total_billing(ce)
    #Erhalten Sie den Gesamtabrechnungsbetrag für diesen Monat (für jeden Service)
    service_billings = get_service_billings(ce)

    #Generieren Sie Nachrichten, die zu Amazon SNS-Themen veröffentlicht werden sollen
    (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()Da das gleiche Datum für Anfang und Ende von nicht angegeben werden kann, liegt es heute im Bereich von "1. des letzten Monats bis 1. dieses Monats (heute)".
    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')

    #Da das Enddatum nicht im Ergebnis enthalten ist, sollte es am Vortag auf dem Display angezeigt werden
    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}Rechnungsbetrag:${total:.2f}'

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

        if billing == 0.0:
            #Wenn keine Anforderung vorliegt, wird die Aufschlüsselung nicht angezeigt
            continue
        message.append(f'・{service_name}: ${billing:.2f}')

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

Ich denke, dies ist vollständig, aber ** die Lamda zugewiesene Rolle hat keine Berechtigung zum Zugriff auf den Kosten-Explorer **, sodass der folgende Fehler auftritt.

"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"

Fügen Sie daher auf dem IAM-Verwaltungsbildschirm der Richtlinie eine Richtlinie hinzu, die auf den Kosten-Explorer zugreifen kann.

Richtlinien erstellen und anhängen

Zeigen Sie zunächst Richtlinienliste auf dem IAM-Verwaltungsbildschirm an und klicken Sie auf "Richtlinie erstellen".

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

Geben Sie die folgenden Elemente ein und klicken Sie auf "Richtlinie bestätigen".

Artikelname Eingabewert / Auswahlwert
Bedienung Cost Explorer Service
Aktion Suchen Sie nach "Kosten und Nutzung abrufen" und überprüfen Sie diese

6-2ポリシー作成.png

Geben Sie auf dem Bildschirm zur Bestätigung der Richtlinie "Name" ein und klicken Sie auf "Richtlinie erstellen".

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

Wechseln Sie zum Bildschirm Rollenliste (https://console.aws.amazon.com/iam/home#/roles) und wählen Sie die Rolle aus, die Sie Lambda zugewiesen haben.

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

Klicken Sie auf "Richtlinie anhängen".

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

Geben Sie unter "Richtlinienfilter" den Namen ein, den Sie beim Erstellen der Richtlinie und bei der Suche festgelegt haben ("AmazonCostExplorerGetCostAccess" im Beispiel dieses Artikels), überprüfen Sie die Treffer und klicken Sie auf "Richtlinie anhängen".

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

Ausführung der Lambda-Funktion

Nachdem die Funktion nun normal ausgeführt werden kann, klicken Sie oben rechts im Einstellungsbildschirm der erstellten Funktion auf "Test".

7-1.Lambda関数.png

Wenn alles richtig eingerichtet ist, sollten Sie eine E-Mail wie die folgende erhalten.

7-2.メール.png

Triggereinstellungen

Setzen Sie schließlich einen Auslöser, um Sie jeden Tag zu einer festgelegten Zeit per E-Mail zu benachrichtigen. Drücken Sie auf der linken Seite des Funktionseinstellungsbildschirms auf "Trigger hinzufügen". 8-1.トリガーの設定.png

Stellen Sie wie folgt ein und drücken Sie "Hinzufügen".

Artikelname Eingabewert / Auswahlwert
Wählen Sie einen Auslöser CloudWatch Events/EventBridge
Regel 新規Regelの作成
Regelname sendDailyCost (entsprechend)
Regeltyp Zeitplanformel
Zeitplanformel cron(0 14 ? * * *)
Trigger aktivieren Überprüfen

Diesmal habe ich es auf 23:00 eingestellt. Da die Zeit in UTC eingestellt ist, stellen Sie als Einschränkung die Zeit ** ein, die durch Subtrahieren von 9 Stunden von ** JST (japanische Standardzeit) ** erhalten wird. 8-2.トリガーの設定.png

Stellen Sie danach sicher, dass Sie die E-Mail jeden Tag zur angegebenen Zeit erhalten.

Jetzt können Sie jeden Tag beruhigt schlafen!

Referenzinformationen / Zitat

Recommended Posts

Lambda x Amazon SNS benachrichtigt Sie täglich per E-Mail über Ihre AWS-Rechnung
Amazon SNS → AWS Lambda → Slack → Führen Sie AWS-Befehle in AWS Chatbot aus
Führen Sie Python planmäßig auf AWS Lambda aus
[Python] Führen Sie Headless Chrome unter AWS Lambda aus
Führen Sie regelmäßig Python-Programme auf AWS Lambda aus
Amazon API Gateway und AWS Lambda Python-Version