How to paramatize service bus managed identity in 'connections.json' for standard logic apps?

190 views Asked by At

I'm attempting to deploy a standard logic app with a service bus connector using a user assigned managed identity but I can't get it to work via terraform and ARM.

It looks connected in portal when I edit the workflow in designer...

enter image description here

But when I actually trigger the workflow I get the following error...

enter image description here

Failed to get oauth token for managed identity in logic app. Response: '{"statusCode":400,"message":"No User Assigned or Delegated Managed Identity found for specified ClientId/ResourceId/PrincipalId.","correlationId":"3b95b432-d690-4703-a571-c5e0d9730c43"}'

This is my service bus api connection...

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "connections_servicebus_name": {
            "defaultValue": "servicebus",
            "type": "String"
        },
        "location": {
            "defaultValue": "uksouth",
            "type": "String"
        },
        "principal_id": {
            "type": "String"
        },
        "servicebus_namespace": {
            "type": "String"
        },
        "subscriptionId": {
            "type": "String"
        },
        "tenant_id": {
            "type": "String"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Web/connections",
            "apiVersion": "2016-06-01",
            "name": "[parameters('connections_servicebus_name')]",
            "location": "[parameters('location')]",
            "kind": "V2",
            "properties": {
                "api": {
                    "id": "[concat('/subscriptions/', parameters('subscriptionId'), '/providers/Microsoft.Web/locations/', parameters('location'), '/managedApis/servicebus')]"
                },
                "parameterValueSet": {
                    "name": "managedIdentityAuth",
                    "values": {
                        "namespaceEndpoint": {
                        "value": "[concat('sb://', parameters('servicebus_namespace'), '.servicebus.windows.net/')]"
                        }
                    }
                },
                "displayName": "[parameters('connections_servicebus_name')]"
            }
        },
        {
            "type": "Microsoft.Web/connections/accessPolicies",
            "apiVersion": "2016-06-01",
            "name": "[concat(parameters('connections_servicebus_name'),'/',parameters('principal_id'))]",
            "location": "[parameters('location')]",
            "dependsOn": [
               "[resourceId('Microsoft.Web/connections', parameters('connections_servicebus_name'))]"
            ],
            "properties": {
               "principal": {
                  "type": "ActiveDirectory",
                  "identity": {
                     "objectId": "[parameters('principal_id')]",
                     "tenantId": "[parameters('tenant_id')]"
                  }
               }
            }
        }
    ],
    "outputs": {
        "connectionRuntimeUrl": {
          "type": "String",
          "value": "[reference(resourceId('Microsoft.Web/connections', parameters('connections_servicebus_name'))).connectionRuntimeUrl]"
        }
    }
}

This is the terraform for deploying the above ARM template...

data "template_file" "servicebus_connection" {
  template = file("../logic-apps/api-connections/servicebus.json")
}

resource "azurerm_resource_group_template_deployment" "servicebus_connection" {
  resource_group_name = data.azurerm_resource_group.rg.name
  template_content    = data.template_file.servicebus_connection.template

  parameters_content = jsonencode({
    "principal_id"         = { value = "${data.azurerm_user_assigned_identity.identity.principal_id}" }
    "subscriptionId"       = { value = "${var.target-subscription-id}" }
    "servicebus_namespace" = { value = "pip-exec" }
    "tenant_id"            = { value = "${data.azurerm_user_assigned_identity.identity.tenant_id}" }
    }
  )

  name            = "connection-${filemd5("../logic-apps/api-connections/servicebus.json")}"
  deployment_mode = "Incremental"

  lifecycle {
    ignore_changes = [
      parameters_content
    ]
  }
}

This is the terraform for my standard logic app deployment....

resource "azurerm_logic_app_standard" "product_feed_4" {

  depends_on = [
    azurerm_resource_group_template_deployment.servicebus_connection,
    azurerm_resource_group_template_deployment.keyvault_connection ]

  name                       = "product-feed-5"
  location                   = "uksouth"
  resource_group_name        = data.azurerm_resource_group.rg.name
  enabled                    = true
  storage_account_name       = data.azurerm_storage_account.storage_account.name
  app_service_plan_id        = azurerm_service_plan.slo_app_plan.id
  storage_account_access_key = data.azurerm_storage_account.storage_account.primary_access_key
  https_only                 = true
  version                    = "~4"

  identity {
    type = "UserAssigned"
    identity_ids = [ data.azurerm_user_assigned_identity.identity.id ]
  }

  app_settings = {
    "FUNCTIONS_WORKER_RUNTIME"     = "node"
    "WEBSITE_NODE_DEFAULT_VERSION" = "~18"
    "keyVault_VaultUri"            = data.azurerm_key_vault.key-vault.vault_uri
    "ResourceGroup"                = var.resource_group
    "SubscriptionId"               = var.target-subscription-id
    "UserAssignedManagedIdentity"  = data.azurerm_user_assigned_identity.identity.name
    "connectionRuntimeUrl"         = jsondecode(azurerm_resource_group_template_deployment.servicebus_connection.output_content).connectionRuntimeUrl.value
  }
}

This is my connections.json...

{
    "managedApiConnections": {
        "servicebus": {
            "api": {
                "id": "/subscriptions/@appsetting('SubscriptionId')/providers/Microsoft.Web/locations/uksouth/managedApis/servicebus"
            },
            "authentication": {
                "identity": "/subscriptions/@appsetting('SubscriptionId')/resourcegroups/@appsetting('ResourceGroup')/providers/Microsoft.ManagedIdentity/userAssignedIdentities/@appsetting('UserAssignedManagedIdentity')",
                "type": "ManagedServiceIdentity"
            },
            "connection": {
                "id": "/subscriptions/@appsetting('SubscriptionId')/resourceGroups/@appsetting('ResourceGroup')/providers/Microsoft.Web/connections/servicebus"
            },
            "connectionProperties": {
                "authentication": {
                    "audience": "https://servicebus.azure.net",
                    "identity": "/subscriptions/@appsetting('SubscriptionId')/resourcegroups/@appsetting('ResourceGroup')/providers/Microsoft.ManagedIdentity/userAssignedIdentities/@appsetting('UserAssignedManagedIdentity')",
                    "type": "ManagedServiceIdentity"
                }
            },
            "connectionRuntimeUrl": "@appsetting('connectionRuntimeUrl')"
        }
    }
}

And finally, this is the JSON representation of my workflow which just has 1 service bus action after the trigger...

{
    "definition": {
        "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
        "actions": {
            "Get_messages_from_a_queue_(peek-lock)": {
                "inputs": {
                    "host": {
                        "connection": {
                            "referenceName": "servicebus"
                        }
                    },
                    "method": "get",
                    "path": "/@{encodeURIComponent(encodeURIComponent('narvar_events'))}/messages/batch/peek",
                    "queries": {
                        "maxMessageCount": 20,
                        "queueType": "Main",
                        "sessionId": ""
                    }
                },
                "runAfter": {},
                "type": "ApiConnection"
            }
        },
        "contentVersion": "1.0.0.0",
        "outputs": {},
        "triggers": {
            "When_a_HTTP_request_is_received": {
                "kind": "Http",
                "type": "Request"
            }
        }
    },
    "kind": "Stateful"
}

Where am I going wrong here?

The API connection shows as 'connected'...

enter image description here

And the user assigned managed identity has the 'Azure Service Bus Data Owner' role against the service bus...

enter image description here

0

There are 0 answers