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:
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.
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:
Next, we have to import the publishsettings into our logged-on user’s profile:
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:
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.
Switch to the Triggers tab. Create a new trigger and set it to Daily at 7am:
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:
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.
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:
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.
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.
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):
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.
Now, we’re good to go. Your scheduled tasks will be executed, no matter if you are signed into the VM or not.
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.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: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.
Switch to the Triggers tab. Create a new trigger and set it to Daily at 7am:
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
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’. 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: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: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.
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.
Now, we’re good to go. Your scheduled tasks will be executed, no matter if you are signed into the VM or not.
No comments:
Post a Comment
Note: only a member of this blog may post a comment.