Azure Pipeline Trigger
One button in GitHub starts an Azure DevOps release, streams its logs back, and records the result.
- 01A form instead of a runbook
- 02Trigger, then verify
- 03Pulling logs that are not pushed
- 04Credentials never in the script
- 05A bounded wait
- 06The result outlives the run
Architecture Overview
GitHub and Azure DevOps do not talk to each other, so promoting a release meant leaving one tab, finding the right pipeline in the other, remembering the right parameters, and then refreshing a page until it finished. This is the telephone line between them: a dispatch form with three dropdowns triggers the Azure pipeline over its REST API, then polls it and copies its logs into the GitHub run as they appear. Success or failure is shipped to Datadog, so months later there is still an answer to which version went where, and whether it worked.
How it works
Core mechanics, failure recovery paths, and system design decisions.
A form instead of a runbook
Direction, version and service are workflow_dispatch inputs, two of them dropdowns, so a subscription name cannot be typo'd. All three are handed to the pipeline as templateParameters and the run title repeats them, which makes a list of past runs readable at a glance.
Trigger, then verify
One POST starts the run. The step checks the HTTP status as well as the returned id, because a 401 with an error body and a successful run both come back as JSON — only one of them has an id, and neither is an exception.
Pulling logs that are not pushed
Azure DevOps does not stream logs out, so scripts/stream-ado-logs.sh polls the timeline API every 15 seconds and streams records by maintaining an ID seen-list, with a final drain pass ensuring trailing lines are never lost.
Credentials never in the script
The PAT and pipeline id arrive as step env vars rather than being interpolated into the shell, and the trigger call is silent — an earlier verbose curl printed the Basic auth header, which GitHub's secret masking cannot catch because the base64 is not the secret it was given.
A bounded wait
The poll loop has a timeout, so a wedged pipeline fails the job with that reason instead of holding a runner for six hours until GitHub kills it.
The result outlives the run
Status, timestamp, service, version and the GitHub run id go to Datadog at the end, and a failed deployment exits non-zero so the GitHub run is red too.
Engineering Highlights
- •One button: no second tab, no remembering pipeline parameters
- •Azure's logs appear inside the GitHub run while it is still going
- •A run that fails in Azure fails in GitHub — the two never disagree
- •Secrets travel through env, never through string interpolation into a shell
- •Honest about the cost: polling keeps a runner alive for the whole deployment