Updated — 25/09/2024 — Please upgrade to PowerShell 7.4 by November 8, 2024, to avoid potential service disruptions or security vulnerabilities in your Functions apps.
Updated — 07/02/2024 — Adding a schedule to automate auto stop and start for Azure VMs during the weekend.
Updated — 16/09/2022 — Please upgrade to PowerShell 7.2 by December 3, 2022, to avoid potential service disruptions or security vulnerabilities in your Functions apps.
Cost management is one of the things to watch out for when managing Azure resources. If you look at the Azure usage report, Azure virtual machine usage would probably be the highest cost contributor to your bill.
As cost management, the best practice is to assess whether your virtual machines need to be up and running always or if some can be stopped at a specific schedule to save operational costs. In this case, it may be beneficial to create a schedule to stop and start your Azure virtual machines.
In this article, you will learn how to create an Azure Function where you can automate and schedule the start and stop of your Azure virtual machines using Tags and save costs.
Table of Contents
Introduction
Auto-Shutdown Azure VMs by schedule is already a built-in feature in the platform which you can find under the Operations section as shown in the figure below, however, Azure does not have an AutoStart option.

The good news is, that Azure provides multiple options to automate and schedule the startup of your virtual machines, and each option has its pros and cons. I will list the different options below so you can decide which option makes sense for you, is scalable, easy to manage, and is cheaper.
Azure Automation
Azure Automation delivers a cloud-based automation and configuration service that supports consistent management across your Azure and non-Azure environments. It comprises process automation, configuration management, update management, shared capabilities, and heterogeneous features. Automation gives you complete control during deployment, operations, and decommissioning of workloads and resources. To automate the schedule of your virtual machines, you create first an automation account and then follow the steps described in this article.
Automation Tasks (preview)
Microsoft recently announced that you can manage your Azure resources more easily, where you can create automated management tasks for a specific resource or resource group by using automation task templates, which vary in availability based on the resource type. For example, for an Azure storage account, you can set up an automation task that sends you the monthly cost for that storage account. For an Azure virtual machine, you can create an automation task that turns on or turns off that virtual machine on a predefined schedule, you can follow the steps shown in this video by my dear friend Thomas Maurer.
At the time of this writing, the automation tasks option is in preview, it’s not recommended for production workloads, and is excluded from service-level agreements.
Azure Functions (the topic of this article)
With Azure Functions, you can develop more efficiently, an event-driven serverless compute platform that can also solve complex orchestration problems. You can build and debug locally without additional setup, deploy and operate at scale in the cloud, and integrate services using triggers and bindings.
You can read more about Azure Functions here.
How do Automation tasks differ from Azure Automation and Azure Functions?
Automation tasks and Azure Functions are more basic and lightweight than Azure Automation. Currently, you can create an automation task only at the Azure resource level. Behind the scenes, an automation task is a logic app resource that runs a workflow and is powered by the multi-tenant Azure Logic Apps service. To use Automation tasks, you need to have an Office 365 account to connect, however, this is not the case with Azure Functions and Azure Automation.
By comparison, Azure Automation is a cloud-based automation and configuration service that supports consistent management across your Azure and non-Azure environments. The service comprises process automation for orchestrating processes by using Runbooks, configuration management with change tracking and inventory, update management for your virtual machines, shared capabilities, and heterogeneous features. An automation account gives you complete control during deployment, operations, and decommissioning of workloads and resources.
In terms of scalability, Azure Functions is more scalable than the Automation task which is configured individually at the Azure resource level that you want to automate, and if you have different schedules that you want to trigger based on, then Azure Automation is not scalable too. With Functions, you write only the code once that truly matters to your business and scale faster.
On the pricing side, I would say that the price for Azure Functions is the cheapest option than using Automation tasks or Azure Automation. The functions app is cheaper when compared to the logic app which is the automation engine behind Automation tasks. When comparing Logic Apps and Azure Functions, both can run under a consumption plan. That means both have a pay-per-usage billing model. I will suggest using the pricing calculator here to compare what would be cheaper between the two options. But if you have high resource utilization then the function will be cheaper than a logic app. Please review the serverless comparison here.
The winner is Azure Functions in terms of scalability, easy to manage for a large number of resources, and cheaper… Read on!
Prerequisites
To follow this article, you need to have the following:
1) Azure subscription – If you don’t have an Azure subscription, you can create a free one here.
2) Azure Resource Group (RG).
3) Function App with App Service plan consumption-based (more on this in the next section).
4) Enable system-assigned managed identity for the Function App and assign the right permissions on the desired subscription(s) or management group(s) (more on this in the next section).
5) At least one Azure virtual machine is deployed in the desired RG. Please check the following quickstart guide to create a Linux or Windows virtual machine.
- Set the appropriate Tag name and Tag value on the target virtual machine that you want to schedule for auto-shutdown and auto-start (more on this in the next section).
Assuming you have all the prerequisites in place, take now the following steps:
Create Function App
In this section, we’ll create a new function app.
Browse to the Azure Portal and then click Create a resource, and select Function App.
Select your desired subscription, and resource group, and give it a nice name, such as “stop-start-azvm“.
The runtime stack means what language we would like to write the Function logic. Azure Function provides support for mainstream languages or platforms such as .NET, Java, Node.js, Python, Custom Handler, and PowerShell Core.
For this scenario, we need to choose PowerShell Core, as shown in the figure below. At the time of this writing, we can only choose version 7.4, the latest version.
Updated – 27/07/2022 – Please use PowerShell version 7.2 (latest) instead of 7.0. To avoid potential service disruptions or security vulnerabilities in your Functions apps, please upgrade to PowerShell 7.2 by December 3, 2022.
Updated – 24/09/2024 – Please use PowerShell version 7.4 (latest) instead of 7.2. To avoid potential service disruptions or security vulnerabilities in your Functions apps, please upgrade to PowerShell 7.4 by November 8, 2024.
Choose your desired Azure region, and then click Next: Hosting > to continue.

Next, we have to create a new or select an existing Storage account, which is required by the latest version of Azure Function V3.
For Plan type, select ‘Consumption‘ for serverless and event-driven scaling for the lowest minimum cost, this Plan is billed dynamically according to your usage, which helps save money when the usage is small as in this case.
As a side note, the ‘Premium‘ is for enterprise-level, serverless applications with event-based scaling and network isolation, and the ‘App Service Plan‘ is for reusing compute from the existing app service plan.
Click Next: Monitoring > to continue.

On the Monitoring tab, Application Insights can be used to monitor the running health of the function, and can also be used as a console output capture when debugging the function. It’s up to you to decide whether to enable it or not. It’s strongly recommended to Enable Application Insights, so you can test your function in the Azure portal and monitor it.
Click Review + Create > to review your input, and then select Create.

After the creation is completed, we need to configure permissions for this Function App to access our virtual machines, which could be deployed across multiple subscriptions.
Open your Function App, and then select Identity under the Settings menu, turn on the System assigned to toggle to On, then click Save as shown in the figure below, and then confirm by selecting Yes.
Once the system-assigned managed identity is enabled, the Function App resource will be registered with Azure Active Directory (Azure AD). After it’s registered, we can control its access to other services like storage accounts, subscriptions, management groups, etc.

Now depending on the scope and coverage, you want to give access to the Function App to stop and start your virtual machines. You can give access to a single VM, resource group(s), subscription(s), or management group(s).
In this example, we will give access to the root management group where all our subscriptions are organized. You need to select your desired scope, then go to Access Control (IAM), select Role Assignments, and then Click + Add | Add role assignment.
Under “Assign access to” choose Function App, search for the Function App name we just created, then add it as a “Virtual Machine Contributor” role as shown in the figure below, and then click Save. The Virtual Machine Contributor role has enough privilege to stop and start the virtual machines.

Please note that we need to give that Function App to stop and start VM permission only and not more, please do not give an Owner or Contributor role. Always make sure to follow the principle of the least privilege.
Updated – 18/08/2023 – Please make sure to update your Function application to the Functions 4.x runtime for continued support. You can do that by going to Configuration under Settings | Function runtime settings | Runtime version ~4.

Function Code and Trigger
Now we have our Function App up and running, let’s create a new Function.
From the Functions blade under Functions, select + Create to create a Function as shown in the figure below.

Next, select “Time trigger” as shown in the figure below, and then click Create.

After the function is created, go to Integration and then select Time (Timer) under Trigger as shown in the figure below.

Next, enter the timing logic’s CRON expression in the “Schedule” field, as shown in the figure below.
0 0 * * * 1-6
Take 0 0 * * * 1-6 as an example. This means that the function will trigger from Monday to Saturday, every hour (hourly). Click Save.

You need to enter a CRON expression of the format ‘{second} {minute} {hour} {day} {month} {day of week}’ to specify the schedule.
As a reference, here is the basic format of the CRON expressions in Azure Functions:
{second} {minute} {hour} {day} {month} {day of the week}
e.g. 0 * * * * * (is equal to every minute).
The following values are allowed for the different placeholders:
| Value | Allowed Values | Description |
|---|---|---|
| {second} | 0-59; * | {second} when the trigger will be fired |
| {minute} | 0-59; * | {minute} when the trigger will be fired |
| {hour} | 0-23; * | {hour} when the trigger will be fired |
| {day} | 1-31; * | {day} when the trigger will be fired |
| {month} | 1-12; * | {month} when the trigger will be fired |
| {day of the week} | 0-6; SUN-SAT; * | {day of the week} when the trigger will be fired |
For more information about Azure Functions time trigger, please refer to the official documentation here.
Please keep in mind that the trigger executes at UTC time. So for Bern (UTC+1), a trigger at 18:00 (UTC) executes at 19:00 Switzerland (UTC+1) wintertime and 20:00 (UTC+2) summertime. You will see later in the PowerShell code how we convert the UTC time zone to make sure that we AutoShutdown and AutoStart the VM based on “West Europe Standard Time” and not UTC.
Next, go to Code + Test of the function, and replace the default PowerShell code for run.ps1 with the below code. Please make sure to replace the $subscriptionids variable values with your Azure subscription(s) information.
# Input bindings are passed in via param block.
param($Timer)
# Add all your Azure Subscription Ids below
$subscriptionids = @"
[
"aaaaaaaa-1234-5678-0000-999999999999",
"bbbbbbbb-1234-5678-0000-999999999999",
"cccccccc-1234-5678-0000-999999999999",
"dddddddd-1234-5678-0000-999999999999",
"eeeeeeee-1234-5678-0000-999999999999",
"ffffffff-1234-5678-0000-999999999999",
"gggggggg-1234-5678-0000-999999999999",
"hhhhhhhh-1234-5678-0000-999999999999",
"iiiiiiii-1234-5678-0000-999999999999",
"jjjjjjjj-1234-5678-0000-999999999999"
]
"@ | ConvertFrom-Json
# Convert UTC to West Europe Standard Time zone
# If you live in a different Time zone, please make sure to update the $date variable below
# Check > https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones?view=windows-11#time-zones
$date = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::Now,"W. Europe Standard Time")
foreach ($subscriptionid in $subscriptionids) {
# Selecting Azure Sub
Set-AzContext -SubscriptionId $SubscriptionID | Out-Null
$CurrentSub = (Get-AzContext).Subscription.Id
If ($CurrentSub -ne $SubscriptionID) {
Throw "Could not switch to SubscriptionID: $SubscriptionID"
}
$vms = Get-AzVM -Status | Where-Object {($_.tags.AutoShutdown -ne $null) -and ($_.tags.AutoStart -ne $null)}
$now = $date
foreach ($vm in $vms) {
if (($vm.PowerState -eq 'VM running') -and ($now -gt $(get-date $($vm.tags.AutoShutdown))) ) {
Stop-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Confirm:$false -NoWait -Force
Write-Warning "Stop VM - $($vm.Name)"
}
elseif (($vm.PowerState -eq 'VM deallocated') -and ($now -gt $(get-date $($vm.tags.AutoStart))) -and ($now -lt $(get-date $($vm.tags.AutoShutdown))) ) {
Start-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -NoWait
Write-Warning "Start VM - $($vm.Name)"
}
}
}
Although param($Timer) in the script above is not used in the code logic, however, it cannot be deleted. This is a required parameter of the Timer Trigger.
The Set-AzContext, Get-AzVM, Start-AzVM, and Stop-AzVM cmdlets are not PowerShell modules, they are from the Azure (Az) PowerShell module. Since Azure PowerShell is already installed on the Function host, those commands can be used directly (more on this in a bit).
Click Save to save your code.

Now before we test our code, we need to enable the Az PowerShell module on the Function App under the App Service Editor.
From the Functions blade under Development Tools, select App Service Editor (Preview), and then click on Go-> as shown in the figure below.

A new browser tab will open where you can edit the requirements for the Function host.
As shown in the figure below, select requirements.psd1 and uncomment the ‘Az’ = ‘6.*’ on line number 7. This is a very important step. Otherwise, the function won’t be able to use the Az module, and thus, the PowerShell code will fail to run.
Updated – 18/08/2023 – Please make sure to use the ‘Az’ = ‘9.*’ OR ‘Az’ = ’10.*’ on line number 7 and NOT the ‘Az’ = ‘6.*’ anymore.
Updated – 25/09/2024 – Please make sure to use the ‘Az’ = ’11.*’ OR ‘Az’ = ’12.*’ on line number 7 and NOT the ‘Az’ = ’10.*’ anymore. Additionally, reducing the number of modules installed to only what you need is appropriate. So, in this example, we need only the “Az.Accounts” and the “Az.Compute” modules. You can add the following two modules to the requirements.psd1 file, as follows. We recommend not importing all of the Az modules ‘Az’ = ’12.*’ as it will increase performance on cold starts.
@{
# For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'.
# To use the Az module in your function app, please uncomment the line below.
'Az.Accounts' = '3.*' # Latest module as of 25.09.2024
'Az.Compute' = '8.*' # Latest module as of 25.09.2024
}

The next update that you want to make is to select the host.json, and then increase the default function timeout to 10 minutes, as shown in the figure below. This is the maximum you are allowed for the Consumption Plan. This will help prevent the function from timing out after the default 5 minutes.
{
"version": "2.0",
"managedDependency": {
"enabled": true
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
},
"functionTimeout": "00:10:00"
}

Close the App Service Editor tab.
Set Tags for Azure VMs
The final step is to set the Tag names and Tag values on the desired virtual machine(s) that you want to schedule for AutoShutdown and AutoStart.
There are multiple ways to set and create tags for your Azure resources. In this example, I will show you two options by using the Azure Portal and the Azure PowerShell.
Navigate to your virtual machine in the Azure Portal, on the Overview page, select (change) next to Tags.
As shown in the figure below, enter the Tag names and adjust the value(s) based on your desired schedule.
Please make sure the Tag names are entered correctly (AutoShutdown and AutoStart), they are case-sensitive, otherwise, the function won’t work.
In this example, the AutoShutdown is set to 20:00 at night, and AutoStart is set to 07:00 in the morning.

The good news is, based on the values set on the tags, the Function will automate the stop and start for the VM.
Please note that you can have different values (times) on different virtual machines, and since the function will trigger hourly, it will stop and start the VM based on the values you enter here.
What about if you want to stop and start the VM at half past the hour? In this case, we need to decrease the time trigger and run the function every 30 minutes instead of every hour.
Now if you have many VMs you want to automate this process, we could use Azure PowerShell to tag our VMs.
Assuming you already have some Tags assigned on your Azure virtual machines, the PowerShell commands below will only add additional tags called AutoShutdown and AutoStart with their respective values “20:00” and “07:00”. For the VMs variable, please replace the “rgname“, and “vmname” with your Azure information.
$vms = @"
[
{
"rgname": "resource-group1",
"vmname": "vm1"
},
{
"rgname": "resource-group2",
"vmname": "vm2"
},
{
"rgname": "resource-group3",
"vmname": "vm3"
}
]
"@ | ConvertFrom-Json
foreach ($vm in $vms) {
$tags = (Get-AzResource -ResourceGroupName $vm.rgname -Name $vm.vmname).Tags
$tags += @{ AutoShutdown = "20:00"; AutoStart = "07:00" }
Set-AzResource -ResourceGroupName $vm.rgname -Name $vm.vmname -ResourceType "Microsoft.Compute/VirtualMachines" -Tag $tags
}
If you want to tag a virtual machine using the Azure CLI, please see how to tag a virtual machine in Azure using the Azure CLI.
Automate Schedule During the Weekend
Another interesting scenario is to schedule the auto stop and start for Azure VMs during the weekend. In other words, some VM resources should not start during the weekend and remain off, while others must be started and available during the weekend.
To make this happen, we need to add another Tag to specify if the VM should be On or Off during the weekend.
As shown in the figure below, enter the Tag names and adjust the value(s) based on your desired schedule and weekend preference. Please make sure the Tag names are entered correctly (AutoShutdown / AutoStart / Weekend), otherwise, the function won’t work.
In this example, the AutoShutdown is set to 19:00, the AutoStart is set to 07:00 in the morning, and the Weekend is ‘Off‘.
If you want the VM to be on during the weekend, then make sure to set the value to ‘On‘ for the Tag ‘Weekend‘.

Next is to use the following PowerShell code in your Azure function under the run.ps1 section that will automate this scenario:
# Input bindings are passed in via param block.
param($Timer)
# Add all your Azure Subscription Ids below
$subscriptionids = @"
[
"aaaaaaaa-1234-5678-0000-999999999999",
"bbbbbbbb-1234-5678-0000-999999999999",
"cccccccc-1234-5678-0000-999999999999",
"dddddddd-1234-5678-0000-999999999999",
"eeeeeeee-1234-5678-0000-999999999999"
]
"@ | ConvertFrom-Json
# Convert UTC to West Europe Standard Time zone
# If you live in a different Time zone, please make sure to update the $date variable below
# Check > https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones?view=windows-11#time-zones
$date = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::Now, "W. Europe Standard Time")
foreach ($subscriptionid in $subscriptionids) {
# Selecting Azure Sub
Set-AzContext -SubscriptionId $SubscriptionID | Out-Null
$CurrentSub = (Get-AzContext).Subscription.Id
If ($CurrentSub -ne $SubscriptionID) {
Throw "Could not switch to SubscriptionID: $SubscriptionID"
}
$vms = Get-AzVM -Status | Where-Object { ($_.tags.AutoShutdown -ne $null) -and ($_.tags.AutoStart -ne $null) -and ($_.tags.Weekend -ne $null) }
$now = $date
foreach ($vm in $vms) {
if (($vm.PowerState -eq 'VM running') -and ($now -gt $(get-date $($vm.tags.AutoShutdown))) ) {
Stop-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Confirm:$false -NoWait -Force
Write-Warning "Stop VM - $($vm.Name)"
}
elseif (($vm.PowerState -eq 'VM deallocated') -and ($vm.tags.Weekend -eq 'Off') -and ($date.dayofweek.value__ -in 1..5) -and ($now -gt $(get-date $($vm.tags.AutoStart))) -and ($now -lt $(get-date $($vm.tags.AutoShutdown))) ) {
Start-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -NoWait
Write-Warning "Start VM - $($vm.Name)"
}
elseif (($vm.PowerState -eq 'VM deallocated') -and ($vm.tags.Weekend -eq 'On') -and ($date.dayofweek.value__ -in 0..6) -and ($now -gt $(get-date $($vm.tags.AutoStart))) -and ($now -lt $(get-date $($vm.tags.AutoShutdown))) ) {
Start-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -NoWait
Write-Warning "Start VM - $($vm.Name)"
}
}
}
Please note that it’s important that the CRON expression for the trigger includes “Saturday” and “Sunday” for the Weekend tags to work. The CRON expression of the timing logic in the “schedule” field for the “function.json” is shown below:
"schedule": "0 0 * * * 0-6",
This means that the function will trigger every hour (hourly) from Monday to Sunday, where 0 starts with Sunday through Saturday 6.
Test the Function Schedule
At this step, we are ready to test our function!
To test your function manually from the Azure portal, you need to set the Cross-Origin Resource Sharing (CORS) in your Function App. Running your function in the portal requires the app to explicitly accept requests from https://portal.azure.com. This is known as cross-origin resource sharing (CORS).
In your Function App, under API, select CORS and then add the Allowed Origins below. This is only required to test the function manually in the Azure portal.
https://portal.azure.com
https://functions.azure.com
https://functions-staging.azure.com
https://functions-next.azure.com

Next, you need to change the tag values for AutoShutdown and AutoStart for a VM to a closer hour depending on what time you created the function app above.
Next, you can trigger the function manually by going to the Function App Overview page, then select your function under Functions, then going to Code + Test, clicking “Test/Run” and clicking Run. If everything is OK you should not see any error in the Logs console as shown in the figure below and now your VM should be stopped or started.

The next option is to simply wait for the next hour when the function will automatically trigger.
If you see any error in the output logs, then go to the Monitor section and check the errors in more detail (you must have Application Insights enabled for the monitoring to work).

If we look at the Activity log for the VM, we can see that schedule worked successfully and the VM was turned off at 20:00 and started at 07:00 in the morning. The event was automatically initiated by the Function App name.

That’s there you have it!
Summary
In this article, I showed you how to create an Azure Function so you can schedule the start and stop of your Azure virtual machines based on the defined Tag names and values. This will help you to save costs and you only pay for virtual machines when you’re running them.
With Azure Functions, you can build more scalable and stable event-driven applications with a microservices-friendly approach using Functions. Please refer to the pricing pages below for more details:
Logic App Pricing: https://azure.microsoft.com/en-us/pricing/details/logic-apps/
Function App Pricing: https://azure.microsoft.com/en-us/pricing/details/functions/
Azure Automation Pricing: https://azure.microsoft.com/en-us/pricing/details/automation/
As a monthly bonus for the consumption plan pricing, both Azure Functions and Logic Apps include a monthly free grant* (the First 4,000 actions are free for logic apps and 400,000 GB-s for functions).
*Free grants apply to paid, consumption subscriptions only. In that scenario, you can leverage the Azure function to automate and schedule the start and stop of your Azure virtual rather than using a logic app (automation tasks) if it is under the free limit.
__
Thank you for reading my blog.
If you have any questions or feedback, please leave a comment.
-Charbel Nemnom-
Hello Tim, thanks for the update!
Yes, you are right for your specific use case where shutdown time is less than start time.
Could you please share the separate logic that you created to check first if the start time is greater than the shutdown time?
On top of my head without testing, I am wondering if in the last portion of the “if” statement for powering ON the VM to use OR instead of AND like the following:
Because when the current time will be greater than the AutoStart, the VM will be powered ON if it’s in the OFF state, however, in the second portion (or), the current time is NOT less than the AutoShutdown in your case, the VM will also be powered ON if it’s in the OFF state.
I believe this should work for your specific use case, but I need to test it in a normal use case where the AutoStart (E.g. 8:00) and the AutoShutdown (E.g. 22:00).
Thanks!
I just introduced another IF condition to see if the shutdown time was greater than the start time. If so, the logic does not need to consider rather it’s also less than the start time. As of today, this is the code I am running:
if (($(get-date $($vm.tags.AutoShutdown)) -gt $(get-date $($vm.tags.AutoStart))))
{
if (($vm.PowerState -ne ‘VM running’) -and ($now -ge $(get-date $($vm.tags.AutoStart)) ) -and (($now -lt $(get-date $($vm.tags.AutoShutdown))) -or ($now -le $adddatevalue)))
{
Start-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -NoWait
Write-Warning “Start VM – $($vm.Name)”
}
else {
Write-Warning “No conditions met to Start or Stop this VM: $($vm.Name)”
}
}
#This means the start time is greater than the stop time, which means the logic above will not work
elseif (($vm.PowerState -ne ‘VM running’) -and ($now -ge $(get-date $($vm.tags.AutoStart)))) {
Start-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -NoWait
Write-Warning “Start VM – $($vm.Name)”
}
else {
Write-Warning “No conditions met to Start or Stop this VM: $($vm.Name)”
}
Regarding your proposed change to address my use case. Changing the logic to use OR instead of AND will not work correctly. The VM will get powered on earlier than the Start Time. For instance, if the start time is 2000 and the stop time is 0800 and the VM is powered off, given a current time of 0700, the condition of it not running is true, the condition of the current time is greater than a start time of 2000 is False, but if you make the next condition OR, the current time will be less than AutoShutdown so the VM will start, even though the stop time is 0800 and it’s only 0700.
Thank you, Tim, for sharing the code.
I looked at it and I see a contradiction of what you’ve said before.
You said that you changed the code to check first if the start time is greater than the shutdown time.
I see in the code above the opposite, you are checking if the shutdown time was greater than the start time!!!
The next thing, I don’t see anywhere when are you shutting down the VM if it’s running?
Is this the complete code, are you sure?
Thanks!
When I edit the requirement.ps1 line 7 to ‘Az’ = ‘6.*’ & run the test then i get 202. But in the filesystem logs I get an error:
2022-07-27T19:00:42 Welcome, you are now connected to log-streaming service. The default timeout is 2 hours. Change the timeout with the App Setting SCM_LOGSTREAM_TIMEOUT (in seconds).
2022-07-27T19:00:48.174 [Information] Executing ‘Functions.TimerTrigger1′ (Reason=’This function was programmatically called via the host APIs.’, Id=6214c04d-e53e-4d43-8cb2-dc7444a6cc07)
2022-07-27T19:00:48.192 [Warning] The Function app may be missing a module containing the ‘Set-AzContext’ command definition. If this command belongs to a module available on the PowerShell Gallery, add a reference to this module to requirements.psd1. Make sure this module is compatible with PowerShell 7. For more details, see https://aka.ms/functions-powershell-managed-dependency. If the module is installed but you are still getting this error, try to import the module explicitly by invoking Import-Module just before the command that produces the error: this will not fix the issue but will expose the root cause.
Hello Imran, please make sure to use the ‘Az’ = ‘8.*’ on line number 7 in the requirement.ps1 and NOT the ‘Az’ = ‘6.*’ anymore.
Could you please try and let me know if it works for you?
Thanks!
Hi Charbel,
After changing the value to 8. Now i get new error:
Connected!
2022-07-28T09:52:32 Welcome, you are now connected to log-streaming service. The default timeout is 2 hours. Change the timeout with the App Setting SCM_LOGSTREAM_TIMEOUT (in seconds).
2022-07-28T09:52:55.835 [Information] Executing ‘Functions.TimerTrigger1′ (Reason=’This function was programmatically called via the host APIs.’, Id=af66791c-ea50-40a6-9a7c-886b9599f7d9)
2022-07-28T09:52:55.877 [Error] Executed ‘Functions.TimerTrigger1’ (Failed, Id=af66791c-ea50-40a6-9a7c-886b9599f7d9, Duration=28ms)Result: FailureException: No parameter defined in the script or function for the input binding ‘Timer’.Stack: at Microsoft.Azure.Functions.PowerShellWorker.AzFunctionInfo..ctor(RpcFunctionMetadata metadata) in D:\a\_work\1\s\src\FunctionInfo.cs:line 134at Microsoft.Azure.Functions.PowerShellWorker.FunctionLoader.LoadFunction(FunctionLoadRequest request) in D:\a\_work\1\s\src\FunctionLoader.cs:line 52at Microsoft.Azure.Functions.PowerShellWorker.RequestProcessor.ProcessFunctionLoadRequest(StreamingMessage request) in D:\a\_work\1\s\src\RequestProcessor.cs:line 224
Hello Imran, thanks for the update!
Could you please confirm what PowerShell Core version did you specify when creating the Azure Function?
Today, there are two versions available 7.0 and 7.2.
You could try to use the ‘Az’ = ‘7.*’ on line number 7 in the requirement.ps1 instead of ‘8.*’.
Let me know if it works for you.
Thanks,
Hi Charbel,
PowerShell Core used was 7.2. Also, I have changed the line 7 value to ‘Az’ = ‘7.*’
But still getting the same error.
Hello Imran, thanks for confirming!
After looking at the last error you shared, it looks like the issue is around the ‘Functions.TimerTrigger1’.
The issue is not around PowerShell anymore, it’s a different error.
So you can keep using ‘Az’ = ‘7.*’ or ‘Az’ = ‘8.*’.
Could you please verify the TimeTrigger CRON expression that you configured as described in this section?
You could also try to Stop and Restart the Function App.
Hope it helps!
Thanks, Charbel. After restarting the Function App, it is working as expected.
Thank you, Imran for the update, great news!
Thank you, this is very helpful for me!
Thank you, Ståle for the feedback, much appreciated!
Hi Charbel,
How many machines have you managed to turn off and on at the same time? I was using this function to turn 3 VMs on and off, but when I increased it to a total of 20 VMs, the function started to give a timeout error. I already changed the timeout time to 1h or unlimited and I even upgraded to the EP1 SKU, I keep getting a timeout.
Hello Carlos, thanks for the comment and feedback!
I am managing more than 50 machines without any problem.
This is the first time I hear about a timeout error when you increase the number of machines.
The function will loop through all the machines they have Tagged and then check one by one.
How many subscriptions do you have? What exact error are you getting?
Please try to recycle the Function App and try again.
Thanks,
I also have not seen that issue and I have over 100 VMs across 8 different subscriptions. However, I did make a change to the Start and Stop commands. I didn’t do it because I had issues, I just thought it would make it more efficient. I changed the commands to not WAIT for the VM to Start or Stop. It just issues the command and then moves to the next VM.
Thank you Tim for sharing your experience and confirming that you didn’t experience that issue.
Yes, adding the -NoWait parameter will make it more efficient instead of waiting.
Only 2 subscriptions, 52 VMs in total, but only 16 VMs tagged.
Error Message: “2022-08-03 07:00:00.029 Timeout value of 01:00:00 exceeded by function ‘Functions.TimerTrigger1’ (Id: ‘be2bf804-acd9-463c-99cd-a09e63aea637’). Initiating cancellation.”
Do you guys still use the Consumption plan then?
@Tim, I believe that’s the reason for my “timeout” issue. I said that because of this monitor’s logs:
2022-08-04 04:59:59.980 Executing ‘Functions.TimerTrigger1′ (Reason=’Timer fired at 2022-08-04T04:59:59.9804438+00:00’, Id=767c92c5-de6e-4209-8d5d-acf458d43515)
2022-08-04 05:02:01.633 OUTPUT:
2022-08-04 05:02:02.518 OUTPUT: Error : (I CAN’T FIND THE REASON FOR THIS “ERROR”
2022-08-04 05:02:02.518 OUTPUT: StartTime : 8/4/2022 5:00:02 AM
2022-08-04 05:02:02.518 OUTPUT: EndTime : 8/4/2022 5:02:01 AM
2022-08-04 05:02:02.518 OUTPUT: OperationId : 6b3c2a2f-cb87-4fcd-adfe-905d9b488043
2022-08-04 05:02:02.518 OUTPUT: Status : Succeeded
2022-08-04 05:02:02.518 OUTPUT:
2022-08-04 05:02:02.787 WARNING: Start VM – VM01
2022-08-04 05:04:02.294 OUTPUT: Status : Succeeded
2022-08-04 05:04:02.294 OUTPUT: OperationId : fd29fc8b-1353-4601-9f1a-3aff04fef101
2022-08-04 05:04:02.295 OUTPUT:
2022-08-04 05:04:02.295 OUTPUT: Error : (SAME HERE)
2022-08-04 05:04:02.295 OUTPUT: StartTime : 8/4/2022 5:02:02 AM
2022-08-04 05:04:02.295 OUTPUT: EndTime : 8/4/2022 5:04:02 AM
2022-08-04 05:04:02.458 WARNING: Start VM – VM02 (BIG TIME GAP HERE)
2022-08-04 06:34:30.373 OUTPUT: Status : Succeeded (TO HERE)
2022-08-04 06:34:30.373 OUTPUT: OperationId : 50da2b48-34fe-4642-a411-42b5e976aa69
2022-08-04 06:34:30.373 OUTPUT: StartTime : 8/4/2022 5:04:02 AM
2022-08-04 06:34:30.373 OUTPUT: Error :
2022-08-04 06:34:30.373 OUTPUT: EndTime : 8/4/2022 6:34:30 AM
2022-08-04 06:34:30.373 OUTPUT:
2022-08-04 06:34:30.444 WARNING: Start VM – VM03
2022-08-04 06:34:35.150 OUTPUT:
Is your final code something like this?
“Start-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Force -NoWait”