Automating Budget Alerts with Cloud Functions

wqwq
3 min readFeb 17, 2024

--

This guide provides a comprehensive overview of how to leverage Cloud Functions to send budget alerts to Slack, a popular communication platform, ensuring that you stay informed about your cloud spending in real-time.

Our Composition

Why do we need Cloud Functions?

Ideally, we want to send messages to Slack from Cloud Pub/Sub.

For Cloud Billing budget alerts, you must configure Email notification channels. Other types of notification channels are not supported.

Set up and enable Cloud Monitoring email notifications Retrieved from
https://cloud.google.com/billing/docs/how-to/budgets-notification-recipients#how-to-setup

But Cloud Billing budget alerts are not supported by other notification types except email. So we have to implement Cloud Functions through Cloud Pub/Sub.

A step-by-step guide to implementing a Cloud Function using Terraform and Golang

Terraform

resource "google_pubsub_topic" "this" {
project = var.gcp_project_id
name = "billing-alert"
}

resource "google_service_account" "notify_bigquery_alert" {
project = var.gcp_project_id
account_id = "notify-bigquery-alert"
display_name = "notify-bigquery-alert"
}

# if you need some roles, you can bind role to service account
resource "google_project_iam_member" "this" {
project = var.gcp_project_id
# example
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.notify_bigquery_alert.email}"
}

resource "random_id" "bucket_prefix" {
byte_length = 8
}

resource "google_storage_bucket" "bigquery_alert_notifier" {
name = "${random_id.bucket_prefix.hex}-gcf-source"
location = "US"
uniform_bucket_level_access = true
}

data "archive_file" "bigquery_alert_notifier" {
type = "zip"
source_dir = "${path.module}/notify_budget_alert/"
output_path = "/tmp/function-source.zip"
}

resource "google_storage_bucket_object" "bigquery_alert_notifier" {
name = "function-source.zip"
bucket = google_storage_bucket.bigquery_alert_notifier.name
source = data.archive_file.bigquery_alert_notifier.output_path
}

resource "google_cloudfunctions2_function" "bigquery_alert_notifier" {
name = "bigquery-alert-notifier"
location = var.gcp_region
description = "bigquery budget alert notifier"

build_config {
runtime = "go121"
entry_point = "Notify"
source {
storage_source {
bucket = google_storage_bucket.bigquery_alert_notifier.name
object = google_storage_bucket_object.bigquery_alert_notifier.name
}
}
}

service_config {
max_instance_count = 2
min_instance_count = 1
available_memory = "256M"
timeout_seconds = 60
ingress_settings = "ALLOW_INTERNAL_ONLY"
all_traffic_on_latest_revision = true
service_account_email = google_service_account.notify_bigquery_alert.email
}

event_trigger {
trigger_region = var.gcp_region
event_type = "google.cloud.pubsub.topic.v1.messagePublished"
pubsub_topic = google_pubsub_topic.this.id
retry_policy = "RETRY_POLICY_RETRY"
}
}

Go

package notify

import (
"context"
"fmt"
"hash/crc32"

"github.com/GoogleCloudPlatform/functions-framework-go/functions"
"github.com/cloudevents/sdk-go/v2/event"
"github.com/slack-go/slack"
)

const (
channel = "#sample"
)

func init() {
functions.CloudEvent("Notify", notify)
}

type MessagePublishedData struct {
Message PubSubMessage
}

type PubSubMessage struct {
Data []byte `json:"data"`
}

func notify(ctx context.Context, e event.Event) error {
var msg MessagePublishedData
if err := e.DataAs(&msg); err != nil {
return fmt.Errorf("event.DataAs: %v", err)
}

slackClient := slack.New("token")
message := &slack.WebhookMessage{
Channel: channel,
Attachments: []slack.Attachment{
{
Color: "danger",
Text: "budget alert",
Fields: []slack.AttachmentField{
{
Title: "hugehuge",
Value: "hogehoge",
Short: false,
},
},
},
},
}

_, _, err = slackClient.PostMessageContext(
ctx,
channel,
slack.MsgOptionAttachments(message.Attachments...),
)

return err
}

How to deploy

By Terraform

We can update Cloud Functions by the above implementation.

By Command

  • Don’t forget 「--entry-point」option. If you don’t specify this option, Cloud Functions Healthcheck will fail.
  • Specify 「--trigger-topic」 option and the Cloud Pub/Sub topic you created.
gcloud functions deploy notify-alert \
--gen2 \
--runtime=go121 \
--region=asia-northeast1 \
--source=. \
--trigger-topic=billing-alert \
--entry-point Notify \
--run-service-account notify-alert@sample.iam.gserviceaccount.com

How to Test

You can find a sample JSON in the GCP document. You can set the below JSON to the Cloud Pub/Sub setting.

{
"budgetDisplayName": "name-of-budget",
"alertThresholdExceeded": 1.0,
"costAmount": 100.01,
"costIntervalStart": "2019-01-01T00:00:00Z",
"budgetAmount": 100.00,
"budgetAmountType": "SPECIFIED_AMOUNT",
"currencyCode": "USD"
}

Reference

--

--