Trigger logic app when Azure Virtual Desktop starts

We have several use cases where we want to “do something” when a user starts an Azure Virtual Desktop. One method could be a login/startup script, but this would run under the user’s or Managed Identity’s context.

A better way is to use an Azure Event Grid System Topic on the resource group that contains the VM’s, which can then forward any event that happens in the resource group.

A system topic is easily deployed using ARM:

    {
        "type": "Microsoft.EventGrid/systemTopics",
        "apiVersion": "2021-12-01",
        "name": "evgt-listenToAvdEvents-01",
        "location": "global",
        "properties": {
            "source": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/rg-avd-weeu-01')]",
            "topicType": "microsoft.resources.resourcegroups"
        }
    }

That having been deployed, we’ll deploy a logic app that is triggered by the topic. In this case, I want to do some advanced filtering so the logic app is only triggered when a VM is started by a user (vs automation). This is indicated by the Guid (principal ID) of Microsoft’s AVD serviceprincipal, in our case 068e1c948d874baba249f9a122cd8003 because we use ‘Start On Connect

To use advanced filtering in a logic app, use “enableAdvancedFilteringOnArrays”: true

The full trigger section of the logic app (in ARM) is as follows:

                    "triggers": {
                        "When_a_resource_event_occurs": {
                            "splitOn": "@triggerBody()",
                            "type": "ApiConnectionWebhook",
                            "inputs": {
                                "body": {
                                    "properties": {
                                        "destination": {
                                            "endpointType": "webhook",
                                            "properties": {
                                                "endpointUrl": "@{listCallbackUrl()}"
                                            }
                                        },
                                        "filter": {
                                            "includedEventTypes": [
                                                "Microsoft.Resources.ResourceActionSuccess",
                                                "Microsoft.Resources.ResourceDeleteSuccess",
                                                "Microsoft.Resources.ResourceWriteSuccess"
                                            ],
                                            "subjectBeginsWith": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/rg-avd-weeu-01/providers/Microsoft.Compute/virtualMachines')]",
                                            "enableAdvancedFilteringOnArrays": true,
                                            "advancedFilters": [
                                                {
                                                    "operatorType": "StringIn",
                                                    "key": "data.authorization.action",
                                                    "values": [
                                                        "Microsoft.Compute/virtualMachines/start/action"
                                                    ]
                                                },
                                                {
                                                    "operatorType": "StringIn",
                                                    "key": "data.authorization.evidence.principalId",
                                                    "values": [
                                                        "068e1c948d874baba249f9a122cd8003"
                                                    ]
                                                }
                                            ]
                                        },
                                        "topic": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/rg-avd-weeu-01')]"
                                    }
                                },
                                "host": {
                                    "connection": {
                                        "name": "@parameters('$connections')['azureeventgrid']['connectionId']"
                                    }
                                },
                                "path": "[concat('/subscriptions/@{encodeURIComponent(''',subscription().subscriptionId,''')}/providers/@{encodeURIComponent(''Microsoft.Resources.ResourceGroups'')}/resource/eventSubscriptions')]",
                                "queries": {
                                    "x-ms-api-version": "2021-12-01"
                                }
                            }
                        }
                    },

You may also want to use the VM’s name in your logic app, this is easily parsed from the Subject field, e.g. as follows:

"Parse_Subject": {
    "runAfter": {},
    "type": "InitializeVariable",
    "inputs": {
        "variables": [
            {
                "name": "subject",
                "type": "string",
                "value": "@triggerBody()?['subject']"
            }
        ]
    }
},
"Parse_MachineName": {
    "runAfter": {
        "Parse_Subject": [
            "Succeeded"
        ]
    },
    "type": "InitializeVariable",
    "inputs": {
        "variables": [
            {
                "name": "machineName",
                "type": "string",
                "value": "@{last(split(variables('subject'),'/'))}"
            }
        ]
    }
},

Important considerations:

  1. the Logic App needs to have a managed identity
  2. The LA’s MI needs to have the EventGrid Contributor role on the system topic
  3. you cannot edit this logic app through the gui, doing so will break it and cause the following error: “Unable to match incoming request to an operation”

Adding eventgrid contributor:

New-AzRoleAssignment -ObjectId $la.Identity.PrincipalId -RoleDefinitionName "EventGrid Contributor" -Scope "/subscriptions/$($context.Subscription.Id)"
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments