Overview

If you’re hosting your applications using Azure Web Applications, I’m sure you’re using Kudu extension very frequently. Knowing your application name, you can visit https://{name_of_your_application}.scm.azurewebsites.net and explore multiple features which Kudu gives you. One of the most common features is PowerShell command line which allows you to run commands directly in the directory where your application is hosted.

Although this is a very continent way to run your commands. There can come a time when you will want to skip web interface. This will help you with performing some actions much faster and help you with scaling - once your request is scripted, you can trigger it multiple times or for multiple web applications.

In this blog post, I’m assuming you have an Azure account and you have at least one application running as Web App within Windows App Service Plan.

Writing the code

In our example, we will use:

  • Azure RM PowerShell module in order to connect to Azure and obtain Web Site information.
  • Kudu Rest API endpoint. Documentation here.

As you can read on Kudu documentation site. There are a few ways in which you can authenticate your request. We will use Web Deploy credentials from Web Site Publish profile. To obtain those credentials, we’ll use AzureRM module.

At the very beginning, we need to authenticate our communication with Azure. There are multiple ways to do it, but we will use the most simple one here:

Login-AzureRmAccount

This will open interactive window where you need to provide your credentials. In the real scenario, you could either pass credentials programmatically or use Service Principal Login.

Now that you’ve logged in correctly, we need to download web application publish profile. Please note that before getting this profile, you need to select the same subscription which your application is using.

$resourceGroup= "Some Resource Group"
$webAppName= "Some-Application"
$outputFilePath= "C:\Temp\Profile.xlm"
Get-AzureRMWebAppPublishingProfile -ResourceGroupName $resourceGroup -Name $webAppName -OutputFile $outputFilePath | Out-Null

By default Get-AzureRMWebAppPublishingProfile will attempt to write content of that publish profile in the console. We can use Out-Null to supress that.

Now we need to extract user and password from that profile. By default it should contain credentials for both Web Deploy and FTP connection. They will both have the same user and password so we can select single node for that. At the end we can remove publish profile file, this won’t be needed anymore.

[xml]$publishProfileContent= Get-Content $outputFilePath
$userName= $publishProfileContent.SelectSingleNode("//publishProfile").userName
$password= $publishProfileContent.SelectSingleNode("//publishProfile").userPWD
Remove-Item $outputFilePath

Kudu command request

We have everything needed to trigger the command. In our example, we will try to ensure that web.config file exists on the remote server. While sending our request, we need to send both command and path, where this command will be executed.

If you wish to use PowerShell, you need to add powershell at the beginning of your command. If not your command will most likely fail with some confusing exception.

$body = @{
  "command" = "powershell ((Get-Item **\\web.config).Count)"
  "dir" = "site\\wwwroot"
}

Our request will be simple POST request with Authentication header and JSON body from above.

 $bytes =[System.Text.Encoding]::ASCII.GetBytes("$($userName):$($password)")
 $encodedCreds = [System.Convert]::ToBase64String($bytes)
 $headers =  @{ Authorization = "Basic ${encodedCreds}" }

 $url= "https://$($webAppName).scm.azurewebsites.net/api/command"
 $result = Invoke-RestMethod -Method POST -ContentType "application/json" -Uri $Url -UseBasicParsing -Headers $headers -Body (ConvertTo-Json $body)

If request was successful and configuration file exists on the server we will get:

>  $result | Format-List | Out-String
Output   : 1
Error    : 
ExitCode : 0

Conclusion

If you connect PowerShell AzureRM with Kudu you will get really powerful tool. Nothing is stopping you from triggering commands for multiple web sites efficiently. In this post I’ve shown you how to do it and I hope it was helpful.