Recently I had the challenge to workout which service bus topic/subscription or queue is used by which logic app.

The constraints I had are:

  • We had 1 resource group with 500 odd Logic apps for the environment
  • We had a service bus premium namespace with loads of queues which are sent to and received from by different logic apps
  • Some of our logic apps which are low volume use a helper logic app to send to queues which simplifies the configuration of setting up so many connectors

I have put together a powershell script which would look at my service bus and describe all of the queues, topics and subscriptions. It would then go through all of the Logic Apps and it would check the triggers and actions to look for places where the queues and topics might be used.

It will then output a csv file which will list the following fields:

  • Logic App Name
  • Action Name
  • Trigger Name
  • Queue
  • Topic
  • Subscription

Below is an example from the csv

Sharing the script as im sure others will find it useful or be able to modify it to suit there use cases.



$subscriptioname = '[my-subscription]'

$serviceBusEntityPrefix = '[environment-prefix]'
$serviceBusResourceGroup = '[rg-name]'
$serviceBusNamespace = '[sb-namespace]'

$logicAppResourceGroup = [logicapp-namespace]'

Get-AzServiceBusNamespace -ResourceGroupName $serviceBusResourceGroup -Name $serviceBusNamespace

$matchList = [System.Collections.ArrayList]::new();
$noMatchList = [System.Collections.ArrayList]::new();

function AddMatch([string] $logicAppName = '', [string] $actionName = '', [string] $triggerName = '', [string] $parameterName = '', [string] $queue = '', [string] $topic = '', [string] $subscription = ''){

    Write-Host 'Adding Match for Logic App: ' $logicAppName
    $match = New-Object -TypeName psobject
    $match | Add-Member -MemberType NoteProperty -Name 'LogicApp' -Value $logicAppName
    $match | Add-Member -MemberType NoteProperty -Name 'LogicAppTrigger' -Value $triggerName
    $match | Add-Member -MemberType NoteProperty -Name 'LogicAppAction' -Value $actionName
    $match | Add-Member -MemberType NoteProperty -Name 'Queue' -Value $queue
    $match | Add-Member -MemberType NoteProperty -Name 'Topic' -Value $topic
    $match | Add-Member -MemberType NoteProperty -Name 'Subscription' -Value $subscription

    $matchList.Add($match)
}

function AddNoMatch([string] $queue = '', [string] $topic = '', [string] $subscription = ''){

    Write-Host 'Adding Match for Logic App: ' $logicAppName
    $item = New-Object -TypeName psobject
    $item | Add-Member -MemberType NoteProperty -Name 'Queue' -Value $queue
    $item | Add-Member -MemberType NoteProperty -Name 'Topic' -Value $topic
    $item | Add-Member -MemberType NoteProperty -Name 'Subscription' -Value $subscription

    $noMatchList.Add($item)
}

function RecursivelyProcessLogicAppActions($actionsObject){

    $actions = $actionsObject.PSObject.Properties 
    
    foreach($action in $actions){
        $actionName = $action.Name
        $actionType = $action.Value.type

        $actionJsonText = $action.Value | ConvertTo-Json

        #This lets us ignore actions like scope or condition or other actions which may cause duplicates
        if($actionType -ne 'Scope' -and $actionType -ne 'Foreach' -and $actionType -ne 'If' -and $actionType -ne 'Until'){

            # Compare with Queues
            foreach($queue in $serviceBusDescription.Queues){

                if($actionJsonText -match $queue.Name){
                    Write-Host $logicAppName ' is a match for Queue: ' $queue.Name ' in action ' $actionName
            
                    AddMatch -logicAppName $logicAppName -queue $queue.Name -actionName $actionName
                }
            }
    
            # Compare with Topics
            foreach($topic in $serviceBusDescription.Topics){

                if($actionJsonText -match $topic.Name){
                    Write-Host $logicAppName ' is a match for Topic ' $topic.Name ' in action ' $actionName

                    AddMatch -logicAppName $logicAppName -topic $topic.Name -actionName $actionName

                    foreach($subscription in $topic.Subscriptions){

                        if($actionJsonText -match $subscription.Name){
                            Write-Host $logicAppName ' is a match for Subscription '$subscription.Name ' on Topic ' $topic.Name ' in action ' $actionName

                            AddMatch -logicAppName $logicAppName -topic $topic.Name -subscription $subscription.Name -actionName $actionName
                        }
                    }
                }
            }  

        }

        
        #If there are child actions then recursively process them too
        $childActions = $action.Value.actions
        if($childActions -ne $null){
            RecursivelyProcessLogicAppActions -actionsObject $childActions
        }   
    }
}

Write-Host 'Reading Queues'
$queueDescriptions= @()
$queues = Get-AzServiceBusQueue -ResourceGroupName $serviceBusResourceGroup -NamespaceName $serviceBusNamespace -MaxCount 500
foreach($queue in $queues){
    if($queue.Name.ToLower().StartsWith($serviceBusEntityPrefix)){
        Write-Host 'Reading Queue: ' $queue.Name

        $queueDescription = [PSCustomObject]@{}
        $queueDescription | Add-Member -MemberType NoteProperty -Name 'Name' -Value $queue.Name
    
        $queueDescriptions += $queueDescription
    }
    else{
        Write-Host 'Skipping: ' $queue.Name ' - the name doesnt match the environment prefix'
    }
}

Write-Host 'Reading Topics'
$topicDescriptions= @()
$topics = Get-AzServiceBusTopic -ResourceGroupName $serviceBusResourceGroup -NamespaceName $serviceBusNamespace -MaxCount 500
foreach($topic in $topics){
    if($topic.Name.ToLower().StartsWith($serviceBusEntityPrefix)){
        Write-Host 'Reading Topic: ' $topic.Name


        $topicDescription = [PSCustomObject]@{}
        $topicDescription | Add-Member -MemberType NoteProperty -Name 'Name' -Value $topic.Name

        $subscriptionDescriptions= @()
        $subscriptions = Get-AzServiceBusSubscription -ResourceGroupName $serviceBusResourceGroup -NamespaceName $serviceBusNamespace -TopicName $topic.Name
        foreach($subscription in $subscriptions){
            Write-Host 'Reading Subscription: Topic=' $topic.Name ' Subscription=' $subscription.Name

            $subscriptionDescription = [PSCustomObject]@{}
            $subscriptionDescription | Add-Member -MemberType NoteProperty -Name 'Name' -Value $subscription.Name
            $subscriptionDescription | Add-Member -MemberType NoteProperty -Name 'Topic' -Value $topic.Name            

            $subscriptionDescriptions += $subscriptionDescription
        }

        $topicDescription | Add-Member -MemberType NoteProperty -Name 'Subscriptions' -Value $subscriptionDescriptions
        $topicDescriptions += $topicDescription

    }
    else{
        Write-Host 'Skipping: ' $topic.Name ' - the name doesnt match the environment prefix'
    }
}

$serviceBusDescription = [PSCustomObject]@{}
$serviceBusDescription | Add-Member -MemberType NoteProperty -Name 'Queues' -Value $queueDescriptions
$serviceBusDescription | Add-Member -MemberType NoteProperty -Name 'Topics' -Value $topicDescriptions


Write-Host 'Finished Reading Service Bus'


# Read Logic Apps
$logicAppResourceGroupItem = Get-AzResourceGroup -Name $logicAppResourceGroup
$logicAppResourceGroupPath = $logicAppResourceGroupItem.ResourceId
Write-Host 'Resource Group Path: '  $logicAppResourceGroupPath

$resources = Get-AzResource -ResourceGroupName $logicAppResourceGroup -ResourceType Microsoft.Logic/workflows
$resources | ForEach-Object { 
    
    $logicAppName = $_.Name
    
    Write-Host 'Testing Logic App = ' $logicAppName
    
    $logicApp = Get-AzLogicApp -Name $logicAppName -ResourceGroupName $logicAppResourceGroup        
    $logicAppUrl = $logicAppResourceGroupPath + '/providers/Microsoft.Logic/workflows/' + $logicApp.Name + '?api-version=2018-07-01-preview'
    
    #Get Logic App Content
    $logicAppJsonText = az rest --method get --uri $logicAppUrl   
    $logicAppJsonObject = $logicAppJsonText | ConvertFrom-Json   

    #Search Logic App Actions
    $actions = $logicAppJsonObject.properties.definition.actions
    RecursivelyProcessLogicAppActions -actionsObject $actions
    
    #Search Logic App Triggers
    $triggers = $logicAppJsonObject.properties.definition.triggers.PSObject.Properties 
    foreach($trigger in $triggers){
        $triggerName = $trigger.Name
        $triggerJsonText = $trigger.Value | ConvertTo-Json
    
        # Compare with Queues
        foreach($queue in $serviceBusDescription.Queues){

            if($triggerJsonText -match $queue.Name){
                Write-Host $logicAppName ' is a match for Queue: ' $queue.Name ' in trigger ' $triggerName
            
                AddMatch -logicAppName $logicAppName -queue $queue.Name -triggerName $triggerName
            }
        }
    
        # Compare with Topics
        foreach($topic in $serviceBusDescription.Topics){

            if($triggerJsonText -match $topic.Name){
                Write-Host $logicAppName ' is a match for Topic ' $topic.Name ' in trigger ' $triggerName

                AddMatch -logicAppName $logicAppName -topic $topic.Name -triggerName $triggerName

                foreach($subscription in $topic.Subscriptions){

                    if($triggerJsonText -match $subscription.Name){
                        Write-Host $logicAppName ' is a match for Subscription '$subscription.Name ' on Topic ' $topic.Name ' in trigger ' $triggerName

                        AddMatch -logicAppName $logicAppName -topic $topic.Name -subscription $subscription.Name -triggerName $triggerName
                    }
                }
            }
        } 

        
    }

     
}


Write-Host 'Exporting matches to CSV file'
$matchList | Export-Csv -Path C:\Temp\ServiceBusLogicAppMatches.csv


 

Buy Me A Coffee