An Interest In:
Web News this Week
- April 2, 2024
- April 1, 2024
- March 31, 2024
- March 30, 2024
- March 29, 2024
- March 28, 2024
- March 27, 2024
AWS Budgets: Update alert thresholds unlimitedly with Lambda
Motivation
When I use AWS Budgets, I can receive notifications when the billing is above a threshold.
The facts are that:
- We can set only 10 alert thresholds for one budget.
- Two budgets are free but we are charged for using more budgets.
For personal use, usually, I'm not charged so much (around 10 USD per month). I want to be notified every 1 USD but there is a limitation of 10 alerts like above fact.
I this post, I share how to update the alert threshold incremently when AWS Budgets triggers.
Architecture
EventBridge triggers Lambda(setBudget) at the 1st of months. This Lambda delete and re-create the budget to initialize for a month and create 10 alert thresholds: 1, 2, 3, ... 10 USD thresholds, for example.
When the billing is above the threshold, the user is notified via SNS email. At the same time, the second Lambda function is triggered and updates the threshold. If the triggered threshold is 1 USD, the Lambda function update the threshold to 11 USD.
CloudFormation template
- Deploy from the Console. Create stack -> Upload a template -> Choose this file.
- Enter Stack name and parameters: budget name, email, increment.
- Leave the rest default. Choose Next, Next, check IAM acknowledge, and Submit.
- When starting in the middle of the month, manually run the
SetBudgetHandler
Lambda function. Note that you'll get notified of all alerts of the current used cost.
AWSTemplateFormatVersion: "2010-09-09"Description: "Increment alert threshold of AWS Budgets"Parameters: budgetname: Type: String Default: increment-notification increment: Type: String Default: 1 email: Type: String AllowedPattern: "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$" ConstraintDescription: Must be a valid email address.Resources: MyTopic: Type: AWS::SNS::Topic MyTopicTokenSubscription: Type: AWS::SNS::Subscription Properties: Protocol: email TopicArn: !Ref MyTopic Endpoint: !Ref email MyTopicPolicy: Type: AWS::SNS::TopicPolicy Properties: PolicyDocument: Statement: - Action: sns:Publish Effect: Allow Principal: Service: budgets.amazonaws.com Resource: !Ref MyTopic Sid: "0" Version: "2012-10-17" Topics: - !Ref MyTopic UpdateBudgetHandlerServiceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole UpdateBudgetHandlerServiceRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyDocument: Statement: - Action: budgets:* Effect: Allow Resource: "*" Version: "2012-10-17" PolicyName: UpdateBudgetHandlerServiceRoleDefaultPolicy Roles: - !Ref UpdateBudgetHandlerServiceRole UpdateBudgetHandler: Type: AWS::Lambda::Function Properties: Code: ZipFile: | import os import re import boto3 budget_name = os.environ['BUDGET_NAME'] increment = os.environ['INCREMENT'] def handler(event, context): print(event) client = boto3.client('budgets') account_id = context.invoked_function_arn.split(":")[4] print("account_id", account_id) message = event['Records'][0]['Sns']['Message'] print("message", message) # catch 1 from "Alert Threshold: > $1.00" match = re.search(r"Alert Threshold: > \$(\d{1,})\.00", message) if not match: print("No Budget Notification. Exit.") return current_value_str = match.groups()[0] print("current_value", current_value_str) current_value = int(current_value_str) next_value = current_value + 10 * int(increment) response = client.update_notification( AccountId=account_id, BudgetName=budget_name, OldNotification={ "NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": current_value, "ThresholdType": "ABSOLUTE_VALUE", }, NewNotification={ "NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": next_value, "ThresholdType": "ABSOLUTE_VALUE", }, ) Role: !GetAtt UpdateBudgetHandlerServiceRole.Arn Environment: Variables: BUDGET_NAME: !Ref budgetname INCREMENT: !Ref increment Handler: index.handler Runtime: python3.9 Timeout: 10 DependsOn: - UpdateBudgetHandlerServiceRoleDefaultPolicy - UpdateBudgetHandlerServiceRole UpdateBudgetHandlerEventInvokeConfig: Type: AWS::Lambda::EventInvokeConfig Properties: FunctionName: !Ref UpdateBudgetHandler Qualifier: $LATEST MaximumRetryAttempts: 0 UpdateBudgetHandlerAllowInvokeStackMyTopic: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt UpdateBudgetHandler.Arn Principal: sns.amazonaws.com SourceArn: !Ref MyTopic UpdateBudgetHandlerMyTopic: Type: AWS::SNS::Subscription Properties: Protocol: lambda TopicArn: !Ref MyTopic Endpoint: !GetAtt UpdateBudgetHandler.Arn SetBudgetHandlerServiceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole SetBudgetHandlerServiceRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyDocument: Statement: - Action: budgets:* Effect: Allow Resource: "*" Version: "2012-10-17" PolicyName: SetBudgetHandlerServiceRoleDefaultPolicy Roles: - !Ref SetBudgetHandlerServiceRole SetBudgetHandler: Type: AWS::Lambda::Function Properties: Code: ZipFile: | import os import boto3 budget_name = os.environ['BUDGET_NAME'] sns_arn = os.environ['SNS_ARN'] increment = os.environ['INCREMENT'] client = boto3.client("budgets") def handler(event, context): account_id = context.invoked_function_arn.split(":")[4] # Delete if the budget exists try: client.delete_budget(AccountId=account_id, BudgetName=budget_name) print("Deleted the old budget.") except client.exceptions.NotFoundException: pass # Create new budget client.create_budget( AccountId=account_id, Budget={ "BudgetName": budget_name, "BudgetLimit": {"Amount": "100.0", "Unit": "USD"}, "CostTypes": { "IncludeTax": True, "IncludeSubscription": True, "UseBlended": False, "IncludeRefund": False, "IncludeCredit": False, "IncludeUpfront": True, "IncludeRecurring": True, "IncludeOtherSubscription": True, "IncludeSupport": True, "IncludeDiscount": True, "UseAmortized": False, }, "TimeUnit": "MONTHLY", "BudgetType": "COST", }, NotificationsWithSubscribers=[ { "Notification": { "NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": threshold, "ThresholdType": "ABSOLUTE_VALUE", "NotificationState": "OK", }, "Subscribers": [ {"SubscriptionType": "SNS", "Address": sns_arn}, ], } for threshold in range(1, 1 + 10 * int(increment), int(increment)) ], ) print("Created a new budget.") Role: !GetAtt SetBudgetHandlerServiceRole.Arn Environment: Variables: BUDGET_NAME: !Ref budgetname SNS_ARN: !Ref MyTopic INCREMENT: !Ref increment Handler: index.handler Runtime: python3.9 Timeout: 10 DependsOn: - SetBudgetHandlerServiceRoleDefaultPolicy - SetBudgetHandlerServiceRole SetBudgetHandlerEventInvokeConfig: Type: AWS::Lambda::EventInvokeConfig Properties: FunctionName: !Ref SetBudgetHandler Qualifier: $LATEST MaximumRetryAttempts: 0 ScheduleRule: Type: AWS::Events::Rule Properties: ScheduleExpression: cron(0 0 1 * ? *) State: ENABLED Targets: - Arn: !GetAtt SetBudgetHandler.Arn Id: Target0 ScheduleRuleAllowEventRuleStackSetBudgetHandler: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt SetBudgetHandler.Arn Principal: events.amazonaws.com SourceArn: !GetAtt ScheduleRule.Arn
Summary
I've shared how to update AWS Budgets threshold incrementally.
Original Link: https://dev.to/aws-builders/aws-budgets-update-alert-thresholds-unlimitedly-with-lambda-5e7h
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To