Start and Stop Windows Azure VMs According to Time Schedule

Today, the Windows Azure management portal does not provide an out-of-the-box capability to define a time schedule for startup and shutdown of virtual machines. Having an automated process for this is of great use, as by simply deprovisioning VMs during off-hours can save you a lot of money. This post will describe a lightweight approach for automated provisioning of VMs according to time schedule.

You might say: “Why don’t you use the Windows Azure Scheduler?”. Well, the Scheduler is great, but it lets you either invoke web services or post messages to a queue. Controlling VMs this way is pretty cumbersome. And with the scheduler I can’t use PowerShell, which is my preferred environment when automating stuff in Azure.

So given I want to use PowerShell I have to think about a way to execute PS cmdlets according to schedule. Well, the Windows Task Scheduler immediately comes to mind. It’s easy to use, built into the Windows OS, reliable, free and can execute pretty much everything you ask it for. But where to host it? I could do it on my laptop, but that’s not running 24/7, so it makes more sense to use an always-on server. As I personally do not have a datacenter, I decided to host an extra Small (A0) virtual machine running in Windows Azure.

So let’s see what are the steps required to build a Windows Server VM in Azure to host the scheduling logic:

Create the Scheduler VM

First, create a Windows Server 2012 R2 VM in the Azure management portal. Deploy it to the datacenter region that fits your needs and specify ‘Extra Small’ as size. Leave the default endpoints, as we will need to log into the VM via RDP.

Essentially it doesn’t really matter if you create a new cloud service or use an existing one for the VM, as we will access the Azure subscription via the PowerShell SDK.

Prepare the Environment

As soon as the scheduler VM has been provisioned, logon via RDP. The first thing we need to do is to install the PowerShell module for Windows Azure into the VM. You can find it here in the Command-line tools section. The installation will be done via the Web Platform Installer.

Install

In order to access our Windows Azure subscription we need to make it ‘known’ to the Azure PowerShell cmdlets. The easiest way to do that is to call a specific cmdlet. So, open up the Windows PowerShell ISE and execute the following statement:

Get-AzurePublishSettingsFile

This will open up a browser window with a Windows Azure sign-in page. Logging in with your Azure service administrator credentials will take you to the following page that lets you download the publishsettings file:

publishsettings

Next, we have to import the publishsettings into our logged-on user’s profile:

Import-AzurePublishSettingsFile -PublishSettingsFile "<downloadpath>\<filename>.publishsettings"

This will essentially import some management certificates into the local user’s cert store (for all subscriptions assigned to your Azure account). These certificates are used to authenticate PowerShell calls to your subscriptions.

You should now delete the publishsettings file. It is no longer required and presents a security risk as it can be used to gain access to your subscriptions.

If you have multiple subscriptions assigned to your Azure account you will have to define a default subscription for which you want the automated provisioning to work:

Select-AzureSubscription -Default "<subscription name>"

Define the Scheduled Tasks

Start VM

Now, as we have the basis to execute authenticated calls to our Windows Azure subscription using PowerShell, we can embed the logic into the Windows Task Scheduler. Let’s say we want to manage a single VM called myvm sitting in a cloud service myservice. In this example we will start the instance up in the morning at 7am and shut it down (i.e. deprovision it) in the evening at 7pm.

First we will define a task to start myvm in the morning. Open up the Task Scheduler management console. From the Action menu, select ‘Create Task…’. Specify a name for the task (e.g. ‘Start myvm’), and select the radio button for ‘Run whether user is logged on or not’. Otherwise, leave all defaults. Note that the task will run in the context of the currently logged-on user.

Task1

Switch to the Triggers tab. Create a new trigger and set it to Daily at 7am:

Task2

Note: machines in Azure are set to UTC time, so you might have to adapt the start time to your local region!

Open the Actions tab. Create a new ‘Start a program’ action, specify powershell.exe as program and the following argument:

Start-AzureVM -Name myvm -ServiceName myservice

Task3

If you want to start multiple machines in a cloud service, you can also do that. Let’s say you might want to start all VMs in the cloud service myservice in a single go, you can specify the following argument:

Get-AzureService -ServiceName myservice | Foreach-Object { Start-AzureVM -ServiceName $_.ServiceName -Name "*" }

On the Settings tab you should activate the checkbox for ‘Run task as soon as possible after a scheduled start is missed’.

Task4

This will prevent missing execution of the task in case the VM would be unavailable at the specified execution time (e.g. because the Hyper-V got patched and our VM would be rebooted just as it had to execute the task). This is sort of a ‘poor man’s HA’ but actually works quite reliably. If you want to test this behavior note that the scheduler would not immediately start the task after coming up, actually but wait a couple of minutes.

That’s it! Note that when saving the task definition you will have to enter your username & password.

Stop VM

Now let’s do the same for shutting down the VM in the evening. Just create a second task called ‘Stop myvm’  and set the trigger to Daily at 7pm (or the corresponding time for your region). The argument for the PowerShell statement is this:

Stop-AzureVM -Name myvm -ServiceName myservice -Force

This statement will not only shutdown the VM, but also deprovision it, i.e. stop accruing cost on your bill. If you wish so, you could add the –StayProvisioned parameter, which will shutdown your VM but keep it deployed. No benefit cost-wise, but this way you could keep the cloud service’s public virtual IP address, in case this VM is the last one running in your cloud service.

Note, that if you deprovision the VM the –Force parameter will be required, in case the VM is the last one in the cloud service myservice. This will allow releasing the public virtual IP address of the cloud service without waiting for confirmation.

Again, you could also stop multiple machines in a cloud service like this:

Get-AzureService -ServiceName myservice | Foreach-Object { Stop-AzureVM -ServiceName $_.ServiceName -Name "*" –Force }

In your Task Scheduler console you should now see the following two task definitions:

Task5

Testing

Now you are ready to test the tasks. Before we start you should enable the task history in order to see what’s going on (it’s off by default). You can do that in the Task Scheduler Library on the right hand side in the management console:

Task6

Make sure the VM you want to manage (myvm in the example above) is running. Right-click the ‘Stop myvm’ task and select Run. If you select the task you can navigate to the History tab in the task details and follow the execution which takes a couple of seconds. As soon as task execution is completed (you can refresh the console using F5) you will see a couple of entries in the history event log. Select the Action completed event and have a look at the Details, especially the return code.

Task7

If the return code is 0, life is good and the VM should be in the Stopped (Deallocated) state in the portal. If an error occurred (i.e. result is not 0), you should take the PowerShell statement and execute it in the PowerShell ISE directly in order to see what the error statement is.

You should also test the second task ‘Start myvm’ interactively, in order to make sure both are configured correctly.

Making it Work

Now, things work fine as long as you are logged on to the machine with the user account that is also the account being used for task execution. If you log off the VM and let a task execute via schedule, you will see that it’s going to fail. Why is that? Well, unattended task execution in the scheduler is a tricky thing, and here’s how you can fix that:

Certificates

First thing to note is that the certificate to authenticate calls to Windows Azure has to be located in the local machine certificate store in case the user is not logged on. I assume the user profile is not loaded properly, and hence access to the local user cert store is not working.

Unfortunately we can’t re-use the certificates imported into the local user store before (by using the publishsettings import), as these don’t let you export the private key. So, we have to create a new certificate and add it to the local machine store. The easiest way to do this is to use the makecert utility from the Windows SDK. If you don’t have a machine handy with the SDK installed, you can get it from here. For makecert you just have to install the SDK itself, without any of the additional components.

Now, create a certificate like this in a command prompt (you can call the certificate whatever you like):

makecert -sky exchange -r -n "CN=azuremgmt" -pe -a sha1 -len 2048 -sr localmachine -ss My "azuremgmt.cer"

This adds a new certificate to the local machine store and creates a .cer file containing the public key of the certificate. Next, we have to upload the public key into our Windows Azure subscription. In order to do this log in to the Azure Management Portal and upload the .cer file in the Settings – Management Certificates page (for details go here).

Windows Azure Profile

What we need to do now is reference the new certificate in the Windows Azure PowerShell profile. This is contained in the WindowsAzureProfile.xml file that is stored in the user’s profile path under C:\Users\<user>\AppData\Roaming\Windows Azure Powershell. Open that file, identify your default subscription and change the <ManagementCertificate> tag to the thumbprint of the cert you created above.

<AzureSubscriptionData>

..

<IsDefault>true</IsDefault>

<ManagementCertificate>171183FB..</ManagementCertificate>

<Name>your subscription name</Name>

..

You can get the thumbprint from the Azure Management Portal or from the .cer file.

What you also need to do is to copy the Windows Azure PowerShell profile to the default Windows profile of the VM. In order to do that copy the whole ‘Windows Azure Powershell’ folder to the path C:\Users\Default\AppData\Roaming.

Task8

Now, we’re good to go. Your scheduled tasks will be executed, no matter if you are signed into the VM or not.

Final Thoughts

You might argue that using the approach described above will cost you $15 a month, just for running the extra small VM hosting the scheduling logic. Well, you have different options here: you can either run the tasks on a server you own on-premises or use an existing VM in Windows Azure that is running anyway. Also, if you consider the cost savings you will achieve with deprovisioning your VMs it should pay off easily. If you shutdown only one single core VM in Azure during the night, it would already save you way more than you spend for the scheduler instance.

8 comments on “Start and Stop Windows Azure VMs According to Time Schedule
  1. Hello, I have been working on a way to get Task Scheduler to stop/start VM’s at Azure for the past few days and ended up creating the certificate as you specified, Setting the Task Scheduler like you wrote.

    Can now run get-azureaccount and it shows my account, but Task Scheduler is still not working correctly. Gives 0x1 error (says completed, but did not run).

    It only works by running add-azureaccount first in Powershell.

    Do you have any suggestions? Would like to get this working automatically without having to do anything first.

    Thanks,
    Denise

  2. Hi Carsten, there’s a free option for hosting your shutdown and power on scripts that you can use in windows azure. All the free websites now comes with a task scheduling mechanism that can run powershell. Look for the webjobs tab in a azure website.

  3. dont waste time trying to get a webjob to do it

    The azure.psd1 module doesn’t exist and can’t be referenced, and it wouldn’t have visibility over your subscription/certificate anyway

  4. Thank you for this excellent write up! Very detailed.
    I have a question about the “WindowsAzureProfile.xml” file. I can only find a “Azureprofile.JSON” file and it makes no mention of management certificates. Aaaargh!
    Can the certificate be loaded with the MMC console? If so, what would be the proper steps? Thanks!
    Tony

  5. Good and useful article. I have already a certificate that I uploaded to “personal” folder of Certificates local machine and users. Still, when I run in ISE ” Start-AzureVM -Name myvm -ServiceName myservice” I get “Your Azure credentials have not been set up or have expired, please run Add-AzureAccount to setup your Azure credentials
    Running get-azureaccount comes back with the right information, of course, running the Add-AzureAccount fixes this and the Start or stop command goes through. Can you please let me know if anything else missing, thanks

  6. Pingback: How to automate shutdowns of Azure VMs | CloudMonix Blog

Leave a Reply to Salam ELIAS Cancel reply

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