Linux Custom Script - Docker Sandbox

boy-child-childhood-6459I do a lot of proof of concepts (POCs) as part of my job.

I hate keeping demo environment around.  They tend to become brittle, out-of-date and filled with the last stuff I did.

I prefer to start from a clean slate every single time when possible.

That means automation.  With automation we can recreate whatever we need on demand.  This way we have a clean environment every single time.  No longer “it worked on my environment I’ve been fiddling with for the last 6 months”.

A very useful tool for automating the configuration of VM is the Linux Custom Script.  This VM extension allows us to inject a script on a VM.  We can use that script to configure our VM.  It integrates with ARM template:  we will then deploy a fully configured VM in one go.

In our last article, we looked at Docker Containers.  Before we get our hands dirty with Docker itself, we would need a place to play, a sandbox.  Somehow the Docker images (i.e. published by Docker) from the Azure Marketplace do not work this week.  I thought it would be a good opportunity to demo the Linux Custom Script.

I could have installed Docker on my laptop and be done with it.  But I like to have a clean environment every time and I like to keep my laptop as close to “factory setting” as possible.  For this reason I always use VMs for Docker.  I always need to refer to the install procedure.  That is tedious and has nothing to do with Docker.  I prefer automation!

To throw a little twist, the ARM template we’re going to build supports two OS.  We support Ubuntu & CentOS (the open source little brother of Red Hat Linux).  We are going to use Logical Functions in ARM to support both.

The result is available on GitHub with deployment buttons, ready to use.

Installing Docker

Installing Docker is part of all good Docker tutorial.  But as with most software, the install has little to do with mastering the software.  Or at least, in this day and age, it shouldn’t.

We can find the instructions on how to install Docker Community Edition (CE) here.  There is a listing of all supported OS with links to specific instructions for each.

Ubuntu’s instructions are here while CentOS instructions are here.

To script those instructions, we proceeded as follow:

It took one or two iterations to get it right.

The only alterations we did:

Linux, with its package managers, makes it quite simple to automate installations.  It was quite straightforward and resulted in the following scripts:

Installing Azure CLI

Similarly, we installed Azure CLI.

Instructions for installation on all OS are here.  Ubuntu’s are here.  CentOS’ are here.

Again, it was quite straightforward and resulted in the following scripts:

ARM Template

We started with a vanilla VM deployment template.

We then added the Custom Script Extension with Linux.  Instructions are quite good and it’s simple enough:  pass a few URLs, only one in our case, then a command to execute.  It is important to note that we can only have one such extension.  This means we must package every script we want to run into one.  In our case we bootstrap the Docker & Azure CLI install in one third script.

We always like to get the latest version of the ARM schemas, so we looked at it here.

In Visual Studio we can even add extension on VMs with right-clicks.

The extension resource is quite simple:

 "type": "extensions",
 "name": "[variables('Custom Script Name')]",
 "tags": {},
 "apiVersion": "2017-12-01",
 "location": "[resourceGroup().location]",
 "dependsOn": [
 "[resourceId('Microsoft.Compute/virtualMachines', variables('VM Name'))]"
 "properties": {
 "publisher": "Microsoft.Azure.Extensions",
 "type": "CustomScript",
 "typeHandlerVersion": "2.0",
 "autoUpgradeMinorVersion": true,
 "settings": {
 "fileUris": "[variables('Script URLs')]",
 "commandToExecute": "[variables('Command')]"

The template file is available here on GitHub.

Conditional deployment

Conditional deployment have existed for something like a year now.  We didn’t even use that to implement multiple OS.  We used the if function in the template.

We did all the work at the variable level so the resources would stay clean.

We use the parameter Operating System, which is of type string and can take the values CentOS & Ubuntu.

We then defined a boolean variable isCentOS:

"isCentOS": "[if(equals(parameters('Operating System'), 'CentOS'), bool('true'), bool('false'))]"

From there we use that variable to define other variable.  Here is an example of the pattern:

"VM CentOS Name": "DockerCentOS-VM",
"VM Ubuntu Name": "DockerUbuntu-VM",
"VM Name": "[if(variables('isCentOS'), variables('VM CentOS Name'), variables('VM Ubuntu Name'))]",

This way we can refer to the variable VM Name in the template and have no if in the template.

We managed to reuse the entire template this way without duplicating code.  Another approach is to use condition on resources.  We could then define 2 VM resources conditional on the OS.  That is the recommended approach.  We found that by using if conditions we were able to duplicate no code and still have a clean template.


We now have a Docker Sandbox we can spin in 5 minutes!

It also is a great example on how to implement simple Linux automation on Azure.  For more complicated setup we would recommend a configuration manager tool.  On Linux there is Ansible, Chef, Puppet (and many others).  On Windows we would recommend Desired State Configuration (DSC).

Leave a comment