Disclaimer: The below process is adapted, not to include any client specific details or information that can affect or impact the private form of business.

Scenario: Being proactive in today world of information technology business is the must-be rule. Imagine that you have dozens of the web applications running at your customers platform, of course all of them are being backed up to the blob storage, as we know by the Microsoft rules, at moment of writing this blog post, web app including it’s database can not be bigger than 10GB for the backup process to succeed.

Question: Do you really wanna wait for your monitoring system to tell you that your backups are failing, or you want to be proactive and find out before the backup fails?

For a while I was searching for an appropriate solution over the internet, one dude wrote an awesome module for accessing the web app file system via it’s FTP server, I thought – maybe I can measure size of all of the files using queries, but for some reason due to high number of requests, very often server was giving 503 error as answer, so I had to find another solution.

Solution: Since the web apps have support for Kudu console, I decided to use it’s Rest API to query file via GET method. I have packaged everything into the Powershell function, actually a few ones into a module, which I will share at the end of this post.

Below I am sharing the code which is followed by the comments on almost each line, so the code is self explanatory.

Note: This can be very time consuming process, depending on the size of your web app, be mindful about that, maybe you want to implement some more logic in the function, so it can stop measuring the size of the web app once it comes to the 10GB.

Tip: We started running this once a day from the Automation Account runbook, we do not care about how much time it takes to measure the web app because eventually we will get the needed information and the computing resources are utilized from Azure platform itself, we collect information about all web apps into the variable and in the foreach loop, we executed the function as a job, so we can measure size of the all web apps in the parallel. We send the output of the function to the Log Analytics in the table format, where we have configured the alerting against the Size column.

Action: Below I am gonna post three functions, main public functions and two private helper functions, but for the whole functional set please refer to the download link at the end of the blog post.

Function Get-AzWebAppSize {
    [CmdletBinding()]
    param (
        # Name of the web app
        [Parameter(Mandatory=$true,
        Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$WebApp
    )
    begin {
        # Private function - confirm that you are connected to Azure Context.
        Confirm-AzSession
    }
    process {
        # Try to find the web app and all of it's properties.
        $FindWebApp = (Get-AzWebApp -Name $WebApp)
        # Private function for handling the empty variables in a nice way.
        Find-EmptyString -Variable $FindWebApp -Message "Cannot find instance of the web app with the name $WebApp" -Action Continue
        # Block for extracting the Kudu credentials.
        $SiteName = $WebApp
        $PublishingProfile = [xml](Get-AzWebAppPublishingProfile -Name $FindWebApp.RepositorySiteName -ResourceGroupName $FindWebApp.ResourceGroup)
        $Username = (Select-Xml -Xml $PublishingProfile -XPath "//publishData/publishProfile[contains(@profileName,'Web Deploy')]/@userName").Node.Value
        $Password = (Select-Xml -Xml $PublishingProfile -XPath "//publishData/publishProfile[contains(@profileName,'Web Deploy')]/@userPWD").Node.Value
        $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $Username,$Password)))
        # Configuring the root of the web app file system.
        $apiUrl = "https://$siteName.scm.azurewebsites.net/api/vfs/"
        # Private function for finding the files via GET Rest API method.
        $BareRoot = Get-ApiFile $apiUrl
        # Open the array for storing the root directories.
        $RootDirectories = [System.Collections.ArrayList]::new()
        # Loop over directories that are initially found and exclude ones that are by-default not included into a web app backup or treated as the part of the web app.
        foreach ($Item in $BareRoot) {
            if (($Item.Path -eq "D:\") -or ($Item.Path -eq "D:\local")) {
                Continue
            }
            else {
                [void]$RootDirectories.Add($Item)
            }
        }
        # Open the array for storing the sizes of each file.
        $Summary = [System.Collections.ArrayList]::new()
        foreach ($Directory in $RootDirectories) {
            # Fire the recursive function for finding the file sizes.
            $Catch = (Get-RecursiveApiFile -Path $Directory.href)
            foreach ($Item in $Catch) {
                [decimal]$Size = $Item.Size
                [void]$Summary.Add($Size)
            }
        }
        # Find the summary in bytes of all file sizes.
        [decimal]$Final = ($Summary | Measure-Object -Sum).Sum
        # Convert bytes into MB.
        $GetDecimal = $Final/1MB
        # Get rid of the decimals, round to the whole number.
        $WebAppSize = [math]::Round($GetDecimal)
        # Output the size and the name of the app into a custom object.
        [PSCustomObject]@{
            WebApp = $FindWebApp.RepositorySiteName
            Size = "$($WebAppSize)MB"
        }
    }
}

 

Function Get-ApiFile {
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$true,
        Position=1)]
        [string]$apiUrl
    )
    Invoke-RestMethod -Uri $apiUrl -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method Get
}

 

Function Get-RecursiveApiFile {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true,
        Position=1)]
        [string]$Path
    )
    process {
        $Data = Get-ApiFile $Path
        if ($Data) {
            foreach ($Item in $Data) {
                if ($Item.Mime -eq "inode/directory") {
                    Get-RecursiveApiFile -Path $Item.href
                }
                else {
                    $Item
                }
            }
        }
    }
}

Download:

Link: https://mega.nz/#!04YijAhA

Key: t-zC0fCspBLoQWSzMjGwtb5B1p7xr6sF3lqnRUKuxmw