DEV Community

Dan Croak
Dan Croak

Posted on • Originally published at dancroak.com

1

Heroku to Slack with AWS Lambda

When my production app processes change state on Heroku, I want to be notified in Slack:

Screenshot of Slack notification

Other examples:

17:38:52 clock.1 `bundle exec ruby schedule/clock.rb` up
17:39:03 web.1 `bundle exec puma -p $PORT -C ./config/puma.rb` up
17:39:05 web.2 `bundle exec puma -p $PORT -C ./config/puma.rb` up
Enter fullscreen mode Exit fullscreen mode

Pager-notifying events:

17:38:52 queuenote.1 `bundle exec ruby queue/note.rb` crashed
Enter fullscreen mode Exit fullscreen mode

Heroku has webhooks for these events but their payloads aren't in the format needed for Slack incoming webhooks.

AWS Lambda is the perfect glue to transform the Heroku webhook's JSON payload into a useful JSON payload for Slack's incoming webhook.

Slack config

Create an incoming webhook. Copy the URL.

Lambda config

Create a Lambda function. AWS' supported runtimes include Node, Python, Ruby, and Go. You can alternatively implement a custom runtime.

Here's an example in Ruby:

require "json"
require "net/http"
require "time"
require "uri"

def lambda_handler(event:, context:)
  dyno = JSON.parse(event["body"])
  name = dyno["data"]["name"] || ""
  state = dyno["data"]["state"] || ""

  ignored_states = ["starting", "down"].include?(state)
  term_one_off = state == "crashed" && ["scheduler", "run"].any? { |p| name.include?(p) }

  if ignored_states || term_one_off || name.include?("release")
    return {
      statusCode: 200,
      headers: { "Content-Type": "application/json" },
      body: "ok"
    }
  end

  uri = URI.parse(ENV.fetch("SLACK_URL"))
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  req = Net::HTTP::Post.new(uri.request_uri)
  req["Content-Type"] = "application/json"

  req.body = {
    text: [
      Time.parse(dyno["created_at"]).getlocal("-07:00").strftime('%H:%M:%S'),
      name,
      "`#{dyno["data"]["command"]}`",
      state
    ].compact.join(" ")
  }.to_json

  res = http.request(req)

  return {
    statusCode: 200,
    headers: { "Content-Type": "application/json" },
    body: res.body
  }
end
Enter fullscreen mode Exit fullscreen mode

Paste the Slack incoming webhook URL as an environment variable, which is encrypted at rest.

Create an API Gateway to make the Lambda function accessible in the Heroku web UI.

Heroku config

Go to:

https://dashboard.heroku.com/apps/YOUR-APP-NAME/webhooks
Enter fullscreen mode Exit fullscreen mode

Create a webhook with event type "dyno". Paste the API Gateway URL as the Payload URL.

Modify to taste

Edit and save the code in Lambda's web-based text editor. Trigger a webhook to test the function. View the auto-created CloudWatch logs for each function call.

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay