Azure Resource Nightly Start/Stop Scheduler
On a schedule, the flow stops tagged non-production VMs (and other deallocatable resources) at end of day and starts them again each morning via Azure Resource Manager, skipping anything tagged keep-on. Posts a Teams summary of what was stopped/started and the estimated cost saved. Cuts cloud spend on idle dev/test environments.
Provided as-is, without warranty of any kind. Review and test each pattern in a non-production environment before deploying it to live automations. See our Terms.
Overview
This solution cuts cloud spend on idle non-production environments using Azure Resource Manager (ARM). It ships as two scheduled cloud flows in one solution: an evening stop flow that deallocates tagged non-prod VMs at end of day, and a morning start flow that powers them back on before the workday. Both skip anything carrying the keep-on opt-out tag and post a Teams + email summary of what was acted on, with an estimated-savings figure.
Why it matters: Dev/test VMs left running overnight and on weekends waste real money. Tag-driven auto start/stop captures that saving on a schedule, with an auditable summary, without anyone remembering to shut things down.
> Connector-first note: Azure Resource Manager has no Power Automate connector in this tenant, so ARM calls use the built-in HTTP action with `ActiveDirectoryOAuth` (a service principal) — the documented FlowLibs ARM pattern. Teams and Outlook use their standard connectors. No Dataverse table is required.
Use Case
An IT/platform team wants non-prod compute powered down outside working hours and back up before the team starts, with cost visibility and a safe opt-out tag. Resources are selected by an environment=dev tag and protected by a keep-on=true tag.
Flow Architecture
Evening_Stop_Sweep (Flow A)
Recurrence (Day @ 19:00 ET)Off-hours stop cadence
Initialize_* (12 variables) (Flow A)
Initialize VariableARM base URL, subscription, API version, env-tag name/value, keep-on name/value, hourly cost, off-hours, power action (deallocate), action label (Stopped), affected-VM accumulator
List_Subscription_VMs (Flow A)
HTTP GET (ARM + AAD OAuth)Lists all VMs in the subscription with tags
Filter_Target_VMs (Flow A)
Filter array (Query)Keeps VMs whose env tag matches AND that are not keep-on
Compose_Estimated_Savings (Flow A)
Composecount × hourly cost × off-hours
Apply_To_Each_Target_VM (Flow A)
Foreach (concurrency 1)Iterates target VMs
Invoke_VM_Power_Action (Flow A)
HTTP POST (ARM + AAD OAuth)Calls …/deallocate on the VM (stops billing)
Append_Affected_VM_Name (Flow A)
Append to string variableBuilds comma-separated VM-name list
Compose_Affected_Summary (Flow A)
ComposeVM names, or None
Post_Summary_To_Teams (Flow A)
Teams PostMessageToConversationPosts action count, VM names, estimated savings
Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_ArmBaseUrl | String | https://management.azure.com | ARM base URL (ports across sovereign clouds) |
| flowlibs_AzureSubscriptionId | String | <configure> | Subscription whose VMs are in scope |
| flowlibs_AzureTenantId | String | <your-tenant-id> | AAD tenant for the OAuth token |
| flowlibs_AzureClientId | String | <configure> | Service principal client id |
| flowlibs_AzureClientSecret | String | <configure> | Service principal secret |
| flowlibs_VmComputeApiVersion | String | 2023-07-01 | Microsoft.Compute REST API version |
| flowlibs_EnvironmentTag | String | environment=dev | tagName=tagValue marking non-prod resources |
| flowlibs_KeepOnTag | String | keep-on=true | tagName=tagValue opt-out tag (never stopped) |
| flowlibs_EstimatedHourlyCostPerVM | String | 0.20 | Estimated hourly compute cost per VM |
| flowlibs_OffHoursPerNight |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| Microsoft Teams | shared_teams | PostMessageToConversation |
| Office 365 Outlook | shared_office365 | SendEmailV2 |
Note — All connections are referenced as solution connection references; the flow is portable between environments as long as a connection is mapped at import time.
Customization Guide
Almost every realistic variant of this flow can be implemented by changing environment variable values. A few cases require small edits inside the flow definition — those are called out explicitly below.
- Per-team windows
- Clone the flow pair per team tag and time zone for staggered start/stop schedules.
- Other resource types
- Extend the ARM calls to scale down AKS node pools, pause Azure SQL databases, or stop App Service plans (different provider paths + power operations).
- Holiday calendar
- Add a lookup that skips the run on company holidays.
- Real cost data
- Replace the flat hourly-cost estimate with a Cost Management query for actual rates per VM SKU.
- Weekend mode
- Add a day-of-week condition to keep resources off across weekends.
Key Expressions
The flow is intentionally light on Power Fx / WDL gymnastics — the heaviest expressions are the branch-name concatenation and the approval outcome check. They are listed below in the order they appear in the flow.
EXPR.01Env / keep-on tag split (Initialize)
Splits the tagName=tagValue env var into name and value parts.
EXPR.02Target filter (Filter_Target_VMs where)
Keeps VMs matching the env tag and not carrying the keep-on opt-out.
EXPR.03ARM list URI
Builds the ARM VM list endpoint.
EXPR.04Power-action URI
Builds the deallocate/start endpoint for the current VM.
EXPR.05ARM OAuth audience
Resolves to https://management.azure.com/.
EXPR.06Estimated savings
VM count times hourly cost times off-hours for an estimated saving.
Customize & download
Generate a ready-to-import copy of this solution with your environment-variable values baked in — available on Base, Pro, or Team.
Upgrade to customize
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.