Guide

Send Email from Azure Pipelines

Azure Pipelines has no built-in email-on-failure step, and wiring an SMTP relay into a job means storing a mailbox password that breaks the moment a provider tightens auth. A cleaner path: install the Nylas CLI in a job and send over the API. One command emails the right people when a stage fails, authenticated by a secret variable, with no SMTP host and no mail daemon. This guide adds email to an Azure DevOps pipeline and scopes it to the failures worth interrupting someone for.

Written by Caleb Geene Director, Site Reliability Engineering

Reviewed by Qasim Muhammad

VerifiedCLI 3.1.17 · Gmail, Outlook · last tested June 9, 2026

Command references used in this guide: nylas email send, nylas auth config, and nylas auth whoami.

How do you send email from Azure Pipelines?

Send email from Azure Pipelines by adding a job that installs the Nylas CLI and runs nylas email send. The CLI installs from a shell script in a couple of seconds on a Microsoft-hosted agent, authenticates with an API key, and sends over HTTPS. The job needs no SMTP server, no port 587, and no sendmail package. One script step turns a pipeline stage into a notifier.

This sidesteps the usual Azure DevOps email pain. There is no native email-on-failure task in the YAML schema, and a custom SMTP integration means storing a mailbox password that breaks when a provider tightens authentication. The CLI sends over the API with a key you store as a secret variable, which Azure masks in logs per Microsoft's pipeline variables documentation.

# azure-pipelines.yml — a notify job
- job: notify
  pool:
    vmImage: ubuntu-24.04
  steps:
    - script: |
        curl -fsSL https://cli.nylas.com/install.sh | bash
        export PATH="$HOME/.config/nylas/bin:$PATH"
        nylas auth config --api-key "$(NYLAS_API_KEY)"
        nylas email send \
          --to team@example.com \
          --subject "Pipeline $(Build.BuildId) finished" \
          --body "Repo $(Build.Repository.Name), branch $(Build.SourceBranchName)." \
          --yes
      displayName: Email pipeline result

How do you authenticate the CLI in an Azure pipeline?

Authenticate with nylas auth config --api-key "$(NYLAS_API_KEY)", reading the key from a secret pipeline variable. This is the headless auth path with no browser and no OAuth redirect, which is what a hosted agent needs. Define the key as a secret variable on the pipeline or in a variable group, and Azure DevOps keeps it out of job logs and out of forked-PR builds.

Treat the key like any deployment secret: never hard-code it in azure-pipelines.yml, and store shared keys in a variable group linked to Azure Key Vault so rotation happens in one place. Run nylas auth whoami first if you want a sanity check before sending. The key authorizes sending from the connected account, so rotate it within minutes if it ever surfaces in a log. Secret variables are not decrypted into the environment automatically, so map them explicitly as shown below per Microsoft's variable groups documentation.

- job: notify
  pool:
    vmImage: ubuntu-24.04
  variables:
    - group: nylas-secrets   # holds NYLAS_API_KEY (Key Vault backed)
  steps:
    - script: |
        export PATH="$HOME/.config/nylas/bin:$PATH"
        nylas auth config --api-key "$NYLAS_API_KEY"
        nylas auth whoami
      env:
        NYLAS_API_KEY: $(NYLAS_API_KEY)   # map the secret explicitly
      displayName: Configure and verify Nylas CLI

How do you email only on pipeline failure?

Email only on failure by gating the job or step with condition: failed(), so it runs when an earlier stage fails and stays silent on green runs. That is the difference between useful alerting and inbox noise. Azure evaluates the condition with the pipeline expression language, and failed() returns true only when a dependency in the same stage failed.

Place the notify job in a late stage and set dependsOn so it sees the outcome of build and test. Enrich the message with predefined variables Azure injects — $(Build.BuildId), $(Build.SourceVersion), $(System.TeamFoundationCollectionUri) — so the recipient lands on the failing run in one click. A good failure email answers what broke and where in the subject line, with the run URL in the body. See Microsoft's conditions documentation for the full set of status functions.

- stage: notify
  dependsOn:
    - build
    - test
  condition: failed()          # only when an upstream stage failed
  jobs:
    - job: alert
      pool:
        vmImage: ubuntu-24.04
      steps:
        - script: |
            curl -fsSL https://cli.nylas.com/install.sh | bash
            export PATH="$HOME/.config/nylas/bin:$PATH"
            nylas auth config --api-key "$NYLAS_API_KEY"
            RUN_URL="$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)"
            nylas email send \
              --to oncall@example.com \
              --subject "FAILED: $(Build.Repository.Name) build $(Build.BuildId)" \
              --body "Branch $(Build.SourceBranchName) ($(Build.SourceVersion)). See $RUN_URL" \
              --yes
          env:
            NYLAS_API_KEY: $(NYLAS_API_KEY)
          displayName: Email on failure

What should the failure email include?

A failure email from Azure Pipelines should be scannable in ten seconds: status, repository, branch, commit, and a direct run link near the top. Keep the body short and send HTML only when a heading and a link improve the scan. Avoid pasting full logs, which already live in the Azure DevOps run with search and retention. The --body flag accepts HTML or plain text, and the CLI auto-detects which.

Build the HTML in a heredoc and pass it to --body. This keeps the YAML readable and lets you include a heading, the failing stage, and an owner line that survives forwarding. One concrete number worth tracking: keep the recipient list under five for high-severity alerts so complaint rates stay low and the messages keep landing in inboxes rather than spam folders.

- script: |
    export PATH="$HOME/.config/nylas/bin:$PATH"
    cat > email.html <<'HTML'
    <h1>Pipeline failed</h1>
    <p>The deploy stage did not finish. Owner: platform-oncall@example.com</p>
    <p>Open the Azure DevOps run for logs and a re-run button.</p>
    HTML
    nylas email send \
      --to oncall@example.com \
      --subject "Deploy failed: $(Build.Repository.Name)" \
      --body "$(cat email.html)" \
      --yes
  env:
    NYLAS_API_KEY: $(NYLAS_API_KEY)
  displayName: Send HTML failure report

How do you reuse the job for deploy approvals?

Reuse the same notify job to email a deploy-approval prompt by removing the failed() condition and running it before a manual approval gate. Azure DevOps environments support manual approval checks that pause a stage, and an email pointing the approver at that pending run shortens the wait. The send command is identical; only the subject, recipients, and trigger condition change.

Add --track-opens to confirm the approver saw the request, and --metadata key1=release-1.4.2 to tag the message so it is filterable later. A message carries up to 50 custom key=value pairs; only the five indexed keys (key1key5) are filterable in API queries. Run the smoke test against a throwaway branch first; rotating a secret and discovering a stale key during a real release is the wrong time to learn the channel broke. See Microsoft's approvals and gates documentation for the gate setup.

- script: |
    export PATH="$HOME/.config/nylas/bin:$PATH"
    nylas auth config --api-key "$NYLAS_API_KEY"
    RUN_URL="$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)"
    nylas email send \
      --to release-manager@example.com \
      --subject "Approval needed: $(Build.Repository.Name) $(Build.BuildId)" \
      --body "Approve or reject the pending deploy: $RUN_URL" \
      --track-opens \
      --metadata key1=approval \
      --yes
  env:
    NYLAS_API_KEY: $(NYLAS_API_KEY)
  displayName: Email approval request

Next steps