name: ewl-azure-pipelines description: Azure Pipelines CI/CD for EWL systems including build templates, deploy templates, and pipeline code generation
Overview
EWL generates Azure Pipelines YAML for client systems. The build pipeline is fully generated. The deploy pipeline uses a generated template with per-installation pipeline files that have a custom region for hand-maintained overrides.
File Locations
EWL Repository
Library/Files/Azure Pipeline Templates/Build.yml-- build pipeline template (contains@@placeholders replaced by the DU)Library/Files/Azure Pipeline Templates/Deploy.yml-- deploy job template (parameters + jobs only; no trigger or resources)Development Utility/Operations/UpdateDependentLogic.cs-- C# code generator for per-installation deploy pipeline files and the deploy job template copy
Client System (Generated)
src/Library/Configuration/Azure Build Pipeline.yml-- generated build pipelinesrc/Library/Configuration/Azure Deploy Job.yml-- generated deploy job template (copy of Deploy.yml with placeholders resolved)src/Library/Configuration/Installation/Installations/{Name}/Azure Deploy Pipeline.yml-- per-installation deploy pipeline (generated region + custom region)
Deploy Pipeline Architecture
Each installation gets its own Azure Deploy Pipeline.yml with two regions:
# Generated region (above marker) -- regenerated by the DU
trigger: none
resources:
pipelines:
- pipeline: build
source: Build
trigger:
enabled: true # true for intermediate, false for live
branches:
include:
- Integration # Integration for non-prod, master for staging/live
extends:
template: ../../../Azure Deploy Job.yml
parameters:
installationName: 'Testing'
resourceGroup: 'rg-systemshortname-intermediate'
appService: 'app-systemshortname-testing'
# END-EWL-REGION
# Custom region (below marker) -- hand-maintained per installation
resourceGroupOverride: 'ClientHosting'
appServiceOverride: 'asas-portals'
deploymentSlot: 'testing-new'
Everything above # END-EWL-REGION is regenerated. Everything below is
preserved across DU runs.
Override Pattern
Generated defaults can be overridden in the custom region. Override parameters
have a default of '' in the template. The resolve step in Deploy.yml picks
the override if non-empty, otherwise uses the generated value.
Exceptions:
deploymentSlothas no override -- it is specified directly in the custom region when needed (default''means no slot)serviceConnectionhas no override -- simple parameter with hardcoded default of'Azure'
Critical Constraints
AzureWebApp@1 Deployment Method
The AzureWebApp@1 task's default deployment method (auto) resolves to
Run From Package for Windows web apps. This mounts the zip as a read-only
virtual filesystem and sets WEBSITE_RUN_FROM_PACKAGE=1, making the app's
wwwroot read-only.
Always use deploymentMethod: 'zipDeploy' to extract files to disk
instead. The zipDeploy method also auto-cleans WEBSITE_RUN_FROM_PACKAGE and
WEBSITE_RUN_FROM_ZIP app settings before deploying.
Template Expressions in Resources Blocks
Template expressions (${{ }}) are completely blocked inside the resources
block of an extends template. This means:
triggeron pipeline resources cannot be parameterized from within the extends template- The
enabledproperty undertriggerdoes NOT accept template expressions - Branch filtering in
trigger.branches.includedoes NOT accept template expressions
Therefore, trigger:, resources:, and branch filtering must live in the
per-installation pipeline file (top-level), not in the shared deploy job
template.
Pipeline Resource Trigger Default Branch
Pipeline resource triggers are only evaluated from the default branch of
the deploy pipeline repository. The YAML with trigger config must exist on
that branch (typically master) for auto-triggering to work.
Artifact Download Across Pipelines
DownloadPipelineArtifact@2 with buildType: current only works within the
same pipeline. Since the deploy is a separate pipeline from the build, use:
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'specific'
project: '$(System.TeamProjectId)'
definition: '$(resources.pipeline.build.pipelineID)'
specificBuildWithTriggering: true
Note: resources.pipeline.build.pipelineID is a runtime value -- use
$(...) syntax, not ${{ }} template expressions.
Naming Conventions
- App service names:
app-{systemShortNameSlug}-{installationShortNameSlug} - Resource groups:
rg-{systemShortNameSlug}-{installationType}where installationType isprodfor Live,intermediatefor others - Slugs are generated using
ToUrlSlug()from Tewl'sStringTools
Build Pipeline
The build template is fully generated and includes:
- .NET SDK install
- NuGet restore
- EWL DU dotnet-tool restore
update-datasync- Second restore (for generated Directory.Build.props)
export-logic- Artifact publishing (per Logic Packages subfolder)
Deploy Job Template
The deploy job template (Deploy.yml) contains:
- Parameter resolution (override pattern)
- Download server-side logic artifact
- Download installation configuration artifact
- Stop app (
az webapp stop) - Build and push the installation container image and update the Container App Job image
- Upload the ISU installation package to blob storage
- Upload logic (
AzureWebApp@1withzipDeploy) - Upload the IIS transform via Kudu VFS
- Run data migrator by starting the installation Container App Job and polling for completion
- Start app (
az webapp start)
Kudu VFS Notes
The deploy template still uses the Kudu VFS API to upload
site/applicationHost.xdt after deployment so IIS modules can be removed.