If you manage cloud infrastructure at scale, you know the pain: VMs running 24/7 when they only need to be active during business hours. The cost impact is immediate, but the fix — automating start/stop — often introduces its own headache: storing service principal credentials, rotating secrets, and hoping nothing breaks when a certificate expires.

In this post, I'll show you how to use Azure Logic Apps with System-assigned Managed Identity to schedule VM start and stop operations — zero secrets, zero credential rotation, and fully auditable through Azure Activity Logs.

Real-world use case: In a recent engagement, we managed a fleet of development and staging VMs that only needed to run during working hours (07:00–18:00 CET). Automating start/stop with Logic Apps saved approximately 65% on compute costs for non-production environments.

The Problem: Scheduled VM Management Without Credential Hell

The traditional approach to automating Azure VM operations requires creating a Service Principal, generating a client secret, storing it somewhere (Key Vault, pipeline variables, a config file someone will forget to rotate), and wiring up authentication code.

That works — until the secret expires, the cert rotates on the wrong schedule, or a junior engineer copies the credentials into a plaintext config. Managed Identity eliminates all of that. The Azure platform handles token issuance and rotation transparently, and RBAC controls exactly what the identity can do.

Architecture Overview

The solution involves three Azure components working together:

  • Azure Logic App (Consumption) — orchestrates the schedule and HTTP call
  • System-assigned Managed Identity — authenticates to Azure Management API without credentials
  • Azure RBAC — grants the identity permission to operate the VM

The Logic App triggers on a Recurrence schedule, then fires an authenticated HTTP POST to the Azure REST API to start or deallocate the VM. The Managed Identity token is injected automatically by the Azure platform — your code never sees it.

Prerequisites

  • An Azure subscription with an existing Virtual Machine
  • Contributor or Owner access to the VM's resource group
  • A Logic App created (Consumption tier recommended for simple schedules)

Step-by-Step Implementation

Step 1: Enable System-Assigned Managed Identity on the Logic App

Navigate to your Logic App in the Azure Portal:

  1. Go to SettingsIdentity
  2. Under the System assigned tab, toggle Status to On
  3. Click Save — Azure creates a service principal in Entra ID automatically

Note the Object (principal) ID that appears. You will need this for the RBAC assignment.

Step 2: Assign Virtual Machine Contributor Role

The Managed Identity needs permission to start/stop the VM. Assign it at the VM or Resource Group level:

  1. Navigate to your VM (or its Resource Group) → Access Control (IAM)
  2. Click AddAdd role assignment
  3. Role: Virtual Machine Contributor
  4. Assign access to: Managed Identity
  5. Select your Logic App from the list → Save

Or via PowerShell:

$principalId = (Get-AzSystemAssignedIdentity `
    -ResourceGroupName 'rg-prod' `
    -Name 'la-vm-scheduler').PrincipalId

New-AzRoleAssignment `
    -ObjectId $principalId `
    -RoleDefinitionName 'Virtual Machine Contributor' `
    -ResourceGroupName 'rg-prod-vms'

Scope recommendation: Assign at VM level (least privilege) unless you need to control multiple VMs. Resource Group scope works if the RG contains only the VMs this Logic App should manage.

Step 3: Build the Logic App Workflow

Open Logic App Designer and build the following two-step workflow:

Trigger: Recurrence

Setting Value
Frequency Day
Interval 1
Time zone (UTC+01:00) Stockholm / W. Europe Standard Time
At these hours 7 (for 07:00 start)
At these minutes 0

Action: HTTP

Add a new step, search for HTTP, and configure:

Setting Value
Method POST
URI Azure Management REST API endpoint (see below)
Authentication Managed Identity
Audience https://management.azure.com

URI for Start VM:

https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachines/{vmName}/start?api-version=2023-03-01

URI for Stop (Deallocate) VM:

https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachines/{vmName}/deallocate?api-version=2023-03-01

Important: Use /deallocate — not /powerOff. The powerOff action stops the OS but keeps the VM in an allocated state, meaning you still pay for compute. Deallocate releases the compute resources entirely.

Step 4: Save and Test

  1. Save the Logic App
  2. Click Run TriggerRun to execute manually
  3. Check Run History — the HTTP action should return 202 Accepted
  4. Verify in the VM overview that the Power state changes to Running

Common Issues & Fixes

403 Forbidden

The Managed Identity does not have the required RBAC role. Double-check the role assignment scope and ensure you selected the correct Logic App identity. Allow up to 5 minutes for propagation after assigning the role.

400 Bad Request

Usually a malformed URI. Verify subscriptionId, resourceGroupName, and vmName are all correct and URL-encoded if they contain special characters.

VM Starts at the Wrong Time

Always set the Time Zone explicitly in the Recurrence trigger. If left blank, Logic Apps defaults to UTC. For Stockholm, use W. Europe Standard Time — this handles DST automatically.

Pro Tips

  • Add a second Logic App (or parallel branch) with /deallocate at 18:00 for full start/stop automation
  • Use Logic App Parameters for subscriptionId, resourceGroupName, and vmName to make the workflow reusable across environments
  • Enable Diagnostic Settings on the Logic App and send logs to Log Analytics for full run history retention beyond 90 days
  • Tag your VMs with AutoShutdown: true and use Azure Policy + Logic Apps to enforce schedules fleet-wide
  • For multiple VMs, use a For Each loop over a JSON array of VM names — one Logic App controls all of them

Summary

Azure Logic Apps with Managed Identity is the cleanest way to automate VM scheduling in Azure. No service principals. No stored secrets. No credential rotation. The Managed Identity token is handled entirely by the platform, and RBAC gives you fine-grained, auditable access control.

The full setup takes under 15 minutes and can save significant compute costs on non-production workloads. Once you have the pattern working for one VM, it scales easily to entire fleets.

Have questions or running into issues? Drop a comment below or connect with me on LinkedIn. If this post helped you, share it with your team — your Azure bill will thank you.