Source Code

Highlight

If you read my previous post on logic app costs and effect of scale, here is another tip for you

Which is, how to find underutilized logic apps to save a lot of money for your org!

Intro

Every single logic app that is not used in a private network environment will cos you around 25-32 USD per month, so the more you find the better.

Why? I’ve covered this in the article about the effect of scale. But in short, it’s about the cost, or more, about reduction of it. With private network environment each underutilized logic app will cost you, hence finding out underutilized logic apps will save you a ton of money.

Myself, I’m a simple person, a simple problem must have a simple solution. So I’ve wrote this script. It run for 30 minutes (API is slow for some reason), and I’ve found 50,000 USD yearly savings for my organization. Big win in 30 minutes, don’t you think?

It’s open source so feel free to use it :)

# Ensure you are logged into Azure
# Connect-AzAccount
 
# Install Az.ResourceGraph module if not installed
if (-not (Get-Module -ListAvailable -Name Az.ResourceGraph)) {
    Install-Module -Name Az.ResourceGraph -Scope CurrentUser -Force
}
 
# Query Azure Resource Graph to get all App Service resource IDs
$query = @"
resourcecontainers
| where ['type'] == 'microsoft.resources/subscriptions'
| where properties.managementGroupAncestorsChain[0].displayName startswith "Tenant Root Group"
         or properties.managementGroupAncestorsChain[0].displayName startswith "B"
         or properties.managementGroupAncestorsChain[0].displayName startswith "C"
         or properties.managementGroupAncestorsChain[0].displayName startswith "D"
| project subscriptionId
| join kind=inner (
    resources
    | where type =~ 'microsoft.web/sites'
    | where kind == 'functionapp,workflowapp'
) on subscriptionId
| project id
"@
 
# Run the query across all subscriptions
$appServices = Search-AzGraph -Query $query -First 1000
 
# Output the list of resource IDs
$appServices.id
 
$laEmptyCount = 0
foreach( $id in $appServices.id ) {
    $workflows = Invoke-AzRestMethod -Uri "https://management.azure.com/$id/workflows?api-version=2024-04-01"
    $parsedResponse = $workflows.Content | ConvertFrom-Json
    if($parsedResponse.value.Count -eq 0) {
        $laEmptyCount = $laEmptyCount + 1
        $laEmptyCount
        Write-Host "Unused LA: $id"
    }
}

Write-Host "Unused logic apps count: $laEmptyCount"

Just update this part with your management groups, or remove this where to search all azure subscriptions you have access to

| where properties.managementGroupAncestorsChain[0].displayName startswith "A"
         or properties.managementGroupAncestorsChain[0].displayName startswith "B"
         or properties.managementGroupAncestorsChain[0].displayName startswith "C"
         or properties.managementGroupAncestorsChain[0].displayName startswith "D"

Either

  • Use “Tenant Root Group” in the where statement to include all subscriptions under root management group or,
  • Just remove entire where statement altogether

Here is the code

Executing the code

If you run the code in https://shell.azure.com you should see something like this

Output

PS /home/adam> # Install Az.ResourceGraph module if not installed
PS /home/adam> if (-not (Get-Module -ListAvailable -Name Az.ResourceGraph)) {
>>     Install-Module -Name Az.ResourceGraph -Scope CurrentUser -Force
>> }
PS /home/adam>  
PS /home/adam> # Query Azure Resource Graph to get all App Service resource IDs
PS /home/adam> $query = @"
>> resourcecontainers
>> | where ['type'] == 'microsoft.resources/subscriptions'
>> | where properties.managementGroupAncestorsChain[0].displayName startswith "Tenant Root Group"
>>          or properties.managementGroupAncestorsChain[0].displayName startswith "B"
>>          or properties.managementGroupAncestorsChain[0].displayName startswith "C"
>>          or properties.managementGroupAncestorsChain[0].displayName startswith "D"
>> | project subscriptionId
>> | join kind=inner (
>>     resources
>>     | where type =~ 'microsoft.web/sites'
>>     | where kind == 'functionapp,workflowapp'
>> ) on subscriptionId
>> | project id
>> "@
PS /home/adam>  
PS /home/adam> # Run the query across all subscriptions
PS /home/adam> $appServices = Search-AzGraph -Query $query -First 1000
PS /home/adam>  
PS /home/adam> # Output the list of resource IDs
PS /home/adam> $appServices.id
/subscriptions/f73706f8-c55b-42b7-9d31-6fc8e0d24146/resourceGroups/logic-apps-standard-cicd-rg/providers/Microsoft.Web/sites/logicappscicd
/subscriptions/f73706f8-c55b-42b7-9d31-6fc8e0d24146/resourceGroups/logic-apps-standard-cicd-rg/providers/Microsoft.Web/sites/logicappscicd-prod
/subscriptions/f73706f8-c55b-42b7-9d31-6fc8e0d24146/resourceGroups/sb-rg/providers/Microsoft.Web/sites/amdemosb
PS /home/adam>  
PS /home/adam> $laEmptyCount = 0
PS /home/adam> foreach( $id in $appServices.id ) {
>>     $workflows = Invoke-AzRestMethod -Uri "https://management.azure.com/$id/workflows?api-version=2024-04-01"
>>     $parsedResponse = $workflows.Content | ConvertFrom-Json
>>     if($parsedResponse.value.Count -eq 0) {
>>         $laEmptyCount = $laEmptyCount + 1
>>         $laEmptyCount
>>         Write-Host "Unused LA: $id"
>>     }
>> }

Here is the key info

Logic App Standard SKU found in your selected query

PS /home/adam> # Output the list of resource IDs
PS /home/adam> $appServices.id
/subscriptions/f73706f8-c55b-42b7-9d31-6fc8e0d24146/resourceGroups/logic-apps-standard-cicd-rg/providers/Microsoft.Web/sites/logicappscicd
/subscriptions/f73706f8-c55b-42b7-9d31-6fc8e0d24146/resourceGroups/logic-apps-standard-cicd-rg/providers/Microsoft.Web/sites/logicappscicd-prod
/subscriptions/f73706f8-c55b-42b7-9d31-6fc8e0d24146/resourceGroups/sb-rg/providers/Microsoft.Web/sites/amdemosb

And if you find a logic app which will be ununsed, then it will print something like this

Unused LA: $/subscriptions/f73706f8-c55b-42b7-9d31-6fc8e0d24146/resourceGroups/sb-rg/providers/Microsoft.Web/sites/amdemosb

And at the end the amount of unused logic apps

Unused logic apps count: 1

Next Actions

Here are some recommendations from me on what you can check out next if you want to learn more

Source Code

Adam Marczak

Programmer, architect, trainer, blogger, evangelist are just a few of my titles. What I really am, is a passionate technology enthusiast. I take great pleasure in learning new technologies and finding ways in which this can aid people every day. My latest passion is running an Azure 4 Everyone YouTube channel, where I show that Azure really is for everyone!

Did you enjoy the article?

Share it!

More tagged posts