10.2 Binary, Web App, Function & Scripted Deployments

Key Takeaways

  • Deploying web applications and Azure Functions using 'WEBSITE_RUN_FROM_PACKAGE=1' mounts an immutable zip archive directly as the read-only wwwroot directory, completely eliminating file locks and speeding up cold starts.
  • YAML pipelines manage IaaS virtual machine deployments using Environment VM resources and rolling batch strategies, superseding legacy Classic Release Pipeline Deployment Groups.
  • The Azure Pipelines agent installed on target VMs registers to Environment pools, leveraging custom tags (e.g., 'role: web') to direct rolling script execution across defined server batches.
  • Scripted post-deployment automation using AzureCLI@2 and AzurePowerShell@5 must enforce cross-platform runtime execution ('pwsh: true') and strict failure trapping ('failOnStandardError: true').
  • Deployment scripts must be strictly designed for idempotency, ensuring repeated pipeline runs achieve the identical desired end state without creating duplicate resources or corrupting configurations.
Last updated: September 2026

10.2 Binary, Web App, Function & Scripted Deployments

While containerization dominates cloud-native architectures, enterprise delivery pipelines frequently manage compiled binary artifacts, Platform-as-a-Service (PaaS) web workloads, serverless functions, and traditional Infrastructure-as-a-Service (IaaS) virtual machines. Achieving zero downtime across these diverse hosting models requires specialized deployment mechanics: eliminating runtime file locks on compiled assemblies, coordinating rolling script updates across virtual machine fleets, and constructing idempotent automation scripts.

For the AZ-400 exam, candidates must master the internal mechanics of Zip Deploy and Run From Package, transition legacy Deployment Groups into modern YAML Environment VM resources, and author resilient, cross-platform deployment automation scripts using Azure CLI and PowerShell Core.


1. Deploying Web Apps & Azure Functions: Zip Deploy vs. Run From Package

Deploying application code to Azure App Service and Azure Functions has evolved through several technical paradigms. Early methods—such as FTP, WebDeploy (MSDeploy), and continuous Kudu git pushes—relied on copying individual files into the web root directory (/home/site/wwwroot on Linux or D:\home\site\wwwroot on Windows). In enterprise production environments, this file-by-file copy introduces severe failure vectors:

  • File Locking (DLL in use): If an active worker process has compiled binaries or native assemblies open in memory, overwriting them during deployment generates an ERROR_FILE_IN_USE or ERROR_ACCESS_DENIED exception, causing corrupted deployments.
  • Inconsistent Intermediate State: During a prolonged file copy, client requests hitting the server execute a hybrid mix of old and new assemblies, resulting in runtime crashes.
  • Deployment Leftovers: Deleting obsolete files requires manual synchronization. Obsolete assemblies left behind can introduce security vulnerabilities or unintended dependency resolution.

The Solution: Run From Package (WEBSITE_RUN_FROM_PACKAGE=1)

To resolve these challenges, Microsoft introduced Run From Package, enabled by setting the application setting WEBSITE_RUN_FROM_PACKAGE = 1 (or setting it to a secure Azure Blob Storage shared access signature URL).

┌────────────────────────────────────────────────────────────────────────┐
│                     Azure App Service / Function App                   │
│                                                                        │
│  [Application Setting: WEBSITE_RUN_FROM_PACKAGE = 1]                   │
│                                                                        │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ Storage / SitePackages:                                           │  │
│  │   20260905-142010.zip (Immutable Package Archive)                │  │
│  └─────────────────────────────────┬────────────────────────────────┘  │
│                                    │                                   │
│                     (Mounted as Virtual Read-Only FS)                  │
│                                    ▼                                   │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │ /home/site/wwwroot (or D:\home\site\wwwroot)                      │  │
│  │ • Read-Only File System                                          │  │
│  │ • Zero File Locks / Atomic Pointer Swap                          │  │
│  │ • Pre-Indexed for Ultra-Fast JIT Assembly Loading               │  │
│  └──────────────────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────────────────┘

Architectural Mechanics & Benefits

  1. Atomic Mount Operation: Instead of extracting thousands of individual files onto the local disk, Azure mounts the zip file directly as the virtual wwwroot directory. The deployment completes as an atomic pointer swap.
  2. Elimination of File Locks: Because the mounted filesystem is strictly read-only, worker processes cannot lock files in a way that blocks subsequent deployments. A new zip file is uploaded alongside the old one, and Azure updates the mount point seamlessly.
  3. Drastic Cold Start Reduction: In serverless Azure Functions and auto-scaled App Services, loading hundreds of loose files across remote storage shares causes substantial disk I/O latency. Reading from an indexed, immutable package eliminates file traversal latency, accelerating cold start initialization by up to 50%.
  4. Complete Artifact Integrity: The application running in production is guaranteed to match the exact byte-for-byte binary package generated by the CI build pipeline, preventing configuration drift.

[!CAUTION] Because wwwroot becomes strictly read-only under WEBSITE_RUN_FROM_PACKAGE=1, any legacy code that attempts to write log files, cache data, or upload temp files directly into wwwroot will throw runtime UnauthorizedAccessException or IOException errors. All dynamic runtime writes must be directed to %TEMP% or /tmp.

Pipeline Implementation: AzureWebApp@1 and AzureFunctionApp@1

# Deploying an ASP.NET Core Web App using Run-From-Package
- task: AzureWebApp@1
  displayName: 'Deploy Web App to Production'
  inputs:
    azureSubscription: 'ContosoARMConnection'
    appType: 'webApp'
    appName: 'app-contoso-core'
    package: '$(Pipeline.Workspace)/drop/**/*.zip'
    deploymentMethod: 'runFromPackage'

# Deploying an Azure Function App
- task: AzureFunctionApp@1
  displayName: 'Deploy Order Processing Function'
  inputs:
    azureSubscription: 'ContosoARMConnection'
    appType: 'functionApp'
    appName: 'func-order-processor'
    package: '$(Pipeline.Workspace)/drop/func-order.zip'
    deploymentMethod: 'runFromPackage'

2. Virtual Machine Deployments: Deployment Groups vs. Environment VM Resources

Enterprise architectures frequently maintain persistent Infrastructure-as-a-Service (IaaS) virtual machines running Linux (RHEL, Ubuntu) or Windows Server for legacy frameworks, specialized third-party software, or high-compliance workloads.

Evolution from Classic Deployment Groups to YAML Environments

  • Classic Release Pipelines (Deployment Groups): In the legacy Classic UI, administrators provisioned a Deployment Group within the Azure DevOps project. Running a registration script on each VM installed the Azure Pipelines agent and registered the machine into the group with metadata tags (e.g., web, db, eastus). While functional, Classic Deployment Groups lack Git versioning, pull request validation, and branch isolation.
  • Modern YAML Pipelines (Environments & VM Resources): In YAML pipelines, deployment targets are represented as Environments. An Environment can contain Kubernetes clusters or Virtual Machine resources. The Azure Pipelines agent installed on the VM registers directly to the environment, enabling true Configuration-as-Code delivery.
   ┌───────────────────────────────────────────────────────────────────────┐
   │                   Azure DevOps YAML Pipeline                          │
   │                   Environment: 'production-vms'                       │
   └───────────────────────────────────┬───────────────────────────────────┘
                                       │ (Rolling Strategy: maxParallel: 2)
         ┌─────────────────────────────┴─────────────────────────────┐
         ▼                                                           ▼
┌───────────────────────────────┐           ┌───────────────────────────────┐
│ VM Resource: 'vm-web-01'      │           │ VM Resource: 'vm-web-02'      │
│ • Agent Pool: production-vms  │           │ • Agent Pool: production-vms  │
│ • Tags: [role: web, zone: A]  │           │ • Tags: [role: web, zone: B]  │
│ • Status: Updating Batch 1    │           │ • Status: Updating Batch 1    │
└───────────────────────────────┘           └───────────────────────────────┘
         │                                                           │
         ▼                                                           ▼
┌───────────────────────────────┐           ┌───────────────────────────────┐
│ VM Resource: 'vm-web-03'      │           │ VM Resource: 'vm-web-04'      │
│ • Agent Pool: production-vms  │           │ • Agent Pool: production-vms  │
│ • Tags: [role: web, zone: A]  │           │ • Tags: [role: web, zone: B]  │
│ • Status: Queued for Batch 2  │           │ • Status: Queued for Batch 2  │
└───────────────────────────────┘           └───────────────────────────────┘

Registering VM Resources into an Environment

To onboard a target VM into an Environment:

  1. In Azure DevOps, navigate to Pipelines > Environments and create an environment (e.g., production-vms).
  2. Select Add resource > Virtual machines, selecting the target operating system (Linux or Windows).
  3. Azure DevOps generates a registration script containing a personal access token (PAT) and organization URL.
  4. Execute the script on the target virtual machine with elevated privileges (sudo or Administrator PowerShell). The script downloads and configures the vsts-agent as a local system service.
  5. Assign metadata tags (e.g., role: web, tier: frontend, datacenter: eastus) directly via the Azure DevOps portal or the registration script.

The Rolling Deployment Strategy in YAML

The YAML strategy: rolling block controls progressive deployment execution across the registered VM fleet. Key parameters and lifecycle hooks include:

  • maxParallel: The number or percentage of virtual machines that can be updated simultaneously (e.g., maxParallel: 2 or maxParallel: 25%).
  • Lifecycle Hooks:
    • preDeploy: Executes before the update starts (e.g., taking the VM out of the Azure Load Balancer backend pool).
    • deploy: Steps that download artifacts, stop services, copy binaries, and start new application services.
    • routeTraffic: Steps to re-attach the VM to the load balancer pool.
    • postRouteTraffic: Executes automated health probes to verify application responsiveness under live traffic.
    • on: failure: Executes remediation or alerts if any step in the lifecycle fails.
jobs:
- deployment: DeployWebFleet
  displayName: 'Rolling Deployment to Web VM Fleet'
  pool:
    vmImage: 'ubuntu-latest'
  environment:
    name: 'production-vms'
    resourceType: VirtualMachine
    tags: 'role: web'
  strategy:
    rolling:
      maxParallel: 2
      preDeploy:
        steps:
        - script: echo 'Draining connections from load balancer...'
          displayName: 'Drain Traffic'
      deploy:
        steps:
        - download: current
          artifact: drop
        - script: |
            sudo systemctl stop contoso-service
            sudo cp -r $(Pipeline.Workspace)/drop/* /var/www/html/
            sudo systemctl start contoso-service
          displayName: 'Update Binaries and Restart Service'
      postRouteTraffic:
        steps:
        - script: |
            curl --fail http://localhost:80/health || exit 1
          displayName: 'Local Smoke Test Health Probe'

3. Scripted Post-Deployment Operations & Automation Tasks

Automated pipelines require post-deployment scripting to execute database seed routines, purge Content Delivery Network (CDN) caches, update routing rules, and register API endpoints. Azure Pipelines provides two primary tasks for executing cloud-management scripts:

AzureCLI@2 vs. AzurePowerShell@5

AttributeAzureCLI@2AzurePowerShell@5
Underlying EngineAzure Command-Line Interface (az)Azure PowerShell Az Modules (Get-AzResource)
Script Typesbash, pscore, batch, pspscore (PowerShell Core), ps (Desktop PS)
Cross-PlatformNative across Linux, macOS, and WindowsNative when configured with pwsh: true
AuthenticationAutomatic via ARM Service ConnectionAutomatic via ARM Service Connection
Best FitCloud-native CLI automation, bash scriptsDeep .NET scripting, complex object pipelines

Mission-Critical Pipeline Properties: pwsh: true and failOnStandardError: true

Two configuration settings are frequently tested on the AZ-400 exam because their omission leads to silent pipeline failures:

  1. pwsh: true: Specifies that the script must run using PowerShell Core (pwsh) rather than Windows PowerShell (powershell.exe). This is mandatory when pipelines run on Microsoft-hosted Linux agents (ubuntu-latest) or self-hosted Linux containers. If pwsh: true is omitted on a Linux agent, the pipeline attempts to invoke powershell.exe and fails immediately.
  2. failOnStandardError: true: By default, Azure Pipelines determines step success based on the process exit code (exit 0). However, many command-line utilities and legacy tools write fatal error descriptions to stderr while exiting with code 0. Enabling failOnStandardError: true instructs the task runner to treat any data emitted to the standard error stream as an immediate task failure.
- task: AzureCLI@2
  displayName: 'Purge Front Door Cache Post-Deploy'
  inputs:
    azureSubscription: 'ContosoServiceConnection'
    scriptType: 'pscore'
    scriptLocation: 'inlineScript'
    failOnStandardError: true
    inlineScript: |
      $ErrorActionPreference = 'Stop'
      Write-Host 'Purging Azure Front Door edge caches...'
      az afd endpoint purge \
        --resource-group rg-contoso-network \
        --profile-name afd-contoso-global \
        --endpoint-name ep-contoso-prod \
        --content-paths '/*'

Engineering for Idempotency in Deployment Scripts

An automated deployment script is idempotent if running it multiple times produces the exact same end state without error, unintended side effects, or duplicate resource generation. Because CI/CD pipelines can be retried following transient network drops or agent timeouts, non-idempotent scripts cause catastrophic environment corruption.

  • Anti-Pattern (Non-Idempotent):
    # FAILS on rerun: Throws ResourceExists error
    az storage container create --name static-assets --account-name contosostorage
    # FAILS on rerun: Appends duplicate configuration lines every run
    echo 'export FEATURE_FLAG=true' >> /etc/environment
    
  • Idempotent Best Practice:
    # Safe on rerun: Checks existence or uses declarative flags
    if ! az storage container show --name static-assets --account-name contosostorage > /dev/null 2>&1; then
      az storage container create --name static-assets --account-name contosostorage
    fi
    # Safe on rerun: Uses sed or grep to replace or insert once
    grep -qxF 'export FEATURE_FLAG=true' /etc/environment || echo 'export FEATURE_FLAG=true' >> /etc/environment
    

4. Comparison Table: Deployment Methodologies

Deployment MechanismTarget PlatformFile Lock SafetyCold Start ImpactRollback CapabilityBest Fit Workloads
Run From PackageApp Service / Functions100% Safe (Read-only mount)Fastest (Pre-indexed)Instant (Swap package pointer)C#, Java, Node.js web APIs & Functions
Zip DeployApp Service / FunctionsVulnerable if app activeModerate (Disk extraction)Requires re-uploading zipSmall scripts, PHP, Python apps
Environment VM (Rolling)IaaS Virtual MachinesManaged via service stopDependent on OS servicePhased re-deploymentLegacy Windows/Linux system services
Containerized DeployAKS / ACA / Web App100% Safe (Container layer)Fast (Cached image layers)Instant (Revision/pod switch)Microservices, Dockerized architectures

5. Realistic Exam Scenario & Common Traps

Scenario: Global Retail Web App Locked DLL Outages

Organization: Tailspin Toys hosts their primary e-commerce web platform on an Azure App Service Premium v3 plan running on Windows. Deployments run via an Azure Pipelines YAML pipeline utilizing the AzureWebApp@1 task.

  • Problem: During high-volume holiday sales periods, CI/CD deployments intermittently fail with ERROR_FILE_IN_USE: The process cannot access the file 'Tailspin.Commerce.dll' because it is being used by another process. When this happens, customer checkouts stall for 5–10 minutes while engineers manually restart the App Service.
  • Requirement: Modernize the deployment pipeline to ensure zero file locks during deployment, reduce application cold-start initialization latency, and ensure that post-deployment cache invalidation scripts written in PowerShell Core execute reliably on both Windows and Linux pipeline build agents.

DevOps Solution:

  1. Update the App Service configuration to set WEBSITE_RUN_FROM_PACKAGE = 1.
  2. Configure the AzureWebApp@1 task with deploymentMethod: 'runFromPackage'.
  3. Verify the application writes temporary session cache files to %TEMP% rather than wwwroot.
  4. Update post-deployment cache purge tasks to use AzurePowerShell@5 with pwsh: true and failOnStandardError: true.

Common Exam Traps to Avoid

  • Trap: Confusing Classic Deployment Groups with YAML Environments. Deployment Groups belong strictly to the Classic UI Release Pipelines. When designing modern YAML multi-stage pipelines, the correct resource is an Environment containing Virtual Machine resources.
  • Trap: Forgetting that Run-From-Package renders wwwroot read-only. If an exam scenario states that an application needs to generate log files or write dynamic image uploads into wwwroot, WEBSITE_RUN_FROM_PACKAGE=1 will fail unless the code is modified to store uploads in Azure Blob Storage or write temp data to %TEMP%.
  • Trap: Omitting pwsh: true when running PowerShell tasks on Linux agents. Windows PowerShell (powershell.exe) does not exist on Linux. Always configure pwsh: true (PowerShell Core) for cross-platform agent compatibility.
Loading diagram...
Immutable Run-From-Package and Environment VM Rolling Architecture
Test Your Knowledge

A company hosts an ASP.NET Core web application on Azure App Service. During frequent continuous deployment cycles, developers report that the deployment task fails intermittently with file access errors indicating that specific application DLLs are currently locked by the running w3wp.exe process. Furthermore, scaling out new instances during high load suffers from slow cold-start initialization times. Which configuration should the DevOps engineer implement?

A
B
C
D
Test Your Knowledge

A DevOps team is converting an existing Classic Release Pipeline into an Azure Pipelines multi-stage YAML pipeline. The deployment targets a fleet of 20 load-balanced Ubuntu virtual machines hosted across multiple Azure Availability Zones. The deployment must execute progressively across the virtual machine fleet, updating no more than two virtual machines at a time while running local verification tests before moving to the next batch. How should the team configure this in the YAML pipeline?

A
B
C
D
Test Your Knowledge

An automation engineer authors a post-deployment script inside an Azure Pipelines YAML pipeline to flush an Azure Front Door CDN profile and configure Redis cache keys. The pipeline executes on Microsoft-hosted Ubuntu Linux agents. During execution, the Azure CLI command writes a severe warning to standard error, but the pipeline step reports green success and continues, resulting in an un-invalidated cache in production. Furthermore, if the pipeline is retried, the script crashes because a Redis key already exists. What two modifications must be made?

A
B
C
D