In case you've not used it before, Azure Resource Manager (ARM) is nowadays the standard way to specify and deploy resources into Azure. Resources (such as Virtual Machines, Storage Accounts, App Services etc.) are deployed into resource groups - logical containers for multiple resources.
Azure also has support for scripting these deployments, in the form of ARM Templates - JSON files that contain the declarative make-up of a resource group. We can observe and report on these deployments through the Azure portal etc., with full tracing of what happened on each deployment.
It's a common task to try to incorporate your ARM templates into your deployment process - so when deploying your code, you can also deploy your infrastructure at the same time. In fact, some build services (including Microsoft's own VSTS) have built-in tasks for doing exactly this.
However, .NET developers may be familiar with the FAKE build system, which provides a number of useful helper libraries alongside a simple DSL to chain together build steps. Until recently, it wasn't especially easy to deploy an ARM template through .NET, which meant your build process needed to be a two-step process:
There are some concerns I have around this approach of mixing multiple tools / languages for a build process:
It would be much nicer to perform the deployment of our infrastructure directly in our FAKE script along with the other required tasks! But before we go much further, let's first look at an example ARM template which we'll later on try to deploy through FAKE.
Here's a sample ARM template,
sample-rg.json which simply creates a Storage Account into an existing Azure Resource Group:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
If you've never used ARM templates before, there are several points of interest:
Input parameters: You can supply input arguments to the template. Here, we're supplying the name we want the storage account to be called (
storageAccountName). We can then reference it later on as
Resources: We specify that we want to create a resource of kind
Storage, using the
Standard_LRS SKU in the
West Europe data centre. Note that ARM is smart enough that if the resource already exists, it'll apply any changes (where possible) to the existing resource; if it doesn't exist, it'll create it.
Output parameters: After we create the storage account, we want to return back out to the caller the key of the newly-created storage account. In ARM templates, you can actually specify dependencies and use properties across resources e.g. create a storage account and then set an app setting in a web app with the storage account key etc. But here, we want to pass the storage account key back to the caller - in our case, a FAKE script - so that we can use it downstream in the FAKE build process.
In order to authenticate with Azure, you'll need to create an Azure application account in your Azure AD, and then grant it access to either the entire subscription, or the existing resource group as needed. The Azure application has a Client ID, Client Secret and a Tenant Id. You'll need to get all three of these from the portal.
These credentials will be used later on in the process to allow our .NET code to securely communicate with Azure.
Once you've got your credentials, you can create a new F# script, adding a Paket dependency to the ARM Helper:
As the ARM Helper is a simple file, you can easily work with it without worrying about NuGet packages etc. - it's just a standalone
.fs file that'll compile into your project or FAKE scripts etc.
The ARM Helper is actually just a simple wrapper around the relatively new .NET Resource Manager Fluent library (part of the collection of .NET Fluent library packages). This library is itself simply a wrapper around the native REST API for accessing the Resource Manager that Azure exposes.
The wrapper provides a few simple functions. Here's an example of it in use:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
Initially, we create our credentials and select a subscription, which we then call
authenticate on. Note in this simple example, we've hard-coded the client secrets into the script. In a continuous deployment model, you'd pass these arguments into the script from your build system e.g VSTS, AppVeyor etc.
The next section uses a simple wrapper around the Fluent SDK to create and execute deployment into Azure - in this case, the contents of the
sample-rg.json file we saw, earlier into the resource group
sample-rg. Notice how we supply any required input parameters using a simple list of
string * string pairs. The API will convert these into a format understood by ARM (note: ARM templates also allow optional parameters with defaults embedded in the template). Once the ARM template has been deployed, the API retrieves any outputs from the deployment and returns them as a
Since there's no reliance on FAKE or any other library, you can call these function however you want. However, it's very easy to put into a FAKE script, as shown in this gist.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41:
Notice in this example we've created a full configuration for deployment (rather than the
deploySimple function used earlier) and applied it to
deployWithProgress, which provides a sequence of messages with status updates whilst deployment occurs; the final message in the sequence contains the outputs from ARM. What's also nice is that with VSTS, variables implicitly show as environment variables and therefore appear as standard build parameters for FAKE to consume. And as a final proof that this does indeed work, here's a version of that FAKE script running in VSTS (using the optional FAKE Runner extension!).
The ARM Helper is at a very early stage at this point, and there are several features we'd like to add to it.
Currently there's no good story around taking advantage of VSTS's excellent Azure authentication integration, so we have to explicitly create an Azure Application and supply the Client ID / Secret / Tenant ID. It would be preferable if we could avoid this.
Secondly, this apporach doesn't utilise VSTS support for "secure" variables, whose values, once entered, do not show in logs or any screens. This would be preferable for the client secrets.
As one of the most common uses for this will be running within FAKE, the helper is deliberately synchronous. You can of course wrap the API calls into your own async block, but we will probably add a native async version as well.
We're considered going straight to the REST API in order to remove the dependency on the REST API. This would enable putting a helper such as this directly into FAKE.
We've been experimenting with creating an ARM type provider. This may allow features such as:
There's definitely room for improvement with the ARM Helper. Nonetheless, this post illustrates how its possible to do away with hybrid Azure CLI / Powershell / FAKE scripts and perform everything directly inside FAKE - providing you with greater control and reuse of your existing skills.