Apprio.Azure.Infrastructure

Package Description


Keywords
License
MIT
Install
Install-Package Apprio.Azure.Infrastructure -Version 1.27.0

Documentation

NuGet - nuget.org NuGet - GitHub Packages Bump Version

Apprio Azure Infrastructure

This library extends functionality and creates a runtime for the Azure Management SDK for .NET. Inpsiration was borrowed heavily from Jamie Phillips. Please consider buying Jamie a coffee.

Features

  • Hosting runtime to execute your infrastructure code
  • Dependency injection to create testable infrastructure code
  • Naming provider so you can enforce strict naming conventions for similar Azure resources
  • Tag provider to standardize tags across provisioned resources
  • Region provider so you can run the same deployment to multiple regions (for active-active or active-passive setups)
  • Region helper for parsing Azure regions

Getting Started

Create a new .NET 5.0 console app

  1. Once you've created your app, add your appsettings.json files. These appsettings.json files should contain non-sensitive configuration settings for your deployment. These files are evaluated where you can have environment settings layered over top of the default settings. Tree structure should look similar to the following:
  • appsettings.json
  • appsettings.dev.json
  • appsettings.prod.json
  • appsettings.qa.json
  1. App settings file should follow a structure similar to this (feel free to add your own settings!):
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "Organization": "exampl",
  "Product": "secureTokens",
  "RegionOptions": {
    "App": [
      "USEast", 
      "USWest"
    ],
    "Shared": "USCentral"
  },
  "SecurityOptions": {
    "SecurityGroupObjectId": "00000000-0000-0000-0000-000000000000",
    "Domain": "example.com"
  },
  "TagOptions": {
    "Tags": [
      {
        "Name": "owner",
        "Value": "me@example.com"
      },
      {
        "Name": "maintainer",
        "Value": "you@example.com"
      }
    ]
  }
}
  1. Set up your IaC jobs. In most cases you'll minimally want two jobs: create and destroy. See the following two templates for a basic implementation: Create
using System.Threading.Tasks;
using Microsoft.Azure.Management.AppService.Fluent;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Models;
using Apprio.Azure.Infrastructure.Abstractions;
using Apprio.Azure.Infrastructure.Models;

public class CreateJob : IJob
{
    private readonly IAzure azure;
    private readonly IAzureOptions azureOptions;
    private readonly ILogger<CreateJob> logger;
    private readonly INameProvider nameProvider;
    private readonly IRegionProvider regionProvider;
    private readonly SecurityOptions securityOptions;
    private readonly ITagProvider tagProvider;

    public CreateJob(
        IAzure azure,
        IAzureOptions azureOptions,
        ILogger<CreateJob> logger,
        INameProvider nameProvider,
        IRegionProvider regionProvider,
        IOptions<SecurityOptions> securityOptions,
        ITagProvider tagProvider)
    {
        this.azure = azure;
        this.azureOptions = azureOptions;
        this.logger = logger;
        this.nameProvider = nameProvider;
        this.regionProvider = regionProvider;
        this.securityOptions = securityOptions.Value;
        this.tagProvider = tagProvider;
    }

    public async Task RunAsync(IJobContext context)
    {
        var tags = tagProvider.CreateDefaultTags();
        logger.LogInformation("Provisioning shared resources");

        var region = regionProvider.GetSharedRegion();

        logger.LogInformation("Provisioning shared resource group");
        var resourceGroup = await azure.ResourceGroups
            .Define(nameProvider.ForResourceGroup(region))
            .WithRegion(region)
            .WithTags(tags)
            .CreateAsync();

        logger.LogInformation($"Provisioning shared resources in `{region}` has completed");
    }
}

Destroy

using System.Threading.Tasks;
using Microsoft.Azure.Management.AppService.Fluent;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Models;
using Apprio.Azure.Infrastructure.Abstractions;
using Apprio.Azure.Infrastructure.Models;

public class DestroyJob : IJob
{
    private readonly IAzure azure;
    private readonly IAzureOptions azureOptions;
    private readonly ILogger<CreateJob> logger;
    private readonly INameProvider nameProvider;
    private readonly IRegionProvider regionProvider;
    private readonly SecurityOptions securityOptions;
    private readonly ITagProvider tagProvider;

    public CreateJob(
        IAzure azure,
        IAzureOptions azureOptions,
        ILogger<CreateJob> logger,
        INameProvider nameProvider,
        IRegionProvider regionProvider,
        IOptions<SecurityOptions> securityOptions,
        ITagProvider tagProvider)
    {
        this.azure = azure;
        this.azureOptions = azureOptions;
        this.logger = logger;
        this.nameProvider = nameProvider;
        this.regionProvider = regionProvider;
        this.securityOptions = securityOptions.Value;
        this.tagProvider = tagProvider;
    }

    public async Task RunAsync(IJobContext context)
    {
        logger.LogInformation("Deleting shared resources");

        var region = regionProvider.GetSharedRegion();

        logger.LogInformation($"Deleting app resources in `{region}`");
        await azure.ResourceGroups.DeleteByNameAsync(nameProvider.ForResourceGroup(region));
        logger.LogInformation($"Deleting shared resources in `{region}` has completed");
    }
}
  1. After setting up your jobs, set up your job host:
using Apprio.Azure.Infrastructure;
using Apprio.Azure.Infrastructure.Abstractions;
using Apprio.Azure.Infrastructure.Models;
using Apprio.Azure.Infrastructure.Services;

public static class Program
{
    public static void Main(string[] args)
    {
        // register jobs fluently
        var create = JobDescriptor.WithType<CreateJob>.WithCommand("create");
        var destroy = JobDescriptor.WithType<DestroyJob>.WithCommand("destroy");

        // create orchestrator
        var jobOrchestrator = new JobOrchestrator();
        // add jobs to orchestrator
        jobOrchestrator.Add(create, destroy);
        // inject any custom services
        jobOrchestrator.OnConfiguringServices += (sender, services) =>
        {
            // todo: add any necessary services for DI
            services.AddExampleServices();
        };

        // set up service host and run job
        JobHost.Run(
            jobOrchestrator, 
            create.Command, // default command if none is provided to the program
            c => AzureOptions.FromEnvironmentVariables, // get azure credentials from environment variables
            args);
    }
}