1. Docs
  2. Pulumi ESC
  3. Environments
  4. Webhooks

ESC Webhooks

    ESC Webhooks is a feature available on the Pulumi Team, Enterprise and Business Critical editions. To try it out, start a trial now.

    ESC Webhooks allow you to notify external services of events happening within your ESC environments. For example, you can trigger a notification whenever a new revision of an environment is created. When an event occurs, Pulumi will notify the registered webhook listeners via a HTTP POST request with metadata about the event. The webhook can then be used to emit a notification, start running integration tests, or even update Pulumi stacks.

    There are large number of real life applications for webhooks including serving as the foundation of most ChatOps workflows.

    Overview

    ESC Webhooks can be attached to either an environment or an organization. Environment webhooks will be notified of events specific to the environment. Organization webhooks will be notified for events happening within each of the organization’s environments.

    Organization webhooks can be managed on the Organization Settings page. Environment webhooks can be managed from the webhooks tab on each Environment’s detail page.

    Organization webhooks

    Pulumi Cloud also supports webhooks for events related to Pulumi IaC stacks and Pulumi Deployments. For additional information on these types of webhooks, see Pulumi Cloud Webhooks.

    Create a Webhook

    Pulumi Webhooks may be created through any of the following methods:

    1. Manually, in the Pulumi Cloud UI using the steps outlined in Create an Organization Webhook in the Pulumi Cloud UI or Create an Environment Webhook in Pulumi Cloud in the Pulumi Cloud UI.
    2. Declaratively, as part of a Pulumi IaC program as shown in Create an Webhook in a Pulumi IaC Program
    3. By invoking the Pulumi Cloud REST API directly.

    Create an Organization Webhook in the Pulumi Cloud UI

    1. Navigate to Settings > Webhooks.
    2. Select Create webhook.
    3. Under Destination, choose Webhook, Slack or Microsoft Teams.
      1. For generic JSON webhooks, provide a display name, payload URL, and optionally a secret.
      2. For Slack webhooks, provide a Slack webhook URL and a display name.
      3. For Microsoft Teams webhooks, provide a Microsoft Teams webhook URL and a display name.
    4. Choose which events you would like to receive using groups and filters menu.

    Create an Environment Webhook in the Pulumi Cloud UI

    1. Navigate to your environment.
    2. Navigate to Webhooks tab.
    3. Select Create webhook.
    4. Under Destination, choose Webhook, Slack, Microsoft Teams or Deployment
      1. For generic JSON webhooks, provide a display name, payload URL, and optionally a secret.
      2. For Slack webhooks, provide a Slack webhook URL and a display name.
      3. For Microsoft Teams webhooks, provide a Microsoft Teams webhook URL and a display name.
      4. For Deployment webhooks, provide the stack to deploy in the format project/stack.
    5. Choose which events you would like to receive using groups and filters menu.

    Create an Webhook in a Pulumi IaC Program

    The following example shows how to create an Environment webhook in a Pulumi IaC program by declaring a Webhook resource with the Pulumi Cloud provider.

    To create an Organization webhook instead of an Environment webhook, the code is virtually identical - just omit the environmentName value when declaring the webhook resource.

    import * as pulumi from "@pulumi/pulumi";
    import * as pulumiservice from "@pulumi/pulumiservice";
    const webhook = new pulumiservice.Webhook("example-webhook", {
        active: true,
        displayName: "webhook example",
        organizationName: "example",
        environmentName: "my-environment",
        payloadUrl: "https://example.com/webhook",
    });
    
    import pulumi
    import pulumi_pulumiservice
    webhook = pulumi_service.Webhook("example-webhook",
        active: True,
        display_name: "webhook example",
        organization_name: "example",
        environmentName: "my-environment",
        payload_url: "https://example.com/webhook",
    )
    
    import (
     "fmt"
     "github.com/pulumi/pulumi-pulumiservice/sdk/go/pulumiservice"
     "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    )
    func main() {
     pulumi.Run(func(ctx *pulumi.Context) error {
      webhook, err := pulumiservice.NewWebhook(ctx, "example-webhook", &pulumiservice.WebhookArgs{
       Active:           pulumi.Bool(true),
       DisplayName:      pulumi.String("example webhook"),
       OrganizationName: pulumi.String("example"),
          EnvironmentName:  pulumi.String("my-environment"),
       PayloadURL:       pulumi.String("https://example.com/webhook"),
      }, nil)
      if err != nil {
       return fmt.Errorf("error creating webhook: %v", err)
      }
      return nil
     })
    }
    
    using Pulumi;
    using Pulumi.PulumiService;
    class PulumiServiceWebhook: Stack
    {
        public PulumiServiceWebhook()
        {
            var webhook = new Webhook("example-webhook", new WebhookArgs{
                Active = true,
                DisplayName = "example webhook",
                OrganizationName = "example",
                EnvironmentName = "my-environment",
                PayloadUrl = "https://example.com/webhook"
            })
        }
    }
    

    Event Filtering

    Event filtering allows you to choose which events should be delivered to each webhook. You may choose to receive all events in a group, or filter to specific events (only failures, only deployment events, etc.). The following table describes the various event filters available and the context in which they are relevant.

    FilterEvent KindWebhook TypeTriggered
    environment_createdenvironmentOrganization webhooks onlyWhen a new environment is created.
    environment_deletedenvironmentOrganization webhooks onlyWhen an environment is deleted.
    environment_revision_createdenvironment_revisionOrganization or EnvironmentWhen a new revision is created on an environment.
    environment_revision_retractedenvironment_revisionOrganization or EnvironmentWhen a revision is retracted on an environment.
    environment_revision_tag_createdenvironment_revision_tagOrganization or EnvironmentWhen a new revision tag is created.
    environment_revision_tag_deletedenvironment_revision_tagOrganization or EnvironmentWhen a revision tag is deleted.
    environment_revision_tag_updatedenvironment_revision_tagOrganization or EnvironmentWhen a revision tag is updated.
    environment_tag_createdenvironment_tagOrganization or EnvironmentWhen a new environment tag is created.
    environment_tag_deletedenvironment_tagOrganization or EnvironmentWhen an environment tag is deleted.
    environment_tag_updatedenvironment_tagOrganization or EnvironmentWhen an environment tag is updated.
    imported_environment_changedimported_environment_changedOrganization or EnvironmentWhen an imported environment was changed.

    There is also an environments filter group that lets you easily subscribe to all environment events.

    GroupEvent Kinds Included
    environmentsenvironment, environment_revision, environment_revision_tag, environment_tag, imported_environment_changed

    Webhook Formats

    When creating a webhook, you can choose between a generic JSON webhook payload, slack formatted events and ms_teams formatted events.

    Slack Webhooks

    Slack Webhooks allow you to seamlessly integrate notifications about your Pulumi resources into your Slack workspace by simply providing a Slack incoming webhook URL and optionally choosing which events you want delivered using event groups and filters.

    You can either create your own Slack app (or use an existing one you may already have installed in your workspace), or follow the link below to quickly get started with a pre-defined Slack app manifest.

    Microsoft Teams Webhooks

    Microsoft Teams Webhooks allow you to seamlessly integrate notifications about your Pulumi stacks and organizations into your Microsoft Teams workspace by simply providing a Microsoft Teams incoming webhook URL and optionally choosing which events you want delivered using event groups and filters.

    Deployment Webhooks

    The Deployment webhook destination lets you trigger updates on your stacks via Pulumi Deployments, usually in response to environment_revision_created or imported_environment_changed events. This enables you to keep dependent stacks up to date with your environment changes automatically.

    Deployment webhooks require that your stacks are configured with Deployment Settings.

    Generic JSON Webhooks

    When using generic JSON webhooks, Pulumi will send an HTTP POST request to all registered webhooks. The webhook can then be used to emit a notification, start running integration tests, or even update additional stacks.

    If a secret is provided, webhook deliveries will contain a signature in the HTTP request header that can be used to authenticate messages as coming from the Pulumi Cloud.

    Payload Examples

    Each webhook payload has a format specific to the payload being emitted. Every payload will contain a sender, organization, and stack reference as appropriate. For examples of specific payloads, see Payload Reference below.

    Each webhook will contain a user field, which is the user who requested the action, an organization which is the organization name, and a URL for the event. It will also contain projectName and environmentName when applicable.

    Environment
    {
      "user": {
        "name": "Morty Smith",
        "githubLogin": "morty",
        "avatarUrl": "https://crazy-adventures.net/morty.png"
      },
      "organization": {
        "name": "Crazy Adventures",
        "githubLogin": "crazy-adventures",
        "avatarUrl": "https://crazy-adventures.net/logo.png"
      },
      "projectName": "website",
      "environmentName": "prod",
      "action": "created",
    }
    
    Environment Revision
    {
      "user": {
        "name": "Morty Smith",
        "githubLogin": "morty",
        "avatarUrl": "https://crazy-adventures.net/morty.png"
      },
      "organization": {
        "name": "Crazy Adventures",
        "githubLogin": "crazy-adventures",
        "avatarUrl": "https://crazy-adventures.net/logo.png"
      },
      "projectName": "website",
      "environmentName": "prod",
      "action": "created",
      "revision": 5
    }
    
    Environment Revision Tag
    {
      "user": {
        "name": "Morty Smith",
        "githubLogin": "morty",
        "avatarUrl": "https://crazy-adventures.net/morty.png"
      },
      "organization": {
        "name": "Crazy Adventures",
        "githubLogin": "crazy-adventures",
        "avatarUrl": "https://crazy-adventures.net/logo.png"
      },
      "projectName": "website",
      "environmentName": "prod",
      "tagName": "stable",
      "action": "created",
      "revision": 5
    }
    
    Environment Tag
    {
      "user": {
        "name": "Morty Smith",
        "githubLogin": "morty",
        "avatarUrl": "https://crazy-adventures.net/morty.png"
      },
      "organization": {
        "name": "Crazy Adventures",
        "githubLogin": "crazy-adventures",
        "avatarUrl": "https://crazy-adventures.net/logo.png"
      },
      "projectName": "website",
      "environmentName": "prod",
      "tagName": "stable",
      "action": "created"
    }
    
    Imported Environment Changed
    {
      "user": {
        "name": "Morty Smith",
        "githubLogin": "morty",
        "avatarUrl": "https://crazy-adventures.net/morty.png"
      },
      "organization": {
        "name": "Crazy Adventures",
        "githubLogin": "crazy-adventures",
        "avatarUrl": "https://crazy-adventures.net/logo.png"
      },
      "projectName": "website",
      "environmentName": "prod",
      "affectedRevisions": [2, 5, 6, 7],
      "importedEnvironmentReference": {
     "projectName": "website",
       "environmentName": "base",
     "revision": 10
      }
    }
    

    Headers

    Payloads contain several headers.

    HeaderDescription
    Pulumi-Webhook-IDUnique ID for each webhook sent which you can reference when looking at delivery logs in the Pulumi Cloud.
    Pulumi-Webhook-KindThe kind of webhook event, e.g. environment.
    Pulumi-Webhook-SignatureOnly set if the webhook has a shared secret. HMAC hex digest of the request payload, using the sha256 hash function and the webhook secret as the HMAC key.

    The following snippets show how to compute and verify the webhook signature. For examples in other languages, see danharper/hmac-examples.

    var crypto = require('crypto');
    
    const sharedSecret = ...
    const payload = req.body.toString();
    
    var hmacAlg = crypto.createHmac('sha256', sharedSecret);
    var expectedSignature = hmac.update(payloadBody).digest('hex');
    
    import * as crypto from "crypto";
    
    const sharedSecret = ...
    const payload = req.body.toString();
    
    const hmacAlg = crypto.createHmac("sha256", stackConfig.sharedSecret);
    const hmac = hmacAlg.update(payload).digest("hex");
    
    const result = crypto.timingSafeEqual(Buffer.from(webhookSig), Buffer.from(hmac));
    
    import hashlib
    import hmac
    import base64
    
    message = bytes('...', 'utf-8')
    secret = bytes('...', 'utf-8')
    
    hash = hmac.new(secret, message, hashlib.sha256)
    hash.hexdigest()
    
    expected_signature = base64.b64encode(hash.digest())
    
    func computeSignature(payload []byte, secret string) string {
    	mac := hmac.New(sha256.New, []byte(secret))
    	_, err := mac.Write(payload)
    	contract.AssertNoErrorf(err, "computing HMAC digest")
    	return fmt.Sprintf("%x", mac.Sum(nil))
    }
    

    Additional Resources

      PulumiUP 2024. Watch On Demand.