While skimming through Ansible’s recent announcement regarding the release of version 2.4, a couple of features in particular caught my attention. For those not familiar, one of Ansible’s shortcomings when it came to many/most of its network modules, was the lack of declarative state functionality. Seeing the move towards addressing those shortcomings, warranted a deeper look.
In the case of network devices, the first step is to begin to transform from imperative configuration management (having to define each step of the process) to declarative configuration management (defining the end state and letting the platform figure out the steps to get there).
To level set, if you previously wanted to configure a DNS server on your network device, adding it initially was declarative in nature. However, when it came to inherently overwriting or deleting that entry because you replaced your old DNS server with a new one, the declarative logic was just not there.
Let’s illustrate this with an imperative example (cli commands):
Now, lets go ahead and use the same imperative logic to replace our existing DNS server with a new one.
Predictably, the new DNS server IP was just appended to our existing configuration. Let’s now try using the declarative logic in the new “ios_system” resource module.
Now, lets again attempt to replace the DNS IP with the new one.
As you can see, apart from adding our desired new server IP, the logic got rid of the old IP behind the scenes. On the surface, this might not seem like that big of a deal. However, imagine doing this at scale for thousands of devices and hundreds of features (not only DNS). In these types of environments, when you want your end state fully controlled by a source of truth (ie, Git), being able to declare end state and have your playbooks run in an idempotent way is critical. Without that, accounting for configuration that is present before deleting it is a lot to keep track of, and ends up being one step forward / two steps back.
The last couple of releases of Ansible have focused on standardizing network modules across vendors. One vendor’s “command” or “config” module should in theory look identical to another vendor’s “command” or “config” module. This type of standardization is great, especially from a Jinja2 template standpoint when trying to build your multi-vendor device configs in a dynamic fashion. The only drawback with the “command”/”config” modules however, is a reliance on the imperative way of managing your configs via cli commands.
With Ansible 2.4, there is now an effort to standardize non-cli specific modules across vendors. Configuring features like DNS (see above) or interfaces across multiple vendors, should look standardized and identical, without the need to specify actual cli commands.
While the feature offering with the new resource modules is still slim to start with, I can see the number of modules rapidly growing in the future releases. Even though I think it’s a bit of a drawback for customers to have to wait for vendorX to implement a separate module for new featureY, the separation between these functions allows for rather useful and creative operational checks to be implemented inside of them (see lldp verification). I’m also curious to see how this YANG-like effort to standardize feature deployment pans out, once the growing number of vendors start pulling these modules in different directions.
At the end of the day, it’s great to see Ansible packing more and more network-specific features into their releases (while doing their best to keep it standardized). By abstracting vendor specific cli commands required to run various services, the barrier to entry for automating your network becomes lower as time goes on.