Network Configuration Templating with Ansible – Part 1

When discussing network automation with our customers, one of the main concerns that come up is the ability to audit their device configurations. This becomes especially important during the last quarter of the year, as many corporations are going through their yearly audit to obtain their required approvals for PCI or other compliance standards. Our solution for that is to use the Golden Configuration application for Nautobot, but it’s also entirely possible to use simple Ansible playbooks to perform the audit. This blog series will go over the essential pieces to understanding network configuration templating using Ansible, but the same process can easily be translated for use with Nautobot.

To start templating your configuration you must identify the feature that you wish to audit. Whether it be your DNS or NTP settings, it’s usually easier to start with small parts of the configuration before moving on to the more complicated parts, such as routing or interfaces. With a feature selected, you can start reviewing the device configurations to create your templates. For this article, we’ll use NTP configuration from an IOS router as the chosen feature:

ntp server 1.1.1.1 prefer
ntp server 1.0.0.1
ntp server 8.8.8.8
clock timezone GMT 0
clock summer-time CET recurring

After you’ve identified the portions of the configuration that you wish to template for the feature, the next step is to review the configuration snippet(s) and identify the variables relevant to the configuration feature. Specifically, you want to extract only the non-platform-specific variables, as the platform-specific syntax should be part of your template with the variables abstracted away for use across platforms. Using the example above, we can extract the following bits of information:

  • three NTP server hosts
    • 1.1.1.1
    • 1.0.0.1
    • 8.8.8.8
  • preferred NTP server
    • 1.1.1.1 is preferred
  • time zone and offset
    • GMT
    • 0
  • daylight saving timezone
    • CET

With these variables identified, the next step is to define a schema for these variables to be stored in. For Ansible this is typically in a YAML file as host or group vars. As YAML is limited in the types of data it can document, lists and key/value pairs typically, it’s best to design the structure around that limitation. With the example above, we’d want to have a list of the NTP servers as one item with a key noting which is preferred, the timezone with offset, and the daylight saving timezone. One potential schema would be like the below:

---
# file: group_vars/all.yml
ntp:
  servers:
    - ip: "1.1.1.1"
      prefer: true
    - ip: "1.0.0.1"
      prefer: false
    - ip: "8.8.8.8"
      prefer: false
  timezone:
    zone: "GMT"
    offset: 0
    dst: "CET"

Defining this structure is important as it will need to be flexible enough to cover data for all platforms while also being simple enough that your templates don’t become complicated. You’ll want to ensure that all other variables that are for this feature are of the same structure to ensure compatibility with the Jinja2 templates you’ll be creating in future parts of this series. It’s possible to utilize something like the Schema Enforcer framework to enable enforcement of your schemas against newly added data. This allows you a level of trust that the data provided to the templates are of the right format.

The next step, once the variables have been defined and you’ve determined a structure for them, is to understand where they belong within your network configuration hierarchy. This means that you need to understand in which circumstances these values are considered valid. Are they globally applicable to all devices or only to a particular region? This will define whether you place the variables in a device-specific variable or a group-specific one, and if in a group which group. This is especially important, as where you place the variables will define which devices inherit them and will use them when it comes time to generate configurations. For this article, we’ll assume that these are global variables and would be placed in the all group vars file. With this in mind, you’ll want to start building your inventory with those variable files. Following the Ansible Best Practices, it’s recommended to have a directory layout like so:

inventory.yml
group_vars/
    all.yml
    routers.yml
    switches.yml
host_vars/
    jcy-rtr-01.infra.ntc.com.yml
    jcy-rtr-02.infra.ntc.com.yml

This should allow for clear and quick understanding of where the variables are in relation to your network fleet. This will become increasingly important as you build out more templates and adding variables. With your inventory structure built out, you can validate that the variables are assigned to your devices as expected with the ansible-invenotry -i inventory.yml --list which will return the variables assigned to each device like so:

{
    "_meta": {
        "hostvars": {
            "jcy-rtr-01.infra.ntc.com": {
                "ntp": {
                    "servers": [
                        {
                            "ip": "1.1.1.1",
                            "prefer": true
                        },
                        {
                            "ip": "1.0.0.1",
                            "prefer": false
                        },
                        {
                            "ip": "8.8.8.8",
                            "prefer": false
                        }
                    ],
                    "timezone": {
                        "dst": "CET",
                        "offset": 0,
                        "zone": "GMT"
                    }
                }
            },
            "jcy-rtr-02.infra.ntc.com": {
                "ntp": {
                    "servers": [
                        {
                            "ip": "1.1.1.1",
                            "prefer": true
                        },
                        {
                            "ip": "1.0.0.1",
                            "prefer": false
                        },
                        {
                            "ip": "8.8.8.8",
                            "prefer": false
                        }
                    ],
                    "timezone": {
                        "dst": "CET",
                        "offset": 0,
                        "zone": "GMT"
                    }
                }
            }
        }
    },
    "all": {
        "children": [
            "routers",
            "ungrouped"
        ]
    },
    "routers": {
        "hosts": [
            "jcy-rtr-01.infra.ntc.com",
            "jcy-rtr-02.infra.ntc.com"
        ]
    }
}

Conclusion

This allows you to validate and ensure that the variables you’ve created are being assigned where you expect. In the next part of this series we’ll dive into how to craft a configuration template using the Jinja2 templating engine.

-Justin



ntc img
ntc img

Contact Us to Learn More

Share details about yourself & someone from our team will reach out to you ASAP!

Author