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.
If your web app is called ‘ocha-make-dev’, then Kudu runs in the associated ‘Service Control Manager (scm)’ site:
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.
- 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.
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.
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:
Great article thank you for posting…
AWESOME !
I’ve used this technique (and sample code) to upload the files for ‘modules’ as part of an Azure Function – I’ll provide a sample+scripts on my blog (soon’ish)
Hi,
I am getting following error:
Invoke-RestMethod : {“Message”:”An error has occurred.”,”ExceptionMessage”:”Runtime keys are stored on
blob storage. This API doesn’t support this
configuration.”
Hi,
Not sure why and how you get this error. Can you explain some more?
grtz,
Octavie
Thank you! I cant believe how they dont have a UI for this. This had me operational within 20 minutes of discovering this page. BTW for anyone else checking this, I found this URL (already referenced at the end of the article ) useful in troubleshooting one or two things when executing the script
https://github.com/projectkudu/kudu/wiki/REST-API