Pipeline Identities, Part 7: Slack and a Webhook URL

In this part you'll have the pipeline tell a Slack channel about each deployment, then clean up the whole lab. The call you'll cover:

  • call 8, job → Slack: the job posts a message after each deployment.

The credential is a URL: Slack accepts any message posted to it. It needs a Slack workspace where you may add an app; a free workspace of your own will do.

Add the Slack step

In .github/workflows/pipeline.yml, add a step after the deploy step:

1      - name: Tell the team on Slack
2        env:
3          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
4        run: |
5          curl -sS -X POST -H 'Content-type: application/json' \
6            --data "{\"text\": \"Deployed ${GITHUB_SHA::7} to the web namespace\"}" \
7            "$SLACK_WEBHOOK_URL"
  • The secret reaches the step as an environment variable, masked in the log.
  • curl posts a JSON message; Slack answers ok.

Push, and read the failure

Commit and push. In the run, Tell the team on Slack fails: the secret SLACK_WEBHOOK_URL does not exist yet, so curl is given an empty address.

Create the webhook

Slack makes the URL for one app and one channel.

  1. At api.slack.com/apps, choose Create New App → From scratch: name it pipeline-identities, and pick your workspace.
  2. Open Incoming Webhooks, and turn Activate Incoming Webhooks on.
  3. Choose Add New Webhook, and pick the channel the messages go to.
  4. Copy the URL, of the form https://hooks.slack.com/services/T…/B…/….
  5. In your repository, open Settings → Secrets and variables → Actions → Secrets, and add SLACK_WEBHOOK_URL, the URL.

Run the pipeline again

Open the failed run, and Re-run all jobs. After the rollout, the channel shows Deployed 3f1c2ab to the web namespace.

Compare your workflow with the whole pipeline

Your .github/workflows/pipeline.yml should now read as follows.

 1name: pipeline
 2
 3on:
 4  push:
 5    branches: [main]
 6  workflow_dispatch:
 7
 8permissions:
 9  contents: read
10  id-token: write # lets the job ask GitHub for an ID token
11
12jobs:
13  deliver:
14    runs-on: ubuntu-latest
15    steps:
16      - name: Check out the code with GITHUB_TOKEN
17        uses: actions/checkout@v7
18
19      - name: Log in to Docker Hub with the stored token
20        uses: docker/login-action@v4
21        with:
22          username: ${{ vars.DOCKERHUB_USERNAME }}
23          password: ${{ secrets.DOCKERHUB_TOKEN }}
24
25      - name: Build and push the image
26        run: |
27          IMAGE="docker.io/${{ vars.DOCKERHUB_USERNAME }}/pipeline-identities:${GITHUB_SHA}"
28          docker build --build-arg COMMIT="$GITHUB_SHA" -t "$IMAGE" app
29          docker push "$IMAGE"
30          echo "IMAGE=$IMAGE" >> "$GITHUB_ENV"
31
32      - name: Trade GitHub's ID token for AWS credentials
33        uses: aws-actions/configure-aws-credentials@v6
34        with:
35          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
36          aws-region: ${{ vars.AWS_REGION }}
37
38      - name: Ask AWS who the job is
39        run: aws sts get-caller-identity
40
41      - name: Point kubectl at the cluster
42        run: aws eks update-kubeconfig --name "${{ vars.EKS_CLUSTER }}" --region "${{ vars.AWS_REGION }}"
43
44      - name: Ask Kubernetes who the job is
45        run: kubectl auth whoami
46
47      - name: Deploy to the web namespace
48        run: |
49          sed "s|IMAGE_PLACEHOLDER|$IMAGE|" k8s/deployment.yaml | kubectl apply -n web -f -
50          kubectl rollout status deployment/hello -n web --timeout=180s
51
52      - name: Tell the team on Slack
53        env:
54          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
55        run: |
56          curl -sS -X POST -H 'Content-type: application/json' \
57            --data "{\"text\": \"Deployed ${GITHUB_SHA::7} to the web namespace\"}" \
58            "$SLACK_WEBHOOK_URL"

Post with the URL from your machine

Nothing else is checked: whoever holds the URL can post to the channel, from anywhere. Post from your own machine:

1$ curl -sS -X POST -H 'Content-type: application/json' \
2>   --data '{"text": "Not from the pipeline"}' \
3>   'https://hooks.slack.com/services/T…/B…/…'
4ok

To cut off a leaked URL, you would remove the webhook in the app's Incoming Webhooks page; a new one gets a new URL.

Clean up

The cluster costs money until it is destroyed; the rest is free, and leaves credentials behind.

  1. Delete the namespace, with the deployment and the pull secret:

    1$ kubectl delete namespace web
    
  2. Destroy AWS's trust in GitHub, in pipeline-identities-aws/, before the cluster, since the access entry belongs to it:

    1$ terraform destroy -var-file=values.tfvars
    
  3. Destroy the cluster: run terraform destroy in the Terraform EKS lab's directory.

  4. On Docker Hub, delete both personal access tokens, and the pipeline-identities repository.

  5. In Slack, delete the pipeline-identities app, which removes its webhook.

  6. On GitHub:

    • delete the repository, or its secrets DOCKERHUB_TOKEN and SLACK_WEBHOOK_URL;
    • delete the pipeline-identities push token, if you pushed with a token in Part 1.