Run GraphWarden proxy deployments from Azure DevOps Pipelines
A complete Azure DevOps YAML pipeline that builds, tests, and smoke-tests a GraphWarden proxy deployment. Variable groups hold credentials and a banner comment prevents accidental commits of real secrets. The template below is runnable after replacing the marked values with your organization's tenant, client id, and secret.
Azure DevOps Pipelines with variable groupsPrerequisites
Add a GraphWarden smoke test with an AI assistant
Add a GraphWarden smoke test to my existing Azure DevOps YAML pipeline.
I want a pipeline stage that:
1. Acquires a token from my GraphWarden proxy's /oauth2/v2.0/token endpoint
2. Fails the build if token acquisition fails
3. Uses variable-group-backed secrets (not inline values)
Reference documentation: https://graphwarden.com/llms.txt
Ask me for my existing azure-pipelines.yml and whether my variable group is already configured.
Reference: llms.txt
The pipeline, end to end
The YAML below is a complete Azure DevOps pipeline. Copy it into a file named azure-pipelines.yml at the root of your repository. Do not edit the variable references — the pipeline reads every secret from the graphwarden-secrets variable group at run time.
# DO NOT COMMIT REAL SECRETS TO THIS FILE.
# Use the Azure DevOps UI to create a variable group "graphwarden-secrets"
# with GRAPHWARDEN_PROXY_URL, GRAPHWARDEN_CLIENT_ID, GRAPHWARDEN_CLIENT_SECRET.
trigger:
branches:
include:
- main
variables:
- group: graphwarden-secrets
- name: dotnetVersion
value: '8.0.x'
stages:
- stage: BuildAndTest
jobs:
- job: Build
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
version: $(dotnetVersion)
- script: dotnet restore
displayName: 'Restore dependencies'
- script: dotnet build --configuration Release --no-restore
displayName: 'Build proxy'
- script: dotnet test --configuration Release --no-build --logger trx
displayName: 'Run proxy tests'
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFormat: 'VSTest'
testResultsFiles: '**/*.trx'
- stage: IntegrationTest
dependsOn: BuildAndTest
jobs:
- job: SmokeTest
pool:
vmImage: 'ubuntu-latest'
steps:
- task: PowerShell@2
displayName: 'Run GraphWarden smoke test'
inputs:
targetType: 'inline'
script: |
# Verify the proxy responds before promoting the build.
$token = Invoke-RestMethod -Method Post `
-Uri "$(GRAPHWARDEN_PROXY_URL)/oauth2/v2.0/token" `
-Body @{
grant_type = 'client_credentials'
client_id = "$(GRAPHWARDEN_CLIENT_ID)"
client_secret = "$(GRAPHWARDEN_CLIENT_SECRET)"
scope = 'https://graph.microsoft.com/.default'
}
if (-not $token.access_token) {
Write-Error "Token acquisition failed against GraphWarden."
exit 1
}
Write-Host "GraphWarden smoke test passed."
env:
GRAPHWARDEN_PROXY_URL: $(GRAPHWARDEN_PROXY_URL)
GRAPHWARDEN_CLIENT_ID: $(GRAPHWARDEN_CLIENT_ID)
GRAPHWARDEN_CLIENT_SECRET: $(GRAPHWARDEN_CLIENT_SECRET)
Variable group setup
The pipeline reads three values from a variable group. Create it once per project.
- In Azure DevOps, open Pipelines > Library > + Variable group.
- Name the group
graphwarden-secrets(the name the pipeline references). - Add three variables:
GRAPHWARDEN_PROXY_URL,GRAPHWARDEN_CLIENT_ID,GRAPHWARDEN_CLIENT_SECRET. - Click the lock icon next to
GRAPHWARDEN_CLIENT_SECRETto mark it as secret. Azure DevOps masks secret values in logs and prevents them from being displayed after saving. - On the variable group page, open Pipeline permissions and grant access to the pipeline that runs
azure-pipelines.yml. - Save the group. The pipeline picks up the values on its next run.
The variable group is organization-scoped; multiple pipelines can reference the same group. For separate environments (staging, production), create one variable group per environment and use a conditional reference in variables: to select the correct group at run time.
Handling ruleset-denied calls in CI
A smoke test that calls real Graph endpoints through a real GraphWarden deployment can hit a ruleset denial (403 Forbidden with a ruleId in the response body). Ruleset denials are expected behaviour: the customer's security policy has blocked the call. A CI pipeline that treats every 403 as a silent success hides real misconfigurations.
The pattern that works:
- For the smoke test above, use the
/oauth2/v2.0/tokenendpoint rather than a Graph endpoint. Token acquisition does not require a specific rule; if it fails, the proxy is unreachable or the credentials are wrong — both are real build failures. - If you add Graph-endpoint assertions, record the exact endpoints and methods your pipeline exercises. Add a test that asserts a specific rule permits them. A flexible approach: let the smoke test call an endpoint, assert the response status is either
200or a known-expected403with a specificruleId. Anything else is a failure. continueOnError: truesilences denials but also silences network outages, token misconfiguration, and real regressions. Use it only when the denial is expected for the branch under test (for example, a pull-request pipeline that runs against a hardened production ruleset).
Troubleshooting
The pipeline reports Variable group 'graphwarden-secrets' could not be accessed
The variable group exists but the pipeline does not have permission to read it. Open the variable group in Pipelines > Library, click Pipeline permissions, and add the pipeline to the allow list. For classic pipelines not yet migrated to YAML, the UI asks for permission on the first run; click Permit in the pipeline summary.
dotnet restore fails with Unable to resolve host 'api.nuget.org'
The agent lacks outbound internet access. Microsoft-hosted agents always have internet; self-hosted agents need outbound TCP 443 to api.nuget.org, www.nuget.org, and your private NuGet feeds. If you run behind a corporate proxy, set the HTTP_PROXY and HTTPS_PROXY environment variables on the self-hosted agent service.
Invoke-RestMethod returns The remote server returned an error: (404) Not Found
The proxy URL in GRAPHWARDEN_PROXY_URL is wrong, or the token endpoint path is wrong. The pipeline uses /oauth2/v2.0/token which is the proxy's standard token path; confirm your GraphWarden deployment exposes the same path (the default) and that the URL includes the scheme (https://proxy.example.com, not proxy.example.com). A trailing slash on GRAPHWARDEN_PROXY_URL produces a double-slash path that some proxies reject.
Token acquisition failed against GraphWarden with an HTTP 401
The GRAPHWARDEN_CLIENT_ID and GRAPHWARDEN_CLIENT_SECRET pair is not the pair registered in the GraphWarden App Admin for this CI pipeline's App Identity. Rotate the credentials through GraphWarden Admin, update the variable group with the new values, and re-run the pipeline. Do not reuse App Identity credentials across environments.
The pipeline succeeds locally but fails on the hosted agent
The hosted agent cannot reach your on-premise proxy. ubuntu-latest runs in Microsoft's Azure infrastructure; an on-premise proxy behind a corporate firewall is not reachable from there. Either expose the proxy on a publicly routable URL (behind its own authentication and TLS) or switch to a self-hosted agent running inside your network.
Last reviewed: