Developer Notes

Save money in the cloud by shutting down resources after hours

Azure cost saving: Shutdown Containers and VMs

While developing a new solution in Azure it's common to spin up resources and try stuff out. Some resources are pricier than others. Virtual Machines and their Azure Container Instances (ACI) can be rather pricy but... you can shut them down

Shutdown resources through Azure API

Azure has very nice APIs and command line tooling that allow you to quickly provision and control resources. The portal will also do nicely for incidental control. In a development environment though, resource are created and destroyed regularly and it becomes hard to keep track of all and their cost.

So shutting down resources after work hours is one of those things you'll want to automate.

Azure Automation is the solution promoted by Microsoft for this type of automation, but I found it hard to setup and not that suitable for developers.

An Azure Function can do the exact same thing in a couple of lines of code.

Azure Automation with Functions

An Azure function, given the right permissions, can access the Azure API, enumerate the resources and shutdown the ones you don't need after hours.

// List virtual machines
var vms = await azure.VirtualMachines.ListAsync();

// Stop all machines that are running
await ResourceHelper.ProcessResources(vms,
         vm => vm.PowerState == PowerState.Running,
         vm => Stop(vm, log),
         log);

The ProcessResource function is an extension method to process all resources in a list. To prevent shutting down resources that you shouldn't a filter is applied using tags:

public static async Task ProcessResources<TResource> ( 
    IEnumerable<TResource> resources,
    Func<Tresource,bool> needsStateChange,
    Func<TResource, Task> action,
    ILogger log )
    where TResource:IResource
    {
       var azure = Configuration.GetAzureApi();
       var taggedResources =  resources.
             // Apply a tag filter
            .Where(vm => vm.HasTags(Configuration.Tags()))
            .ToArray();

        log.LogInformation($"Found {taggedResources.Count()} resources");

        // Fan out and change the state of all the resources in parallel
        var tasks = taggedResources
           .Where(resource => needsStateChange(resource) &amp;&amp; !resource.AlwaysOn())
           .Select(action);

        // Wait for all the state changes to complete
        await Task.WhenAll(tasks);

        log.LogInformation("Done");
     }

Shutting down Containers

Azure Container Instances can also be stopped and restarted as desired. The main thing to remember is that containers are stateless so if you need any kind of state you'll have to put it in a database or storage account. Container instances are a bit less mature than VMs in Azure so managing them is a bit less straight forward.

Stopping a container instance is easy. Starting it again is not so easy. It seems that the paradigm that ACI was designed for is provision / run / exit / done.

A bit of digging around yields the following code for starting a container after it's been stopped:

public static Task Start(IContainerGroup containerGroup)
{
  return ContainerGroupsOperationsExtensions
     .StartAsync(
           containerGroup.Manager.Inner.ContainerGroups,
           containerGroup.ResourceGroupName,
           containerGroup.Name);
}

Full source code is up on GitHub.