Skip to content

[BUG]: AzureRmWebAppDeployment@4/@5 WarDeploy skips updated files when WAR entries have unchanged mtime and size #22176

@joechen0713

Description

@joechen0713

New issue checklist

Task name

AzureRmWebAppDeployment

Task version

4 and 5

Issue Description

Issue Description

Issue Summary

We observed a deployment inconsistency when deploying a WAR package to Azure App Service on Linux using the AzureRmWebAppDeployment task.

After investigation:

  • AzureRmWebAppDeployment@3 works as expected.
  • AzureRmWebAppDeployment@4 and AzureRmWebAppDeployment@5 do not reliably update all files.
  • The issue appears related to the deployment mechanism used by @4/@5, specifically the Kudu WarDeploy API.

This occurs when WAR packages are built with reproducible or normalized timestamps (e.g., 1980-02-01T08:00:00+00:00).


Environment

  • Azure App Service:
    • Linux
    • Tomcat 11
    • Java 21
  • Azure DevOps task:
    • Affected: AzureRmWebAppDeployment@4, AzureRmWebAppDeployment@5
    • Working: AzureRmWebAppDeployment@3
  • Package type:
    • WAR

Symptom

After deploying a WAR package using AzureRmWebAppDeployment@4 or @5:

  • index.html is not updated
  • Hashed assets (e.g., main-<hash>.js) are updated correctly

The same deployment works correctly with AzureRmWebAppDeployment@3.


Observed Behavior

AzureRmWebAppDeployment@5 deploys WAR packages via Kudu:

POST /api/wardeploy?isAsync=true&name=ROOT → 202

During deployment:

  • The WAR is extracted to a temporary directory
  • Files are synchronized to:
    /home/site/wwwroot/webapps/ROOT
    
  • Synchronization appears to use rsync

Observed result:

Number of regular files transferred: 0
Total transferred file size: 0 bytes

This indicates no files were updated, even though the WAR contains new content.


Root Cause Analysis

The issue is caused by how rsync determines file changes.

By default, rsync compares:

  • File size
  • Modification timestamp (mtime)

It does not compare file content.

In this case:

  • WAR files are built with normalized timestamps (e.g., 1980-02-01T08:00:00)
  • Existing deployed files also have the same mtime

For a file like index.html:

Attribute Result
mtime same
size same
content different

Because both mtime and size match, rsync skips the file.


Why Hashed Files Still Update

Files like:

main-<hash>.js

are updated because:

  • Filename changes between builds
  • File does not exist in the destination

Therefore, rsync writes the new file regardless of timestamp.


Why AzureRmWebAppDeployment@3 Works

AzureRmWebAppDeployment@3 uses a different deployment API:

PUT /api/zip/site/wwwroot/webapps/ROOT/ → 200

This method:

  • Extracts files directly into the target directory
  • Overwrites existing files unconditionally
  • Does not rely on mtime/size comparison

As a result, all files (including index.html) are correctly updated.


Expected Behavior

WAR deployment should update files when content changes, even if:

  • File name is unchanged
  • File size is unchanged
  • Timestamp (mtime) is unchanged due to reproducible builds

Actual Behavior

Files may be skipped during deployment when:

  • mtime is unchanged
  • file size is unchanged

Resulting in stale content being served after deployment.


Impact

This can lead to partially updated deployments for Java applications, especially when using:

  • Gradle or Maven reproducible builds
  • Normalized archive timestamps

The deployment reports success, but application content is inconsistent.


Workarounds

  • Ensure file timestamps (mtime) change between builds
  • Use AzureRmWebAppDeployment@3 instead of @4/@5

Suggested Improvements

Please consider improving WAR deployment behavior in @4/@5:

  1. Use checksum-based comparison (rsync --checksum)
  2. Provide an option to force overwrite
  3. Allow disabling rsync optimization
  4. Align behavior with @3 (ZIP extract overwrite)
  5. Document this limitation for reproducible WAR builds

Conclusion

The issue occurs because AzureRmWebAppDeployment@4/@5 relies on WarDeploy (rsync), which compares file size and mtime but not content.

When WAR files are built with normalized timestamps, changed files (e.g., index.html) may be skipped during deployment, resulting in stale application content.


Environment type (Please select at least one enviroment where you face this issue)

  • Self-Hosted
  • Microsoft Hosted
  • VMSS Pool
  • Container

Azure DevOps Server type

dev.azure.com (formerly visualstudio.com)

Azure DevOps Server Version (if applicable)

No response

Operation system

Ubuntu

Relevant log output

No error but we can see the following in the log even we did have change in the index.html:
Number of regular files transferred: 0
Total transferred file size: 0 bytes

Full task logs with system.debug enabled

No response

Repro steps

We can reproduce the issue using the following task by modifying the content of index.html while keeping the file size unchanged. After building the WAR package with Gradle (with reproducible builds enabled) and deploying it, we can observe that index.html remains unchanged and is not updated.

steps:
- task: AzureRmWebAppDeployment@4
  displayName: 'Azure App Service Deploy: xxx'
  inputs:
    azureSubscription: 'xxx'
    appType: webAppLinux
    WebAppName: 'xxx'
    packageForLinux: '$(System.DefaultWorkingDirectory)/_xxx/xxx-artifact/build/libs/ROOT.war'

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions