First of all, this is a 50,000-foot view of both solutions with some key considerations to tempt fate and interest for further investigation. Now secondly, in the context of deploying IaaS Virtual Machine instances in Azure (note that my example in this blog is the deployment of said IaaS VMs with Azure DevOps #ShipIt) I wanted to go over using ARM Template Customer Script Extension vs PowerShell Desired State Configuration (via Azure Automation ASC).
This is a precursor blog post to another on ARM Custom Script Extensions. I thought it worthwhile to get some thoughts on this out to finalise some design decisions I've recently made for a customer.
The two solutions can basically do the same thing, but fundamentally they are quite opposed in how they go about doing that. In addition, Desired State Configuration has a bigger arsenal up it sleaves when it comes to functionality to maintain a virtual machines specific state. So, I'm not exactly talking immutable machines here which is by far the preferred option when it comes to cloud IaaS infrastructure. Let me get that viewpoint out there to align myself with the cool kids. This is more in the context of getting a VM ready for consumption by downstream application teams.
So in this example, I need to deploy some IaaS infrastructure with Azure DevOps Release Pipelines; the classic deployment model there. No YAML, all GUI, but all good; no really! For me, that pattern works well. But when it comes to deployment of Azure resources by way of Azure Resource Manager Templates- there's an important decision to make around post deployment instance configuration.
Custom Script Extension / Custom Script
Immediately after a VM is created with an ARM Template, you can call a module either in the main VM deployment module of “Microsoft.Compute/virtualMachines”, or after the VM is created, but immediately following in a “Microsoft.Resources/deployments” module.
A Custom Script Extension (that’s a “type”: “CustomScriptExtension” on Windows and a “type”: “CustomScript” on Linux) downloads one or several scripts from your favourite source control (#GitHub) or public cloud repo (#AzureBlobStorage) and executes said script(s) on the VM.
You can get down and dirty with whatever examples you see fit here, but for the most part, this immediate pattern is good for post-deployment configuration (like domain join, setting specific permissions/access to the VM, or software deployment of some flavour).
Some important information around CSE's:
- The VM Agent runs these immediately following its creation- that’s important to remember
- Note: the VM Agent gets automatically installed when creating any new VM
- That means idempotency in scripts is the biggest consideration when wanting to use CSE's
- If a script isn't written with idempotency in mind, you'll run into errors and headaches for more than it's worth your mental health
- Because it runs immediately following a VM creation, there’s a maximum of 90min that the script(s) can run before AzDO will Terminator II that process
- Custom Script Extension is only for Windows instances
- Custom Script is for only for Linux instances
- I believe Linux also has a nasty limitation around the number of Custom Scripts per Azure RM template- ahh… that’s 1 (but I’ll get to some goodness around bypassing said limitation in another blog)
- CSE's can be run from the Azure Portal as well in a more ‘post-deployment phase’ manually, but in my use case this wasn't an option because manually
There are other good points in the Microsoft documentation on this available here.
PowerShell Desired State Configuration
On the spectrum of simple and fast, with CSE being on that end, Desired State Configuration is towards the opposite end. By that I mean a correct implementation of DSC takes more hours to design and implement in the correct way.
Where CSE's provide some quick functionality in an ARM template to download and execute a script on an IaaS VM after creation, DSC can be used in Continuous Integration/Continuous Deployment pipelines (#BuzzWords) to maintain state.
With this greater functionality comes the requirement to deploy additional infrastructure to run and maintain that DSC. With Azure, Automation State Configuration (ASC) can do that heavy lifting from a single service point of view.
Some important considerations and information on DSC is as follows:
- A VM state is maintained to what is declared, and when it is declared
- Meaning that trying to get everything within a 90min AzDO Release Pipeline execution is irrelevant
- Declarative state allows for changes to be made and amended to said state, in a central location even, and pushed out to multiple instances
- CSE's are specific to the VM's deployed as part of the ARM template (considering Copy loops can apply to multiple VMs there)
- Azure ASC offers some pretty sexy reporting when it comes to VM compliance which is one of the best features of the service
- It's quick to determine out of compliance VMs and work to remediate that
So, what did I do?
Quick, easy, dirty, icky…. err yep; Custom Script Extensions. Why!!!???
For the specific use case, the customer had, only a specific post-deployment pattern needed to be run on VM instances. The overall pipeline was very much mutable, and apps deployed by app teams needed an environment to transition from legacy on-premises data centres.
The AzDo deployment requirements and the initial state of the VM instance were that of getting them ready for consumption. Additional tooling after the fact would take care of the state. That tooling may very well have been Desired State Configuration or an equivalent solution. For me… out of scope, so I'm not 100% sure.
In a follow-up blog to this I'll go into a pattern on how to execute multiple scripts within an ARM Template for both Windows and Linux. I know in my use case Azure DevOps can be leveraged to do that with multiple ARM templates run across different Tasks in a specific Stage.
That all seemed like the easy road. I took the hard road which eventually worked out quite well.