Azure Arc machine

Onboarding Custom VM to Azure Arc with Terraform


I finally managed to play the hybrid cloud the other day! True story. We’ve inherited a farm of virtual machines in a French cloud provider I’ve never heard before. Naturally, our initial task was to begin monitoring what’s happening within this setup. Is CPU high or low, what’s the memory consumption, are there any interesting activities happening, etc. I’ve blogged about a number of monitoring and logging tools before, but this time the simplest approach seemed to be to just bring those machines under the Azure umbrella. Since we already spend most of our time there, using Azure’s native security and monitoring tools to manage these instances seemed like the most practical choice.

It was surprisingly easy. Azure Arc, which consists of Azure’s tools for managing a hybrid cloud, offers a rather straightforward onboarding script that handles most of the work. The remaining task involves creating several ‘receiving’ resources within Azure itself, which is even more straightforward

So in this post, I want to share my excitement with the rest of the world and to show how one can onboard an arbitrary Digital Ocean’s virtual machine to Azure with Azure Arc. Why Digital Ocean? Well, I like those guys. They have been hosting my blogs for the last 8 years for 5–6 bucks per month per machine, which I appreciate a lot. Plus, their Terraform support is very decent, and that’s the other piece of technology we’re going to use. After all, it’s not Middle Ages anymore and everything should use Infrastructure as a Code and Terraform.

So, let the fun begins!

Step 1: Create Digital Ocean Droplet

Digital Ocean (hereinafter – DO) calls its virtual machines droplets, which is… OK. My parents called me Pavel, so who am I to judge. Anyway, in addition to Terraform’s digitalocean provider to create a droplet, we’ll use another one – tls, to generate a set of SSH keys for later login. By including just a few Terraform resources in and, you can have a fully configured virtual machine with SSH access ready in under 42 seconds. Beat that, Azure.

I’ve already added DO’s API access token to the environment variable – DIGITALOCEAN_ACCESS_TOKEN, so don’t be surprised why there are no explicit authentication parameters.

For anyone, who wonders, here’s what terraform apply goes through:

  1. Creates an SSH key (tls_private_key.this),
  2. Registers that SSH key in DO (digitalocean_ssh_key.this),
  3. Creates a droplet (digitalocean_droplet.this), kindly asking to inject previously registered SSH public key (digitalocean_ssh_key.this.fingerprint) into it, and finally
  4. Executes whoami command (terraform_data.arc-connection) on newly created machine to confirm that SSH key (tls_private_key.this.private_key_openssh) is working. Later we’ll replace whoami with something more useful.

I did run the deployment, and indeed, the science is solid – DO reported that there’s a new droplet in my project, and Terraform’s output indicated that whoami responded with root. Hurray!

Step 2: Prepare Azure Arc Provisioning Script

Now comes the tricky part: adding a machine to Azure Arc involves running a custom script on it. Azure is even kind enough to provide a template.

However, the script does require some editing. To start, we should remove sudo from line 24, as the script will run as root anyway. Additionally, adding the declaration of the DEBIAN_FRONTEND=noninteractive variable should prevent the script from attempting unnecessary interactions in a headless environment, thus avoiding potential execution halts due to prompts.

Next, the script requires placeholders to be replaced with actual values. Fortunately, Terraform can help us generate these values. We’ll need the following:

  1. Service Principal: along with its password (lines 2 and 3), assuming the SP has permissions for registering Arc machines (Azure Connected Machine Onboarding role)
  2. Subscription ID, Tenant ID (lines 6 and 8)
  3. Resource group and its region (lines 7 and 9)
  4. Correlation ID – is a random GUID (line 11), which might be helpful for debugging, and which I’ve personally never had to use.

Lastly, there’s a subtle requirement: our Azure subscription must have the Microsoft.HybridCompute provider enabled, which isn’t the default behavior. If it’s not activated, the onboarding script will raise complaints about lacking permissions to enable the provider by itself. This could be confusing, as the documentation doesn’t mention this particular aspect.

So, without any more delay, here’s the file that generates a fully populated onboarding script, ready for execution, along with all the necessary surrounding resources:

The code is quite self-explanatory and meets all the requirements we’ve outlined above. Ideally, the region of the resource group should be close to the virtual machine’s region. That’s why the azure_arc_region variable is set to canadacentral, which, like DO’s tor1, is located in Toronto

Moving forward, we’ll add registrations for three more Terraform providers (azuread, azurerm, random) and introduce one more variable in By running terraform apply, we will confirm that the science is still solid, and we are able to create resources in Azure as well.

Pure, pure witchcraft.

By the way, Terraform picked up Azure credentials implicitly from the environment too – from already authenticated Azure CLI. In a production, you’d probably choose to be more explicit about your configuration.

Step 3: Onboard Digital Ocean Droplet to Azure Arc

And now, the grand event. We already have a VM in Digital Ocean, and Step 2 ended up with Azure Arc onboarding script stored in a local variable, so why not cross-breed these two together? For that, we just need to replace whoami in our SSH provisioner (terraform_data.arc-connection) with the reference to the onboarding script. Plus, provisioner really should wait until Microsoft.HybridCompute provider registration and Azure Connected Machine Onboarding role assignment are over. Otherwise, nasty race condition might will happen.

And now, the drumroll. I removed (terraform destroy) all previously created resources to make sure that end-to-end deployment doesn’t have any race conditions, and terraform apply -auto-approve created this beauty in just 2 minutes and 8 seconds:

Digital Ocean droplet in Azure Arc


The efforts of countless generations have brought us to this moment. Egyptians built pyramids. Greeks played with logic. Eastern Europeans probably did something useful too. And in our turn, we connected Digital Ocean droplet to Azure Arc, which opens doors to all goodies that Azure has. Automatic updates management, logs collection, monitoring, configuration management, you name it.

In the next post, we’ll enable some of those features. With Terraform, obviously. Despite the frequency of the recent posts, I hope that will happen much sooner than in a year. Probably in a few weeks or so. We’ll configure VM insights, so hybrid cloud concept from the abstract beauty becomes something actually useful. Stay tuned!

Leave a Reply

Your email address will not be published. Required fields are marked *