Over the last few months I have been doing more and more work with Ansible to manage end-to-end deployments in Azure. For the most part Ansible’s core set of Azure modules work with no problems and more than do the job.
However, with the rate which Microsoft are both adding new features and functionality it is difficult to keep up. The core Ansible modules rely on several Azure Python libraries to be updated before the functionality can be added or introduced to the core module set.
One work around is to use the set of preview modules on Ansible Galaxy provided by Microsoft. However, being this cutting edge comes with a warning that using them could introduce breaking changes to the core Azure modules due to the preview modules reliance on the updated Python SDK.
Typically I tend to ignore these as a lot of the work I do is for production systems and I don’t want to rely on something which could change, potentially quite dramatically, before it gets merged into the core set of Ansible Azure modules.
Luckily, Ansible 2.6 introduced the
azure_rm_resource module, this gives you access to create, update or delete any Azure resource using the Azure REST API, this means that if the Azure REST API supports managing the resource / feature you want to interact with then you will now be able to do it directly from Ansible.
Great you may be thinking to yourself, end of blog post, I will go ahead and use
azure_rm_resource for all of those bits I have been trying to put work arounds in place for …
… as you can imagine, the Azure REST API is quite complicated and there isn’t much documentation for the module.
Using the azure_rm_resource module
So let’s dive in and take a look at how we can use the
azure_rm_resource and Azure REST API to launch a resource, which at the time of writing, is not supported nativity in the core Azure Ansible modules. For this I am going to look at adding an Azure Private DNS zone and then attach it to a Virtual Network – for more information on Azure Private DNS see the overview page.
To start with we are going to be needing a resource group and virtual network, for this we will use the core
azure_rm_subnet modules as we don’t need anything special;
The next part of the playbook is where things start to get interesting as this is where we are going to be making our first call to the Azure REST API using
There are few things to unpack here, before we look at each of the parameters in the task do lets quickly look at what the REST API would look like.
First off is the
PUT request, the request contains the subscription ID, resource group and information around the provider to use, which in this case is the
Microsoft.Network/privateDnsZones one before finally we provide the name of the zone which we would like to be created and the API version to use;
The JSON body we would send with the
PUT request contains the following;
You can see that a lot of the request above is repeated in the parameters we have supplied in the task;
api_version– here you can specify the version of the API to use, this is useful to maintain compatibility within your tasks.
resource_group– is self explanatory, which resource group would you like the REST API to create the resource in?
provider– the provider to use, this just needs to be the name of the provider and not be prefixed by
resource_type– the type of resource we will be creating.
resource_name– the name of the resource we want to create.
body– the body of the request.
So far so good, if we wanted to we could run our playbook and the Resource Group, Virtual Network, Subnets and Private DNS Zone would all be created, however, we need a few more steps to register our newly created Private DNS zone with the virtual network.
Differences when launching resources using the azure_rm_resource module
This is where one of the main differences between using the core Ansible Azure modules and the Azure REST API rears its head. You will be used to the majority of Ansible modules waiting for resource you are creating to complete creating and gather a bunch of facts about the newly created resource before exiting.
As we are now offloading the creation of the resource to the Azure REST API the
azure_rm_resource module only really cares that is making a valid REST request rather than the resource has completed launching. This means we will have added some logic to ensure that our Internal DNS zones have successfully been created before progressing with the registration.
To do this we can use the
azure_rm_resource_info module, this also interacts with the Azure REST API – but it only performs
As you can see, we are registering the output of the request and retrying it until the registered output doesn’t contain ‘NotFound‘, we are also ignoring errors to stop the playbook run from failing on subsequent executions.
Let’s do something a little more complex
Now that we know that our Private DNS zone has been created we are almost ready to register it within our Virtual Network. The raw REST API request to do this looks something like the following;
This time the JSON body contains the following;
As you can see, the body contains more information – one bit of which is a little troublesome. The ID of the virtual network requires a little bit of information, we already know the virtual network name and which resource group the virtual network is in, however, we also need to provide the subscription ID.
So far, Ansible has been supplying this for us but now we actually need to include it as a variable within the task, as we want our playbook to be as dynamic as possible we really should be getting this information on the fly.
Figure out your Azure Subscription ID using Ansible
This is where I struggled a little bit as most of the fact gather modules I looked didn’t return just the subscription ID, so to get around this we are going to use the
azure_rm_resourcegroup_info module. This returns the full resource ID of the group, this in our case would be something like
So what we need to do is register the output of running the
azure_rm_resourcegroup_info module and then using regular expressions grab the subscription ID and set it as a fact using the
Now that we have subscription ID we can then register our Private DNS with the Virtual Network and allow resources created in there to automatically register themselves with the DNS zone;
Now that we have everything we need to create the resource we can run the playbook, a recording of which can be found below;
Checking the Azure portal shows that the resource has been created as expected;
That is only half of the story though, remember the task which registers the internal DNS zone with the virtual network – the JSON body which was sent was starting to get a little complicated…
…and that was only a basic task, trying to do more complex tasks can result in some quite complicated requests needing to be made.
Using Visual Studio Code to get a head start
Luckily, there is a way you can get a head start. Version 0.5.0 of the Ansible Visual Studio Code extension introduced the
Samples for azure_rm_resource command, which is in preview. This command does something extremely useful, it downloads a copy of the Azure REST API specification from the official GitHub repo and updates them to be formatted for use with the
For example, to get an idea on what we needed to do to create the private DNS zone I opened Visual Studio Code, and selected the
Ansible: Samples for azure_rm_resource (PREVIEW) command;
Once selected, I search for
privatedns which then gave me all of the options around managing an Azure Private DNS zone;
Once I had selected
PrivateZones_CreateOrUpdate I was given a list of API versions to choose from, then Visual Studio Code opened a new file and populated it with the same data;
As you can see from the sample data above it is quite easy to follow, also without this tool, performing a complex API request would be extremely difficult as you can see from the task below which creates an Application Gateway;
When dealing with a lot of parameters like that you really do need the head start which the
Samples for azure_rm_resource gives you – I can’t tell you the amount of time and hassle the extension has saved me so far 😄.
Being able to interact directly with the Azure REST API using a tool such as Ansible is a real time saver and let’s you keep up with new features as they are added to Microsoft without having to wait for the Azure module authors and SDK to catch-up.
Practice Manager (SRE & DevOps)
Russ heads up the SRE & DevOps team here at N4Stack.
He's spent almost 25 years working in IT and related industries and currently works exclusively with Linux.
When he's not out buying way too many records, Russ loves to write and has now published six books.
To find out more about Russ click here!