ModernCyber Blog

Ansible with Cisco FMC

Written by Chad Mitchell | Nov 15, 2022 5:00:00 AM

Repetitive tasks are, in many cases, required but shouldn't be manual tasks in today's day and age. Infrastructure as Code (IaC) and configuration automation tools are here to stay and can easily simplify your daily life with proper use and execution. I have become a fan of Ansible in the recent years and was pleased to discover the Cisco Secure Firewall Management Center (FMC) Ansible library.

Where can I get it???

The collection is posted on the CiscoDevNet GitHub account. The current version is tested with Ansible version 2.9.17 & 2.10.5. I can tell you that I have run it without issues using 2.13.3. It is additionally noted to be tested against FMC versions 7.0 & 7.1 and I will also add that I have run it multiple times against FMC version 7.2 without issue.

Installing the collection on your system is no different than any other collection install and uses Ansible Galaxy with the following command

ansible-galaxy collection install cisco.fmcansible

The GitHub repo also provides a nice mix of samples showing the basic utilization and structure of the collections capabilities.

Where's the documentation?

Where I started to run into my main challenge with the collection was the documentation on the usage of ALL of the available APIs in the FMC when using the Ansible collection. This left me with having to disect the information from the OpenAPI Specification documentation and here is how I did it.

First we need to start out with an installed and accesible FMC. While documentation states that the REST API is enabled by default you should verify this by going to Configuration > REST API Preferences using the gear icon on the top right of the FMC dashboard.

Figure 1 - Enable FMC API

Once you have confirmed that the API is enabled you can now log in to the API Explorer running on the FMC. To do this, open a new browser window and go to https://<fmc-ip>:443/api/api-explorer and log in with your administrator credentials. On the following page you will find a browsable list of APIs that can be used on the FMC to configure ALMOST everything via API. Where the struggle comes is deciphering what the operationId is to call the API that you are interested in using. The Operation ID is not listed on the API Explorer page anywhere (that I could find).

OpenAPI Specification

Thankfully, the API Explorer gives us the option to download the API specification where more details can be found. In the top right, you will see the option to 'Download OAS 3.0 Spec'. Selecting this will give you a JSON formatted output of the full API specification which contains many more details that are hidden behind the web-based browser. It's a rather large file and finding what you are looking for may be daunting. But not to worry we will use the information from the API Explorer UI to get us to the right section of the OAS3 document easily.

For this example, we will use the API to create new network objects in the FMC Objects database. On the API Explorer, we can see the API URL and description for the call that we intend to use.

Figure 2 - API Explorer - Network Objects

The way I started was with taking the Description or URL and searching the OAS3 document with that phrase. You may end up with more than one match so the next keyword to look for is either the API method that begins the section of the spec or the 'operationId' which is exactly what is needed to instruct the Ansible collection to execute what we want it to execute.

Using our search string of the description here we can see the API's POST method for the createMultipleNetworkObject API which is what we want to use in the Ansible playbook.

"post" : {
"tags" : [ "Object" ],
"description" : "**Retrieves, deletes, creates, or modifies the network objects associated with the specified ID. If no ID is specified for a GET, retrieves list of all network objects. _Check the response section for applicable examples (if any)._**",
"operationId" : "createMultipleNetworkObject",
"parameters" : [ {
"name" : "bulk",
"in" : "query",
"description" : "Enables bulk create for network objects.",
"required" : false,
"schema" : {
"type" : "boolean"
}
}, {
"name" : "domainUUID",
"$ref" : "#/components/parameters/domainUUID"
} ]

The Ansible Playbook

Now that we know the operation and have some of the sample playbooks from the GitHub library. We can now assemble our Ansible playbook starting with the hosts declaration and connection data. This section tells Ansible what hosts to target with the following tasks, how to connect to the host, how to handle errors, and if we will collect and use any variables from the remote system.

---
- hosts: fmc
gather_facts: False
connection: httpapi

Following this Ansible needs to tell the FMC API which Domain the rest of the configuration applies to. For most FMC deployments that I have worked with there is only one domain. However in the case that the FMC you are working on has more you can specify the correct index based on the list of domains returned by the following task. This is the code to get all the domains configured on the FMC.

 tasks:
- name: Get Domain
cisco.fmcansible.fmc_configuration:
operation: getAllDomain
register_as: domain
until: domain[0].uuid != ""
retries: 10
delay: 30

The API response and assignment of the domain variable.

ok: [10.99.10.13] => {
"ansible_facts": {
"domain": [
{
"name": "Global",
"type": "Domain",
"uuid": "e276abec-e0f2-11e3-8169-6d9ed49b625f"
}
]
},

After we have the domain list and have registered it as a runtime variable with the 'register_as' command. We are now ready to create the network objects. Whether we are creating one network object or a bunch of them the same API of 'createMultipleNetworkObject' will be used. But we will need to tell the API if it is a bulk operation if we are going to send more than one object in the call. We will start with the example of a single object as seen below.

 - name: Create Network Object
cisco.fmcansible.fmc_configuration:
operation: createMultipleNetworkObject
path_params:
domainUUID: ''
data:
name: 'TEST'
value: '10.99.123.0/24'
type: 'Network'
description: 'Test Network'
overridable: False
register_as: object

As you can see we use the domain variable from our first call to tell the API the correct API URL Path Domain parameter. Following that we provide the data for the single network object that we want to add to the objects database. You could do this for each individual object in an iterative process but the runtime would be significantly longer than just executing a bulk. Again, using the API Explorer we can easily see the option to enable the bulk process or in the OAS3 spec we can see that a bulk parameter can be set. This is done using the query_params option in the Ansible task. The syntax of the object declaration in the Ansible playbook also must change to instruct the process to serialize to a list of dictionaries vs only a single dictionary. This is done by starting each object declaration with a '-'.

 - name: Create Network Object
cisco.fmcansible.fmc_configuration:
operation: createMultipleNetworkObject
path_params:
domainUUID: ''
query_params:
bulk: True
data:
- name: 'TEST1'
value: '10.99.123.0/24'
type: 'Network'
description: 'Test Network 1'
overridable: False
- name: 'TEST2'
value: '10.99.124.0/24'
type: 'Network'
description: 'Test Network 2'
overridable: False
register_as: object

Finally we can run our playbook.

chad@MC-MBP-CHAD % ansible-playbook create_fmc_network_objects.yml

PLAY [fmc] ********************************************************************************************************

TASK [Get Domain] *************************************************************************************************
ok: [10.99.10.13]

TASK [Create Network Object] **************************************************************************************
changed: [10.99.10.13]

PLAY RECAP ********************************************************************************************************
10.99.10.13 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Concluding Playbook

I am truly excited to see Ansible collections for more Cisco solutions and have already been using the FMC collection heavily for current projects and future solutions. I hope the instructions listed above help anyone struggling to find the right operationId's or required variables in their use of the FMC library. Happy coding!

ModernCyber’s Services

If you are looking for help with automation or deployment services using a proven methodology with consistent results let us know. We are actively working on multiple projects where infrastructure as code is used and an invaluable asset to getting success with your missions.

Schedule some time to speak with one of our cybersecurity experts.