Adding a backup_filter file to Azure Web App with PowerShell

Need to exclude files from your Web App Backup? Then you can use a file called _backup.filter and add it to the D:homesitewwwroot folder of your app. In this file you specify the files and folders you want to exclude. Further in this post I show you how to add this file to a Web App using PowerShell.

What to backup?

To start with we need to know what files and folders exists and what’s desired to backup. A way to view a list of all files and folder is to use Kudu. Click Advanced Tools → Go setting for your web app to access Kudu, when you access the Kudu site you can navigate the folder structure by clicking Debug Console → PowerShell or CMD.

The manual way to add excluded backup

Let’s say you only want to backup the image folder from a default ASP.NET Core app (as in this example). The manual way of doing this is to create a file named _backup.filter inside the folder D:homesitewwwroot, containing a list of the files and folders you want to exclude, all from the Kudu portal.

The Kudo file system exception list

For instance, to backup only the images folder from the image above, we need to add the files and folders highlighted in yellow in the _backup.filter file. In this case, the file would like below. With one directory or file per line.

sitewwwrootcss
sitewwwrootjs
sitewwwrootlib
sitewwwrootfavicon.ico

As mentioned above the manual way to add the backup exclusion list is to upload the _backup.filter file to D:homesitewwwroot. After the file is added, the files and folders are excluded from the future backups backups scheduled or manually initiated.

Adding _backup.filter file with PowerShell

Why adding the need to add the _backup.filter file with PowerShell? If you have multiple Web Apps you need to backup and all of the apps should be backed up the same way. Adding the file with PowerShell can spear you form a lot of repetitive work for each Web App.

So, how can you add the _backup.filter file with PowerShell? By using the Kudu/VFS API.

The first thing to do is to generate the required authorization header we need when calling the API. In the process block there is three functions. The first function retrieves the publishing credentials for the Web App, the second function creates the authorization header used to calling the API, and the third function uploads the file.

After we have the authorization information from function one and two, we can all the Kudu API to upload the _backup.filter file in function three.

To interact with the Kudu file system we can use the VFS (Virtual File System) API. It reflects the same file system structure as shown within the Azure Portal. By using the cmdlet Invoke-RestMethod we can call the API url and provide the authorization header and the reference to the _backup.filter file (found in the -InFile parameter). This file must be located locally for the script to work, since it copies the file from the local storage to the virtual file system.

function Copy-FileToWebApp {
    [CmdletBinding()]
    param (
        # Name of Web App
        [Parameter(Mandatory = $true)]
        [String]
        $WebAppName,

        # Name of Resource Group the Web App is stored
        [Parameter(Mandatory = $true)]
        [String]
        $RGNameWebApp
    )

    begin {
        # Location of the _backup.filter file on local machine
        $LocalFilterFilePath = "._backup.filter"
        $LocalFilterFileName = "_backup.filter"
    }
    process {

        function Get-PublishingProfileCredentials($RGNameWebApp, $WebAppName) {
        Write-Host -ForegroundColor Yellow "Retriving publishing creditentials for $WebAppName..."
            $ResourceType = "Microsoft.Web/sites/config"
            $ResourceName = "$WebAppName/PublishingCredentials"
            $PublishingCredentials = Invoke-AzResourceAction -ResourceGroupName $RGNameWebApp `
                -ResourceType $ResourceType `
                -ResourceName $ResourceName `
                -Action list `
                -ApiVersion 2018-02-01 -Force
            return $PublishingCredentials
            Write-Host -ForegroundColor Green "Successfully retrived publishing credentials for $WebAppName..."
        }

        function Get-KuduApiAuthorisationHeaderValue ($RGNameWebApp, $WebAppName) {
            Write-Host -ForegroundColor Yellow "Getting Kudu API authorization header for $WebAppName..."
            $PublishingCredentials = Get-PublishingProfileCredentials $RGNameWebApp $WebAppName
            return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $PublishingCredentials.Properties.PublishingUserName, $PublishingCredentials.Properties.PublishingPassword))))
            Write-Host -ForegroundColor Green "Finished getting Kudu API authorization header for $WebAppName..."
            }

        function Copy-FileToWebApp ($KuduApiAuthorisationToken, $WebAppName, $LocalFilterFileName, $LocalFilterFilePath ) {
            Write-Host -ForegroundColor Yellow "Uploaded file $LocalFilterFileName to VFS (Virtual File System)..."

                        $KuduApiUrl = "https://$WebAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/$LocalFilterFileName"
            $Result = Invoke-RestMethod -Uri $KuduApiUrl `
                -Headers @{"Authorization" = $KuduApiAuthorisationToken; "If-Match" = "*" } `
                -Method PUT `
                -InFile $LocalFilterFilePath `
                -ContentType "multipart/form-data"
            Write-Host -ForegroundColor Green "Successfully uploaded $LocalFilterFileName, done."
        }
        $AccessToken = Get-KuduApiAuthorisationHeaderValue $RGNameWebApp $WebAppName
        Copy-FileToWebApp $AccessToken $WebAppName $LocalFilterFileName $LocalFilterFilePath
    }
    end {
        Write-Host -ForegroundColor Green "[END] - Script complete."
    }
}

Below is an example of how to run the script, after loading the script into memory by using either dot source or by highlighting all code and hitting F8 in either VS Code or PowerShell ISE. Then you can run the following cmdlet.

Copy-FileToWebApp -WebAppName "webapp7110-stage" -RGNameWebApp "webapp-stage-rg" -Verbose

Other files or multiple files?

Oh wait! If you want to upload more than one file, you can customize the script a little bit. By adding a new parameter that takes the local directory path, and then using Get-ChildItem to retrieve each item within the folder, and then calling the the Copy-FilesToWebApp for item within the directory.

function Copy-FilesToWebApp {
    [CmdletBinding()]
    param (
        # Name of Web App
        [Parameter(Mandatory = $true)]
        [String]
        $WebAppName,

        # Name of Resource Group the Web App is stored
        [Parameter(Mandatory = $true)]
        [String]
        $RGNameWebApp,

        # Folder
        [Parameter(Mandatory = $true)]
        [String]
        $FolderPath
    )
    process {

        function Get-PublishingProfileCredentials($RGNameWebApp, $WebAppName) {
            Write-Host -ForegroundColor Yellow "Retriving publishing creditentials for $WebAppName..."
            $ResourceType = "Microsoft.Web/sites/config"
            $ResourceName = "$WebAppName/PublishingCredentials"
            $PublishingCredentials = Invoke-AzResourceAction -ResourceGroupName $RGNameWebApp `
                -ResourceType $ResourceType `
                -ResourceName $ResourceName `
                -Action list `
                -ApiVersion 2018-02-01 -Force
            return $PublishingCredentials
            Write-Host -ForegroundColor Green "Successfully retrived publishing credentials for $WebAppName..."
        }

        function Get-KuduApiAuthorisationHeaderValue ($RGNameWebApp, $WebAppName) {
            Write-Host -ForegroundColor Yellow "Getting Kudu API authorization header for $WebAppName..."

            $PublishingCredentials = Get-PublishingProfileCredentials $RGNameWebApp $WebAppName
            return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $PublishingCredentials.Properties.PublishingUserName, $PublishingCredentials.Properties.PublishingPassword))))

            Write-Host -ForegroundColor Green "Finished getting Kudu API authorization header for $WebAppName..."
        }

        function Copy-FileToWebApp ($KuduApiAuthorisationToken, $WebAppName, $FileName, $FolderPath ) {
            Write-Host -ForegroundColor Yellow "Uploaded file $FileName to VFS (Virtual File System)..."

            $KuduApiUrl = "https://$WebAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/$FileName"
            $Result = Invoke-RestMethod -Uri $KuduApiUrl `
                -Headers @{"Authorization" = $KuduApiAuthorisationToken; "If-Match" = "*" } `
                -Method PUT `
                -InFile $FolderPath `
                -ContentType "multipart/form-data"

            Write-Host -ForegroundColor Green "Successfully uploaded $FileName, done."
        }

        $LocalFiles = Get-ChildItem -Path $FolderPath
        $AccessToken = Get-KuduApiAuthorisationHeaderValue $RGNameWebApp $WebAppName
        $LocalFiles | ForEach-Object {
            Write-Host -ForegroundColor Yellow "Uploading file $($_.Name)..."
            Copy-FileToWebApp $AccessToken $WebAppName $_.Name $_.FullName
            Write-Host -ForegroundColor Yellow "[Done] Uploading file $($_Name)..."
        }
    }
    end {
        Write-Host -ForegroundColor Green "[END] - Script complete."
}
    }

If the script is loaded to memory, you can run it like the below 😉

Copy-FilesToWebApp -WebAppName "webapp7110-stage" -RGNameWebApp "webapp-stage-rg" -FolderPath "C:temp1-TestDirectory" -Verbose

That’s about it for this Saturday, have a great weekend!