Creating what I call "topologies" or "environments" in Azure can be a multi-stage process:
- Design and deploy your infrastructure.
- Configure the individual infrastructure components to correctly communicate with each other.
- Deploy your application to the infrastucture.
The goal of a deployment process should, ideally, be to spin up an environment from nothing to fully deployed and configured in a single step - something that is often referred to as a no-touch deployment.
Farmer is a library that helps make such deployments to Azure simpler by providing a simple domain-specific language built on top of mature and reliable technologies that supports all three stages.
ARM templates - an enabler for automation
Before we look at Farmer, let's look at one of the technologies that Farmer is built on top of: ARM templates. ARM offers much of the functionality for the three steps above:
- You can often create your architecture in a single file.
- You can create "links" between two resources, and even supply values that are generated at deployment-time from one to the other. For example, you can tell ARM to get the connection string of a as-yet-undeployed database to an application setting of a web application.
- You can link source code from a GitHub repository to the App Service, which will build and deploy the outputs to the web app using the Kudu service in Azure App Service.
You can also deploy a pre-built application into the App Service. However, this requires the application to have been deployed into an existing storage account, and so does not pass the test of "no-touch" i.e. from zero to fully deployed and configured. Also note that the Kudu feature referenced above is considered to be obsolete and no longer a recommended way of creating a production-level continuous deployment process.
A real sample of ARM templates
A really good example of using ARM to achieve a no-touch deployment can be seen here by Mark Brown, who works on the Azure Cosmos DB team:
More ARM template fun today. Created a template that deploys:
-Cosmos DB
-App Service
-A web app out of GitHub
-Injects uri and keys from Cosmos in App Settings.Basically a "no touch" deploy with no copy/pasting of sensitive keyshttps://t.co/zEX39IdwoY
— Mark Brown (@markjbrown) June 19, 2020
The ARM template that is linked in that tweet contains the definition for:
- A Cosmos DB account (but no database or container)
- An Azure Server Farm
- An Azure Web Application
- The instructions to deploy a specific GitHub repository of a C# ASP .NET application to the web application.
It also hooks up the dependency chain between them so that they are deployed in the correct order, and sets several application settings on the Web Application that refer to properties of CosmosDB. For example:
{
"properties": {
"siteConfig": {
"appSettings": [
"name": "CosmosDb:Account",
"value": "[reference(resourceId('Microsoft.DocumentDb/databaseAccounts/', variables('cosmosAccountName'))).documentEndpoint]"
]
}
}
Here, the CosmosDB:Account
setting that refers to the documentEndpoint
on the Cosmos DB is applied to the App Service. There are no secrets passed into the ARM template at all - the application in effect is capable of configuring itself at deployment time. You could even go further and use something like Azure KeyVault to ensure the secrets are kept completely isolated, but the approach here is markedly better than either manually retrieving the secrets and configuring the application post-deploy, or worse yet, committing secrets into source control. If KeyVault had been included in the template it would have further complicated matters, so I'm glad in this case as an educational resource that it was ommitted.
The end result is that you can go from an ARM template, an Azure subscription and the GitHub repository to a fully deployed application in a single deployment step, whilst keeping application secrets to an bare minimum (only visible within the Azure Portal in the App Service).
Using Farmer to simplify deployments
The difficult of the approach above is that the ARM template itself is almost 170 lines long. Farmer has many benefits over "raw" ARM templates, and overall is a much better fit for authoring templates: generally they are between 4-10 times smaller than the equivalent ARM template, and simpler to reason above. Even better, Farmer simply emits ARM templates, so if you already use ARM, you can retain all the existing deployment and visualisation tooling that you might already be using.
I enjoy trying to port raw ARM templates into Farmer - not only does it function as a good example of the before / after benefit, but just as important, it also validates if Farmer has the appropriate features and extensibility points as required. In this case, Farmer didn't yet support source code deployments into the App Service (for the reasons outlined earlier), but it was a relatively simple addition to make. What is good is that everything else required was already supported including the built-in, strongly typed expressions - so that instead of the JSON snippet above to link CosmosDB to the App Setting, in Farmer it's as simple as this:
webApp {
setting "CosmosDb:Account" theDatabase.Endpoint
}
In fact, as the entire Farmer template is only 30 or so lines, here it is; I've removed the parameterisation to aid readability but the source code provided shows how to easily parameterise it (you can look at the full repository here).
let theDatabase = cosmosDb {
name "Tasks"
account_name "isaac-to-do-app-cosmos"
consistency_policy Session
}
let theWebApp = webApp {
name "isaac-to-do-app"
sku WebApp.Sku.B1
settings [
"CosmosDb:Account", theDatabase.Endpoint
"CosmosDb:Key", theDatabase.PrimaryKey
"CosmosDb:DatabaseName", theDatabase.DbName.Value
"CosmosDb:ContainerName", "Items"
]
source_control "https://github.com/Azure-Samples/cosmos-dotnet-core-todo-app.git" "master"
disable_source_control_ci
depends_on theDatabase
}
let deployment = arm {
location Location.WestEurope
add_resources [
theDatabase
theWebApp
]
}
As you can see, Farmer templates are lightweight, simple to read & reason about, and easy to maintain. The Farmer code above actually goes further than the original ARM template as it also creates the CosmosDB database in addition to the account, as well as an App Insights instance that is fully configured and ready to go.
Testing it out
Farmer templates can be deployed manually by generating the ARM template and then using any number methods such as PowerShell, Azure Portal or HTTP. Alternatively, Farmer contains a simple API to quickly deploy Farmer code directly.
Once deployed, the deployed infrastructure looks as follows.
And viewing the connected ASP .NET application confirms that everything is working as expected:
As an added bonus, since Farmer automatically provisions and connects Application Insights to the web app, this means we can use features such as Live Metrics stream, Log traces including calls to external dependencies (such as Cosmos DB) and an automatically generated application map.
Summary
"No touch" deployments should definitely be a goal for your cloud deployments. On Azure, one way to achieve this is through ARM templates; unfortunately this are difficult to author. One of the goals of Farmer is to make it easier for developers and devops teams to make doing the "right thing" easy, rather than discourage us because it's either too difficult to figure out, or too costly to author.
The full repository for the code samples above are available here.
If you're interested in trying out Farmer, it's free and open-source, whilst you can always contact us if you're in the need for some guidance and support in adopting best practices for repeatable cloud deployments.