Ansible and the Azure REST API
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_resourcegroup
, azure_rm_virtualnetwork
and 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 azure_rm_resource
;
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 byMicrosoft
. -
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 GET
requests;
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 /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroup/myResourceGroup
.
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 set_fact
module;
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 azure_rm_resource
module.
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 DNS
;
Selected 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.
Further Insights
Got 2 minutes to spare to watch our Managed DevOps offering?
Have a watch of our short 2-minute video on our Managed DevOps offering – a modern approach for modern applications.
Find out about our DevOps Managed Service
Our DevOps Managed Services are designed for customers who require a modern operational model for cloud native applications.
How Not to Screw Up Your Move to Azure
A deep dive into our experience of delivering hundreds of cloud migration projects, including lessons learned and must-do activities.
Russ McKendrick
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!