Logic App | Access Key Revocation and Regeneration

Overview

In previous articles I have subtly referenced risks and best practices regarding HTTP triggered workflows and their use of Access Keys for security, such as:

  • Some Potential Risks:

    • If a Key is leaked, it can be used by anyone who obtains it to call your Logic App Workflow.
    • If a Key has expired or been invalidated then services, applications, and or users who have not been provided a new key will cease to be able to invoke your workflow.
  • Some Access Key Best Practices to mitigate risks:

    • Have a revocation plan - Make sure that you are prepared to respond if a Key is compromised.
    • Have a rotation plan - Look to replace the Key with new ones at regular intervals.

Given the risks and best practices described, I wanted to put together a solution whereby the regeneration and issue of the access keys could be automated. This allows for keys to be regenerated on a regular schedule provided they might need to comply with security policies. Further to this, regenerating the access keys means old ones are invalidated and thus protects you in the incident were a key is compromised.

Solution

Access Key Revocation and Regeneration

To automate the regeneration of the access keys, I am going to make use of the Azure Rest API and the following components:

  • Azure Key Vault - Used as secure storage for the workflow access keys.
  • Identity and Access Management - Services, applications, and users will gain access to the access key secrets in Key Vault through Identity and role assignment at the secret level (Principal of Least Privilege).
  • PowerShell Script - A script developed to invoke the Regenerate Access Key Azure Rest API.
  • Bicep Template - A template developed to obtain and re-issue the workflow access keys to Key Vault.
  • DevOps Pipeline - Using an automated pipeline to manually or on schedule invoke the PowerShell script and redeploy the Bicep template.

⚠️ Note For automated roll out of access keys to services, applications, or users, their reference of the key vault secrets must not be directly tied to a version of the secret, rather always point to current.

Script to Regenerate a Workflow Access Key

The following PowerShell script is used to Regenerate a specific Logic App Workflow Access Key. By regenerating a new key, the previous will also be invalidated.

$SubscriptionId = '{SubscriptionID}'
$ResourceGroupName = '{ResourceGroupName}'
$LogicAppName = '{Standard Logic App Name}'
$WorkflowName = '{Workflow Name}'

# Not required if automated and using as part of a DevOps pipeline.
Connect-AzAccount -Subscription $SubscriptionId 

$accessToken = Get-AzAccessToken

$request = @{
    Method = 'POST'
    Uri = "https://management.azure.com/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/providers/Microsoft.Web/sites/$($LogicAppName)/hostruntime/runtime/webhooks/workflow/api/management/workflows/$($WorkflowName)/regenerateAccessKey?api-version=2024-04-01"
    Body = @{
        keyType = 'Primary'
    } | ConvertTo-Json
    Headers = @{
        Authorization = "Bearer $($accessToken.Token)"
        "Content-Type" = "application/json"
    }
}

Invoke-RestMethod @request

Bicep Template to Obtain and Surface Workflow Access Keys to Key Vault

The following Bicep template is used to obtain the workflow access key and create the secret in key vault (if already deployed, then a new version).

/********************************************
Bicep Template: Workflow Access Keys Role Out
        Author: Andrew Wilson
*********************************************/

targetScope = 'resourceGroup'

// ** User Defined Types **
// ************************

@description('Object type used to identify a Workflow and Trigger')
@metadata({
  workflowName: 'The name of the workflow within your Standard Logic App.'
  workflowTrigger: 'The HTTP trigger name within the workflow'
})
@sealed()
type workflow = {
  workflowName: string
  workflowTrigger: string
}

@description('Array of Standard Logic App Workflows')
@minLength(1)
type workflowArray = workflow[]

// ** Parameters **
// ****************

@description('Name of the Logic App to place workflow(s) sig into KeyVault')
param LogicAppName string

@description('Name of the Key Vault to place secrets into')
param keyVaultName string

@description('Array of Workflows to obtain sigs from.')
param workflows workflowArray

// ** Variables **
// ***************

// ** Resources **
// ***************

@description('Retrieve the existing Logic App')
resource logicApp 'Microsoft.Web/sites@2024-04-01' existing = {
  name: LogicAppName
}

@description('Retrieve the existing Key Vault instance to store secrets')
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
  name: keyVaultName
}

@description('Create the Logic App workflow access key as a secret - Deployment principle requires RBAC permissions to do this')
resource vaultLogicAppKey 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [for workflow in workflows: {
  name: '${logicApp.name}-${workflow.workflowName}-sig'
  parent: keyVault
  tags: {
    ResourceType: 'LogicAppStandard'
    ResourceName: logicApp.name
  }
  properties: {
    contentType: 'string'
    value: listCallbackUrl(resourceId('Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers', logicApp.name, 'runtime', 'workflow', 'management', workflow.workflowName, workflow.workflowTrigger), '2022-09-01').queries.sig
  }
}]

// ** Outputs **
// *************

In the instance that the access key is compromised due to a user/component/service being compromised, then the action would be two fold:

  1. Revoke the user/component/service access to the secret in key vault.
  2. Regenerate and issue a new access key.

Hope this helps and have fun.

For the original version of this post see Andrew Wilson's personal blog at Logic App | Access Key Revocation and Regeneration