Friday, April 17, 2020

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 Azure Container Instances (ACI) can be rather pricey but... you can shut them down after hours to save some money.

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) && !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. Cointainer 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, but starting it again not so. It seems that the paradigm that ACI was designed for is provision / run / exit / done. The restart function is simply not in the published API.

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.

Friday, April 10, 2020

ASP.NET Core - test function creation

In a previous post I talked about testing controller creation in an ASP.NET Core MVC or WebAPI type of application. Azure Functions don't use controllers though. They're a little more loosly defined by marking a method with an attribute. You can still use a similar test to verify that all functions in your project can be instantiated with dependency injection.

The integration test

This is an xUnit test and you can run this inside your CI/CD pipeline. The Functions method returns the types all classes that contain function methods. This works with both 'regular' Azure Functions and Azure Durable Functions and the activities that go with it.

In the Arrange phase of the test, the Function host is constructed and the type containing the function is added explicitly so we can easily verify the type can be instantiated.

By using GetRequiredService we force the container to return a proper error which includes any types missing form the dependency graph.

Further reading

Monday, March 30, 2020

ASP.NET Core - test controller creation

The default dependency injection container in ASP.NET Core is pretty decent but lacks some of the features of the more seasoned containers like Castle Windsor.
One of the things that's sorely lacking, in my opinion, is being able to validate whether the dependency graph is complete. This used to be one of the first tests I add to a .NET Framework app when using a dependency injection container.
It's quite common for devs to break the dependency graph while evolving the application and it can be a costly mistake. The build will not break but the application will be broken.

A partial fix : validate controller creation

Within ASP.NET Core the entry point for any external request is a controler, so if we ensure the dependency graph is complete for all controllers we'll be able to catch a good portion of mistakes.
One of the nice things that ASP.NET Core brings to the table is the in-memory TestServer, designed specifically to enable integration testing. Testing startup configuration is most definitely an integration test.
Using that test server you can bootstrap your application and run tests against the dependency container.
The Gist below has all the code for a working test.

Test project setup

For this code to execute, you should setup an XUnit test project, either from Visual Studio or from the command line:

dotnet new xunit -o MyProject.Tests

Then add the following Nuget packages
  • Microsoft.AspNetCore.Mvc.Testing
  • FluentAssertions
Finally, add a reference to your ASP.NET Core web application project. The Startup class is the startup class for your application. If needed you can provide appsettings.json to give your application the required configuration settings.

The test code

The test uses the WebApplicationFactory provided by the ASP.NET Core for the TestServer (check the Microsoft docs for more info).
The constructor will receive the factory and allow you to configure it as needed. The test method itself uses XUnit's Theory to execute once for each controller. The controllers are discovered using reflection. Depending on your setup, you may need to use a different strategy to find the controllers in your application.
Finally, the test itself does some magic to get a hold of the ServiceProvider and tries to create each controller.

The error message will be nice and verbose so you know which controller could not be created.

Friday, January 31, 2020

Grant yourself access to an Azure Key Vault

Azure Key Vault is a great way to protect your secrets but while you’re working on ARM templates to deploy your environment you’ll need access to the Key Vault from time to time to verify it’s contents or setup new secrets.

Adding a policy in the Azure Portal

This is easy enough when you have access to Azure Active Directory; you can add a policy to allow yourself into the key vault. Sometimes though, you have to work under stricter conditions where AAD access is not allowed. The Azure portal won't even let you pick your own account:
No access to AAD means no new policy in the Azure Portal
The principal selection dialog will show you an error like:
Graph call failed with httpCode=Forbidden, errorCode=Authorization_RequestDenied, errorMessage=Insufficient privileges to complete the operation., reason=Forbidden

Adding a policy through Azure CLI

You can still grant yourself access with Azure CLI:
az keyvault set-policy –n my-vault –-secret-permissions list get set delete –object-id [my-aad-guid]
Here are the full details on that command.
When you don’t have access to the Azure AD, it’s also a bit tricky to find the object id for your (guest) account. Fortunately, you can find that id using Azure CLI as well:
az ad signed-in-user show --query objectId
Make sure you are logged in using az login and select the right Azure subscription using az account set
Here’s the full PowerShell script: