Enumerating all the Logic App runs history using PowerShell and REST API

What Are Logic Apps?

Logic Apps are a piece of integration workflow hosted on Azure which is used to create scale-able integrations between various systems. These are very easy to design and provide connectivity between various disparate systems using many out of the box connectors as well as with the facility to design custom connectors for specific purposes. This makes integration easier than ever as the design aspect of the earlier complex integrations is made easy with minimum steps required to get a workflow in place and get it running.

Problem Scenario

Whenever Logic Apps are Hosted onto the Microsoft Azure Platform for integrating various business flows, it becomes imperative that the Logic App run is monitored on a daily basis to check if there were any errors and resubmit the execution of the Logic Apps. There will be scenarios where Azure Administrators should be able to re-submit all the failed runs of the logic App. The problem with the Azure Powershell Get-AzureRmLogicAppRunHistory is that it returns only the latest 30 items.

The following Azure Powersehll command ony returns latest 30 runs
Get-AzureRmLogicAppRunHistory -ResourceGroupName $grpName -Name $logicApp.Name

To enumerate for all the Logic App Runs, REST API should be used to get all the runs using the paging mechanism. The REST can be called from PowerShell using
Invoke-RestMethod

REST APIs allow users to interact with various services over HTTP/HTTPS and follow a common methodology of using methods to read and manipulate information. REST return information in a standard way, typically through JavaScript Object Notation (JSON). The Invoke-RestMethod cmdlet is built with REST in mind. It allows the user to invoke various methods for web service APIs and easily parse the output.

The Invoke-RestMethod cmdlet sends HTTP and HTTPS requests to Representational State Transfer (REST) web services that return richly structured data. PowerShell formats the response based on the data type. For JavaScript Object Notation (JSON) or XML, PowerShell converts (or deserializes) the content into objects.

Get Logic App Runs using REST

Azure exposes REST API to list the workflow runs.

https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Logic/workflows/{workflowName}/runs/{runName}?api-version=2016-06-01

The API endpoints are protected using oAuth2.0. The endpoints can be accessed using the Bearer token (Access Token). The Bearer authentication (also called token authentication) is an HTTP authentication scheme that involves security tokens called bearer(access) tokens. The client must send the access token in the Authorization header when making requests to protected resources:
Authorization: Bearer <token>
The access Token from the interactive login can be accessed using PowerShell as described below. The Access Token can also be retrieved using a registered appliaction credentials in Azure AD.

Connect-AzureRmAccount
Get-AzureRmContext
$subscription = Get-AzureRmSubscription -SubscriptionName $subscriptionName
$context = $subscription | Set-AzureRmContext
$tokens = $context.TokenCache.ReadItems() | Where-Object { $_.TenantId -eq $context.Subscription.TenantId } | Sort-Object -Property ExpiresOn -Descending
$token = $tokens[0].AccessToken

The Authentication header for Invoke-RestMethod can be passed as described below.

 $headers = @{
    'Authorization' = 'Bearer ' + $token
  }
Invoke-RestMethod -Method 'POST' -Uri $uri -Headers $headers

The following code describes the method to get all the Logic App Runs and re-submit the failed one. The Response contains the nextLink property to get the link for the next Paging items.

function Get-LogicAppHistory {
  param
  (
    [Parameter(Mandatory = $true)]
    $Token,
    [Parameter(Mandatory = $true)]
    $subscriptionId,
    [Parameter(Mandatory = $true)]
    $resourceGroupName,
    [Parameter(Mandatory = $true)]
    $logicAppName,
    [Parameter(Mandatory = $false)]
    $status,
    [Parameter(Mandatory = $true)]
    $startDateTime,
    [Parameter(Mandatory = $false)]
    $endDateTime
  )
  $headers = @{
    'Authorization' = 'Bearer ' + $token
  }
  $uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/{2}/runs?api-version=2016-06-01' -f $subscriptionId,$resourceGroupName,$logicAppName
  $method = (Invoke-RestMethod -Uri $uri -Headers $headers -Method Get) 
  $output = $method.value
  foreach ($item in $output) {
    if ((($item.properties.status -eq $status) -and ($item.properties.startTime -ge $startDateTime)) -and ($item.properties.startTime -le  $endDateTime ))
    {
      $uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/{2}/triggers/{3}/histories/{4}/resubmit?api-version=2016-06-01' -f $subscriptionId,$resourceGroupName,$logicAppName,$item.properties.Trigger.Name,$item.Name
      Write-Host "Submitting" $uri
      Invoke-RestMethod -Method 'POST' -Uri $uri -Headers $headers
    }
  }
  while ($method.nextLink)
  {
    $nextLink = $method.nextLink; 
    Write-Host $nextLink
    $method = (Invoke-RestMethod -Uri $nextLink -Headers $headers -Method Get)
    $output = $method.value
    foreach ($item in $output) {
      if (($item.properties.status -eq $status) -and ([DateTime]$item.properties.startTime -ge $startDateTime) -and ([DateTime]$item.properties.startTime -le $endDateTime))
      {
        $uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/{2}/triggers/{3}/histories/{4}/resubmit?api-version=2016-06-01' -f $subscriptionId,$resourceGroupName,$logicAppName,$item.properties.Trigger.Name,$item.Name
        Write-Host "Submitting" $uri
        Invoke-RestMethod -Method 'POST' -Uri $uri -Headers $headers
      }
    }
  }
}

The complete Code

The complete code is below.

function Get-LogicAppHistory {
  param
  (
    [Parameter(Mandatory = $true)]
    $Token,
    [Parameter(Mandatory = $true)]
    $subscriptionId,
    [Parameter(Mandatory = $true)]
    $resourceGroupName,
    [Parameter(Mandatory = $true)]
    $logicAppName,
    [Parameter(Mandatory = $false)]
    $status,
    [Parameter(Mandatory = $true)]
    $startDateTime,
    [Parameter(Mandatory = $false)]
    $endDateTime
  )
  $headers = @{
    'Authorization' = 'Bearer ' + $token
  }
  $uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/{2}/runs?api-version=2016-06-01' -f $subscriptionId,$resourceGroupName,$logicAppName
  $method = (Invoke-RestMethod -Uri $uri -Headers $headers -Method Get) 
  $output = $method.value
  foreach ($item in $output) {
    if ((($item.properties.status -eq $status) -and ($item.properties.startTime -ge $startDateTime)) -and ($item.properties.startTime -le  $endDateTime ))
    {
      $uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/{2}/triggers/{3}/histories/{4}/resubmit?api-version=2016-06-01' -f $subscriptionId,$resourceGroupName,$logicAppName,$item.properties.Trigger.Name,$item.Name
      Write-Host "Submitting" $uri
      Invoke-RestMethod -Method 'POST' -Uri $uri -Headers $headers
    }
  }
  while ($method.nextLink)
  {
    $nextLink = $method.nextLink; 
    Write-Host $nextLink
    $method = (Invoke-RestMethod -Uri $nextLink -Headers $headers -Method Get)
    $output = $method.value
    foreach ($item in $output) {
      if (($item.properties.status -eq $status) -and ([DateTime]$item.properties.startTime -ge $startDateTime) -and ([DateTime]$item.properties.startTime -le $endDateTime))
      {
        $uri = 'https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Logic/workflows/{2}/triggers/{3}/histories/{4}/resubmit?api-version=2016-06-01' -f $subscriptionId,$resourceGroupName,$logicAppName,$item.properties.Trigger.Name,$item.Name
        Write-Host "Submitting" $uri
        Invoke-RestMethod -Method 'POST' -Uri $uri -Headers $headers
      }
    }
  }
}
function ResubmitFailedLogicApp {
  param(
    [Parameter(Mandatory = $true)]
    [string]$subscriptionName,
    [Parameter(Mandatory = $true)]
    [string]$resourceGroupName,
    [Parameter(Mandatory = $true)]
    [string]$logicAppName,
    [Parameter(Mandatory = $true)]
    [string]$status
  )
  $currentAzureContext = Get-AzureRmContext
  if (!$currentAzureContext)
  {
    Connect-AzureRmAccount
    $currentAzureContext = Get-AzureRmContext
  }
  $startDateTime = Get-Date -Date '2019-10-14'
  $endDateTime = Get-Date -Date '2019-10-23'
  $subscription = Get-AzureRmSubscription -SubscriptionName $subscriptionName
  $context = $subscription | Set-AzureRmContext
  $tokens = $context.TokenCache.ReadItems() | Where-Object { $_.TenantId -eq $context.Subscription.TenantId } | Sort-Object -Property ExpiresOn -Descending
  $token = $tokens[0].AccessToken
  $subscriptionId = $subscription.Id;
  Write-Host $subscriptionId
  Get-LogicAppHistory -Token $token -SubscriptionId $subscriptionId -resourceGroupName $resourceGroupName -logicAppName $logicAppName -Status $status -startDateTime $startDateTime -endDateTime $endDateTime
}
Write-Host "#######  Example  #######"
Write-Host "ResubmitFailedLogicApp -subscriptionName 'New ENT Subscription' -resourceGroupName 'resourceName' -logicAppName 'LogicAppName' -status 'Failed'"
Write-Host "#######  Example  #######"
ResubmitFailedLogicApp

View Post