In our previous article Identifying Workflows Inside Azure Logic Apps, we explored how to identify workflows running within Azure Logic Apps across different instances in an Azure tenant. That script allowed us to automate the process of retrieving workflow details such as name, helping with resource management and assesment. By leveraging PowerShell, we eliminated the need for manual tracking through the Azure portal and streamlined the reporting process. By the way as for now, there is no easy way to get this information from Azure Portal.
That approach was particularly useful for Logic Apps Standard, which can host multiple workflows within a single instance. By automating the data retrieval, we generated structured reports showing which workflows were running on which Logic Apps, making it easier to audit and optimize resources.
Identifying Functions Inside Function Apps
Along with Logic App Reports Script, this time we’re applying a similar approach—but for Azure Function Apps! Instead of tracking workflows in Logic Apps, we’ll now focus on Function Apps and the functions running inside them. This report will help you identify all functions deployed across your Azure subscriptions, providing insights into their language runtime, last modified time, and state (enabled/disabled).
Azure Function Apps can host multiple functions within a single instance, just like Logic Apps Standard can host multiple workflows. If you’re working with*serverless computing, keeping track of these functions is crucial, whether for monitoring, debugging, or cost management.
In this article, we will:
✅ Identify all Function Apps within your Azure subscriptions.
✅ Retrieve a list of functions running inside each Function App.
✅ Extract key details such as language runtime, last modified time, and state (enabled/disabled).
✅ Generate a structured CSV report for further analysis.
🔧 Querying Function Apps and Functions Using PowerShell
To achieve this, we will use Azure PowerShell and Azure REST APIs to retrieve all Function Apps in our tenant and list the functions running inside them. Below is a script that scans all subscriptions, collects relevant function details, and exports them into a CSV report for analysis.
# Retrieve all subscriptions
$subscriptions = Get-AzSubscription
# Initialize arrays to store output data
$functionAppsOutput = @()
$functionsOutput = @()
# Loop through each subscription
ForEach ($subscription in $subscriptions) {
Write-Host "Processing subscription: $($subscription.Name)" -ForegroundColor Cyan
# Set the context to the current subscription
Set-AzContext -Subscription $subscription.Id
# Retrieve all Function Apps in the subscription
$functionApps = Get-AzFunctionApp
# Loop through each Function App
ForEach ($functionApp in $functionApps) {
Write-Host "Processing Function App: $($functionApp.Name)" -ForegroundColor Green
# Prepare the Function App object
$functionAppObj = [PSCustomObject]@{
SubscriptionName = $subscription.Name
SubscriptionId = $subscription.Id
ResourceGroupName = $functionApp.ResourceGroup
FunctionAppName = $functionApp.Name
Location = $functionApp.Location
AppServicePlanId = $functionApp.ServerFarmId
Kind = $functionApp.Kind
State = $functionApp.State
DefaultHostName = $functionApp.DefaultHostName
Enabled = $functionApp.Enabled
}
# Add to Function Apps output array
$functionAppsOutput += $functionAppObj
# Prepare REST API URL to list functions within the Function App
$resourceGroupName = $functionApp.ResourceGroup
$functionAppName = $functionApp.Name
$subscriptionId = $subscription.Id
$url = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Web/sites/$functionAppName/functions?api-version=2022-03-01"
try {
# Invoke REST API to get function details
$response = Invoke-AzRestMethod -Method GET -Uri $url
if ($response.Content -ne $null) {
$functions = ($response.Content | ConvertFrom-Json).value
if ($functions.Count -gt 0) {
# Loop through each function
ForEach ($function in $functions) {
$functionObj = [PSCustomObject]@{
SubscriptionName = $subscription.Name
SubscriptionId = $subscription.Id
ResourceGroupName = $resourceGroupName
FunctionAppName = $functionAppName
FunctionName = $function.properties.name
Language = $function.properties.language
LastModified = $function.properties.lastModifiedTimeUtc
IsDisabled = $function.properties.isDisabled
EntryPoint = $function.properties.entryPoint
}
# Add to functions output array
$functionsOutput += $functionObj
}
} else {
# No functions found in this Function App
$functionsOutput += [PSCustomObject]@{
SubscriptionName = $subscription.Name
SubscriptionId = $subscription.Id
ResourceGroupName = $resourceGroupName
FunctionAppName = $functionAppName
FunctionName = "No Functions Found"
Language = ""
LastModified = ""
IsDisabled = ""
EntryPoint = ""
}
}
} else {
# Unable to retrieve functions
$functionsOutput += [PSCustomObject]@{
SubscriptionName = $subscription.Name
SubscriptionId = $subscription.Id
ResourceGroupName = $resourceGroupName
FunctionAppName = $functionAppName
FunctionName = "Unable to retrieve functions"
Language = ""
LastModified = ""
IsDisabled = ""
EntryPoint = ""
}
}
}
catch {
Write-Warning "Failed to retrieve functions for Function App: $functionAppName"
$functionsOutput += [PSCustomObject]@{
SubscriptionName = $subscription.Name
SubscriptionId = $subscription.Id
ResourceGroupName = $resourceGroupName
FunctionAppName = $functionAppName
FunctionName = "Error retrieving functions"
Language = ""
LastModified = ""
IsDisabled = ""
EntryPoint = ""
}
}
}
}
# Generate unique filenames with timestamp
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
# Export Function Apps report to CSV
$functionAppsFileName = "FunctionApps-Report-$timestamp.csv"
Write-Output "INFO: Exporting Function Apps to CSV file: $functionAppsFileName"
$functionAppsOutput | Export-Csv -Path "$PSScriptRoot\$functionAppsFileName" -NoTypeInformation -Force
# Export Functions report to CSV
$functionsFileName = "Functions-Report-$timestamp.csv"
Write-Output "INFO: Exporting Functions to CSV file: $functionsFileName"
$functionsOutput | Export-Csv -Path "$PSScriptRoot\$functionsFileName" -NoTypeInformation -Force
📊 Understanding the Report Output
🔹 Function Apps Report (FunctionApps-Report-YYYYMMDD-HHmmss.csv
)
Contains details of all Function Apps, including state, location, and enabled status.
🔹 Functions Report (Functions-Report-YYYYMMDD-HHmmss.csv
)
Lists all functions running inside each Function App, with key details like language runtime, modification time, and entry point.
Example Function Apps Report:
SubscriptionName | SubscriptionId | ResourceGroupName | FunctionAppName | Location | AppServicePlanId | Kind | State | DefaultHostName | Enabled |
---|---|---|---|---|---|---|---|---|---|
Subscription-1 | d068fd18-094c-4b7c-b3d4-6efbbfa5424b | resource-group-1 | function-app-1 | UK South | /subscriptions/a8689dae-f56e-48ad-a607-542eb0f20acb/resourceGroups/resource-group-1/providers/Microsoft.Web/serverfarms/function-plan-1 | functionapp | Running | function-app-1.azurewebsites.net | True |
Subscription-2 | 7fe0317a-0b6a-4ffa-b187-279a59475640 | resource-group-2 | function-app-2 | UK West | /subscriptions/485b419b-6e18-4275-91b3-0647a48b914b/resourceGroups/resource-group-2/providers/Microsoft.Web/serverfarms/function-plan-2 | functionapp | Running | function-app-2.azurewebsites.net | True |
Example Functions Report:
SubscriptionName | SubscriptionId | ResourceGroupName | FunctionAppName | FunctionName | Language | LastModified | IsDisabled | EntryPoint |
---|---|---|---|---|---|---|---|---|
Subscription-1 | 9d6ec9f1-49ba-445c-9a2c-a0f78f786e7c | resource-group-1 | function-app-1 | ProcessData | DotNet | 2024-07-15T10:00:00Z | False | Main.Process |
Subscription-2 | c6cd29d9-a534-4caa-ab97-5d37c5dbf6b3 | resource-group-2 | function-app-2 | HandleQueueMessage | Python | 2024-07-16T12:45:00Z | False | handler.main |
🚀 Next Steps
This script automates Function App monitoring just like we did for Logic Apps workflows before. If you have additional requirements—such as checking execution history or filtering by runtime—you can extend this script further!