78

Deallocate VM based on Activity Log

In many organizations, virtual machines in Azure are kept running even when they’re no longer needed – especially during business…

In many organizations, virtual machines in Azure are kept running even when they’re no longer needed – especially during business hours.

But what if users could simply shut down their VMs themselves, and behind the scenes, a Logic App would take care of fully deallocating them to save costs?

That’s exactly what I implemented: a lightweight automation that listens to shutdown events in the Azure Activity Log and triggers a deallocation to avoid unnecessary charges.

In this post, I’ll walk you through how it works, how to deploy it, and how it helps reduce cloud spend without requiring your users to change their habits.

💡 Why is it useful?

  • Cost Optimization: You avoid unnecessary compute costs when VMs are not actively in use.
  • Automation: Users and Admins don’t need to worry about remembering to deallocate machines — it’s done for them.
  • Visibility: You can track the automation via logs and even add alerts if needed.

⚙️ Components You’ll Need

  • Logic App with Managed Identity
  • API Connection to managed API – AzureVM
  • Alert Rule
  • Action Group
  • Role Assignment – e.g. Virtual Machine Contributor

🎯 How can we achieve this?

Like the title already says – by using ActivityLogs. 

In this case the user initiated shutdown will create a log which got explicit details in the alert context. 

Key sentences like “Virtual Machine is stopping” and “or due to a guest activity” are unique in combination to the event of the OS internal initiated shutdown.

So we use this information to build our automation.

🤖 Now, let’s automate!

So – we know the correct information but how can we use this?

Alert Rule

(https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-types#activity-log-alerts)

To not let this alert get triggered to often we will be more precise and set the following limits:

  • Resource type: “Microsoft.Compute/virtualMachines”
  • Reason: “UserInitiated”
  • Category: “ResourceHealth”

At this point we got an alert which get triggered every time when a new ActivityLog event appears with the limits above. 

Action Group

(https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/action-groups)

To react to such an event we need an ActionGroup. This defines what should happen after the alert / event occurs. 

Define the action to run the Logic App like you can see below.

At this point we might create our own Azure Runbook and code this with the right PowerShell cmdlets but let us keep it as simple as possible. 

We will use a LogicApp which get triggered by the action group. 

LogicApp

(https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-overview)

The LogicApp will get triggered via a HTTP request and firstly initiate all of the required variables – based on the HTTP body:

  • Variable: VMName
    • Name: VMName
    • Type: String
    • Value: “triggerBody()?[‘data’][‘essentials’][‘configurationItems’][0]”
  • Variable: LogDetails
    • Name: Details
    • Type: String
    • Value: “triggerBody()?[‘data’][‘alertContext’][‘properties’][‘details’]”
  • Variable: Resourcegroup
    • Name: RGName
    • Type: String
    • Value: “triggerBody()?[‘data’][‘essentials’][‘targetResourceGroup’]”
  • Variable: Subscription
    • Name: SubId
    • Type: String
    • Value: “split(triggerBody()?[‘data’][‘essentials’][‘alertId’], ‘/’)[2]”

With these variables we just need a condition which checks the variable “Details” if it contains “Virtual Machine is stopping” and/or “or due to a guess activity” like we found out at the beginning.

So we can filter the event if it is caused by a user initiated shutdown.

If the condition is true, then the VM should be deallocated by the logic app built-in task. 

At this point we just implement this by using the “VMName”, “RGName” and “SubId” variables, so we make it more subscription and resource group independent.

That’s it!

Further Requirements

Managed Identity

(https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)

Role Assignment

(https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments)

You can also limit the role assignment at a different scope but keep in mind that the VMs have to be within that scope.

API Connection

(https://learn.microsoft.com/en-us/azure/connectors/introduction)

For the LogicApp to make it possible that the Managed Identity can reach the managed API AzureVM.

✅ Testing & Logs

Deploy the Logic App, shut down a VM from within the OS (e.g. Windows Start > Shutdown), and check the Activity Log and Logic App Run History to confirm the deallocation action has triggered successfully.

🧠 Lessons Learned

  • Shutting down a VM != deallocation
  • Automating repetitive tasks like this leads to real savings and improved governance
  • Using Terraform or Bicep helps keep things modular and maintainable
  • Start simple — don’t force complexity early on

🚀 Simple Overview

  1. User shutdown VM
  2. New ActivityLog entry appears
  3. Alert rule get’s triggered with the following limitations:
    1. Category: ResourceHealth
    2. Reason: UserInitiated
    3. ResourceType: VirtualMachine
  4. Defined action in the action group sends a http request to the LogicApp
  5. LogicApp starts
    1. Initializes variables
    2. Checks if the ActivityLog contains the right details to confirm it is an OS internal shutdown
    3. Deallocate the VM
  6. Save Money!

Solutions as Code

Simon

Cloud Engineer focused on Azure, Terraform & PowerShell. Passionate about automation, efficient solutions, and sharing real-world cloud projects and insights. ⸻ Let me know if you’d like to make it more casual, technical, or personalized.

Leave a Reply

Your email address will not be published. Required fields are marked *