Azure Web Apps or App Services are quite flexible regarding deployment. You can deploy via FTP, OneDrive or Dropbox, different cloud-based source controls like VSTS, GitHub, or BitBucket, your on-premise Git, multiples IDEs including Visual Studio, Eclipse and Xcode, and using MSBuild via Web Deploy or FTP/FTPs. And this list is very likely to keep expanding.

However, there might be some scenarios where you just need to update some reference files and don’t need to build or update the whole solution. Additionally, it’s quite common that corporate firewalls restrictions leave you with only the HTTP or HTTPs ports open to interact with your Azure App Service. I had such a scenario where we had to automate the deployment of new public keys to an Azure App Service to support client certificate-based authentication. However, we were restricted by policies and firewalls.

The Kudu REST API provides a lot of handy features which support Azure App Services source code management and deployments operations, among others. One of these is the Virtual File System (VFS) API. This API is based on the VFS HTTP Adapter which wraps a VFS instance as an HTTP RESTful interface. The Kudu VFS API allows us to upload and download files, get a list of files in a directory, create directories, and delete files from the virtual file system of an Azure App Service; and we can use PowerShell to call it.

In this post I will show how to interact with the Azure App Service Virtual File Sytem (VFS) API via PowerShell.

Authenticating to the Kudu API.

To call any of the Kudu APIs, we need to authenticate by adding the corresponding Authorization header. To create the header value, we need to use the Kudu API credentials, as detailed here. Because we will be interacting with an API related to an App Service, we will be using site-level credentials (a.k.a. publishing profile credentials).

Getting the Publishing Profile Credentials from the Azure Portal

You can get the publishing profile credentials, by downloading the publishing profile from the portal, as shown in the figure below. Once downloaded, the XML document will contain the site-level credentials.

Getting the Publishing Profile Credentials via PowerShell

We can also get the site-level credentials via PowerShell. I’ve created a PowerShell function which returns the publishing credentials of an Azure App Service or a Deployment Slot, as shown below.

Bear in mind that you need to be logged in to Azure in your PowerShell session before calling these cmdlets.

Getting the Kudu REST API Authorisation header via PowerShell

Once we have the credentials, we are able to get the Authorization header value. The instructions to construct the header are described here. I’ve created another PowerShell function, which relies on the previous one, to get the header value, as follows.

Calling the App Service VFS API

Once we have the Authorization header, we are ready to call the VFS API. As shown in the documentation, the VFS API has the following operations:

  • GET /api/vfs/{path}    (Gets a file at path)
  • GET /api/vfs/{path}/    (Lists files at directory specified by path)
  • PUT /api/vfs/{path}    (Puts a file at path)
  • PUT /api/vfs/{path}/    (Creates a directory at path)
  • DELETE /api/vfs/{path}    (Delete the file at path)

So the URI to call the API would be something like:

  • GET https://{webAppName}

To invoke the REST API operation via PowerShell we will use the Invoke-RestMethod cmdlet.

We have to bear in mind that when trying to overwrite or delete a file, the web server implements ETag behaviour to identify specific versions of files.

Uploading a File to an App Service

I have created the PowerShell function shown below which uploads a local file to a path in the virtual file system. To call this function you need to provide the App Service name, the Kudu credentials (username and password), the local path of your file and the kudu path. The function assumes that you want to upload the file under the wwwroot folder, but you can change it if needed.

As you can see in the script, we are adding the “If-Match”=”*” header to disable ETag version check on the server side.

Downloading a File from an App Service

Similarly, I have created a function to download a file on an App Service to the local file system via PowerShell.

Using the ZIP API

In addition to using the VFS API, we can also use the Kudu ZIP Api, which allows to upload zip files and expand them into folders, and compress server folders as zip files and download them.

  • GET /api/zip/{path}    (Zip up and download the specified folder)
  • PUT /api/zip/{path}    (Upload a zip file which gets expanded into the specified folder)

You could create your own PowerShell functions to interact with the ZIP API based on what we have previously shown.


As we have seen, in addition to the multiple deployment options we have for Azure App Services, we can also use the Kudu VFS API to interact with the App Service Virtual File System via HTTP. I have shared some functions for some of the provided operations. You could customise these functions or create your own based on your needs.

I hope this has been of help and feel free to add your comments or queries below. 🙂

Application Development and Integration, Azure Platform, PowerShell
, , , ,

Join the conversation! 11 Comments

  1. Reblogged this on Sprouting Bits and commented:

    Reblogging this post to my personal archive 🙂

  2. Hi,

    I want to have email notification when WebJob stops or terminated with any error, can it be done in Azure (without using Zapier)?,

    I noticed in Web test result panel that it always redirects to login portal and give success response all the time.

    How I can add credentials to my Web Test URL?

    Can you please assist on this step by step?, Thanks.


  3. Great post has helped a great deal. Just a small issue that one of your function is named wrong? you have a script for a function Get-KuduApiAuthorisationHeaderValue but when you use it you specify Get-KuduApiAuthorisationToken but with username and password,which don’t need specifying, not resourcegroup and webappname ?

    • Hey Jim, thanks your comments and for pointing this out. I believe I updated one of my gists without checking the others. I have updated them accordingly. Now you don’t need to specify the credentials. 🙂

  4. Great article, it really helped me.
    One question, If I try upload a file to a slot that is stopped, I get “Error 403 – This web app is stopped”. Is there any way I can copy files to a staging slot?

  5. This is just the one i was looking for the, great article but small issue though what would pass for kudupath ?

  6. Im also getting an error dnt have access to my local path

  7. All working perfectly now. I have to specify the downloading file with the extension.

  8. This is so awesome. Nice work

  9. Thank you very much for this explanation. I could use a lot of it.
    By now i have one big issue:
    When i want to download a zip it gets me a html object instead of the zip. When i open it in the browser, i can download it but i need to have it directly in the powershell script. Do you know what i’m doing wrong?
    The call looks likt this:
    $Zip = Invoke-RestMethod -Uri $UrlZipDownload -Headers @{“Authorization”=$KuduApiAuthorisationHeaderSource;”If-Match”=”*”} -Method GET -ContentType “application/zip”

Comments are closed.