Copy files to Azure Web App with PowerShell and Kudu API

Imagine a smooth running Azure Web site and you want to add some new files to it. I am not talking about new code, so no new version of the web app to deploy. No, think about additional resource or content files. In my case departments around the world are onboarding and using the web app. They also provide translations to have the UI in their language and culture. These files are stored and managed from a central location. From this location all existing translation files are copied to the Azure Web app.

Kudu

Let me introduce Kudu. Kudu is the engine behind git deployments in Azure Web Sites. From the Azure Portal you can access it to navigate to your Web App and select Advanced Tools from the Development Tools.

image

 

If your web app is called ‘ocha-make-dev’, then Kudu runs in the associated ‘Service Control Manager (scm)’ site:

image

 

It has also a set of REST APIs available to use for your custom scenario’s to interact with your Azure Web App. The one I am going to use is the Files API or to be precise the Virtual File System API.

Authentication

Before we can use the REST APIs authentication needs to take place. There are 2 kind of credentials:

  • Deployment Credentials
    These credentials are provided by yourself.
    image
  • Publish Profile Credentials
    These credentials are created automatically for each web site. You can get your hands on these credentials by downloading the publish profile.
    image

The PowerShell script

For the script I am going to use the Publish Profile credentials. To get from PowerShell you must run the script with an account that has all the required permissions to access the Azure Web App. For demo purposes I am using an account that is owner of the Resource Group where the web app exists.

image

Let’s start with the authentication process. I want to get the publish profile credentials and use that to generate the required Authorization Header when calling the Kudu API later. Take a look at the following 2 functions.

function Get-PublishingProfileCredentials($resourceGroupName, $webAppName){

    $resourceType = "Microsoft.Web/sites/config"
    $resourceName = "$webAppName/publishingcredentials"

    $publishingCredentials = Invoke-AzureRmResourceAction -ResourceGroupName $resourceGroupName -ResourceType $resourceType -ResourceName $resourceName -Action list -ApiVersion 2015-08-01 -Force

       return $publishingCredentials
}

function Get-KuduApiAuthorisationHeaderValue($resourceGroupName, $webAppName){

    $publishingCredentials = Get-PublishingProfileCredentials $resourceGroupName $webAppName

    return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword))))
}

The first function retrieves the publishing credential and the second function creates the Authorization Header to be used when doing the REST call.

Now we have the authorization information we can call the Kudu API for upload a file. Take a look at the following function:

function Upload-FileToWebApp($kuduApiAuthorisationToken, $webAppName, $fileName, $localPath ){

    $kuduApiUrl = "https://$webAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/app/resources/$fileName"
    
    $result = Invoke-RestMethod -Uri $kuduApiUrl `
                        -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                        -Method PUT `
                        -InFile $localPath `
                        -ContentType "multipart/form-data"
}

To interact with the Kudu file system we can use the VFS api. VFS stands for Virtual File System. It reflects the same file system structure as shown in the Azure Portal.

With the Invoke-RestMethod we call the API url and provide that authorization Header and a reference to a file to upload (the InFile atrtribute). This file must exist on your local system.

From the main script flow we can have a script that starts it all:

$localFiles = Get-ChildItem $tempFolder

$accessToken = Get-KuduApiAuthorisationHeaderValue $resourceGroupName $webappName
    
$localFiles | % {
    Write-Host "Uploading $($_.Name)" -NoNewline
    Upload-FileToWebApp $accessToken $webappName $_.Name $_.FullName 
    Write-Host -f Green " [Done]"
}

A complete script can be downloaded from my Assets page. Have fun and good luck!

Articles for reference that I used for creating this solution:

Share