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