Learn Azure DevOps Pipeline for Static Web Apps Deployment

Oct 22, 2026AzureDevOpsEngineering
Summarize with AI: Google AI Claude ChatGPT Perplexity Grok

AI links open with a title + excerpt (these tools can't fetch the page themselves) — use "Copy full article" to paste the complete text for a fuller summary.

Share:

Introduction

This article demonstrates how to deploy an Azure Static Web App — frontend and API together — from an Azure DevOps pipeline.

The whole pipeline is about 30 lines, and one task does the work. The interesting part is a bug we hit with the build output path, because it is not obvious and the error does not tell you the cause.

The Pipeline

trigger:
  branches:
    include:
      - master

pr:
  branches:
    include:
      - master

pool:
  vmImage: ubuntu-latest

variables:
  - group: swa-secrets

steps:
  - checkout: self
    submodules: true

  - task: NodeTool@0
    inputs:
      versionSpec: '20.x'
    displayName: 'Install Node.js'

  - task: AzureStaticWebApp@0
    inputs:
      app_location: 'frontend'
      api_location: 'api'
      output_location: 'dist/frontend/browser'
      app_build_command: 'npm run build -- --configuration production'
      api_build_version: '20'
      azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN)
    displayName: 'Build and deploy to Azure Static Web Apps'

Features

  • One task builds the frontend, builds the API and deploys both.
  • Pull requests get their own preview environment automatically.
  • No artifact publishing step, no separate release pipeline.

With the following steps, you can set up your own.

  1. Create the deployment token.
  2. Configure the three locations.
  3. Understand the pull request trigger.
  4. Get the output path right.

Create the Deployment Token

In the Azure Portal, open your Static Web App and go to Overview → Manage deployment token. Copy it.

Manage deployment token in the Azure Portal

In Azure DevOps, put it in a variable group — ours is swa-secrets — and mark it secret. Then reference the group in the pipeline.

variables:
  - group: swa-secrets

Warning: this token deploys to your site. Anyone who has it can replace your production app. Use a variable group with a secret variable, never a plain variables: entry in the YAML, and never echo it in a script step.

Configure the Three Locations

These three inputs cause most of the confusion, because they are relative to different things.

app_location: 'frontend' — the folder holding your frontend source, relative to the repository root. This is where the build command runs.

api_location: 'api' — the folder holding your Azure Functions, also relative to the repository root. The task builds it for you; there is no separate step.

output_location: 'dist/frontend/browser' — where the built files land, relative to app_location, not the repo root. That difference is the thing to hold on to.

api_build_version: '20' pins the Node version the Functions build uses, matching the engines field in api/package.json. Keep them the same or you will debug a version mismatch that only appears in CI.

Understand the Pull Request Trigger

pr:
  branches:
    include:
      - master

This is worth enabling. A PR targeting master gets its own preview environment — a full deployment at a temporary URL, with its own copy of the app and API.

So a reviewer clicks a link and uses the actual change. Not a screenshot, not a local checkout. The environment is removed when the PR closes.

One note on branches. Ours triggers on master, not main. If your deploys mysteriously never run, check that this matches your actual default branch name — we lost time to exactly that early on, and the pipeline reports nothing, because nothing triggered it.

Get the Output Path Right

Here is the bug worth the article.

Our Angular build produces dist/frontend/browser. That browser sub-folder is standard for modern Angular builds — the same layout used when there is a server bundle, kept even when there is not.

We also needed staticwebapp.config.json, which lives at the repository root, to end up inside that output folder so Static Web Apps picks up the routing and headers.

The first attempt chained it onto the build command.

app_build_command: 'npm run build -- --configuration production && cp ../staticwebapp.config.json dist/frontend/browser/staticwebapp.config.json'

That looks fine and it is fragile. It hardcodes the output path in a second place, it puts shell syntax into a YAML string, and it runs outside Angular's knowledge of its own build. When the artifact was not produced where the task expected, the deploy failed with a message about the missing output — which points at the deployment, not at the copy command that quietly created the mismatch.

The fix was to let Angular do it, as a build asset in angular.json.

{
  "glob": "staticwebapp.config.json",
  "input": "../"
}

And the build command goes back to being a build command.

app_build_command: 'npm run build -- --configuration production'

Now the config file is part of the build output by definition. If the output path ever changes, the file follows it, because Angular owns both.

The general lesson: when a build tool already has a way to include a file, use it. A cp in your CI command is a second source of truth for a path, and the two will disagree eventually — usually on a day when the error message is about something else entirely.

Watch the Run

A successful pipeline run in Azure DevOps

The task logs both builds and the upload. When something fails, read upward from the first red line rather than the last — the final error is usually a consequence of an earlier one, as it was here.

Conclusion

In this article we learned how to deploy an Angular frontend and an Azure Functions API to Azure Static Web Apps from one Azure DevOps task, how the three location inputs relate to each other, why pull request previews are worth enabling, and why copying a file with cp in the build command is worse than declaring it as a build asset.

Thirty lines of YAML, one task, and one path that has to be exactly right.

Reference

#azure-devops#ci-cd#azure-static-web-apps#angular#pipeline#yaml

Comments

Be the first to comment.

Leave a comment

Never shown publicly.

Comments are reviewed before appearing publicly.