Saturday, August 11, 2018

Ansible REST API - Interacting with Cisco FirePower Management Center (FMC) - 08 - Get deployable devices and deploy policy

This post belongs to my "Ansible REST API - Interacting with Cisco FMC" series. The following is the table of content of this series:
  1. Introduction and Ansible playbook download
  2. Script flow charts
  3. Introduction of REST API and Cisco FMC API Explorer
  4. Script prerequisites
  5. Request Access Token
  6. Get policy content, modify content and "PUT' in FMC - Part 1
  7. Get policy content, modify content and "PUT' in FMC - Part 2 
  8. Get deployable devices and deploy policy
In the previous post, we have successfully upload the modified policy rule to FMC. Now we need to deploy the new policy to the edge device(s). The following are the steps:

a. get the array of the devices which do not have updated configuration (deployable devices)
b. obtain the "version" of the configuartion
c. obtain the "Device ID" of the deployable devices
d. lookup the deployment JSON template and modify its "version" and "device ID" keys
e. make a "POST" call to FMC to deploy the update configuration to the edge device(s)

First, we make a "GET" call to fetch the deployable device information from FMC.
 
  - name: Get_Deployment_Device_Info

The return of this "GET" call is information about the devices which have not applied the updated configuration. The same as other "GET" returns, it contains a "items" array in a key which named "json".

Please be noted that all of the following tasks will only be executed when the "config_has_been_changed" is equal to "true".

location: /etc/ansible/roles/FMC-enable-policyrule/tasks/main.yml

  - name: Get_Deployment_Device_Info
    uri:
      url: "https://{{FMC_IP}}/api/fmc_config/v1/domain/{{domain}}/deployment/deployabledevices?expanded=true"
      method: GET
      headers:
        "x-auth-access-token": "{{acc_token}}"
        Connection: keep-alive
      validate_certs: no
      status_code: 200
      body_format: json
    register: DeployableDevice
    when: config_has_been_changed


- name: get device array

WE need to abstract the "items" array from the "DeployableDevice.json" key value:


 location: /etc/ansible/roles/FMC-enable-policyrule/tasks/main.yml

  - name: get device array
    set_fact:
      device_array: "{{DeployableDevice.json | json_query('items[]')}}"
    when: config_has_been_changed


We utilise the "json_query" here to achieve this task. 

- name: get version number

In the "items" array, we will find one more multiple items. Each item contains the information for a deployable device. What we need here is the "version" key value and the "device ID" key value.

The "version" number of all the deployable devices are the same in FMC. Every time, even you just make a single change on a single device policy, the version for all the deployable devices will be updated. And this "version" number is calculated by timestamp. 

Therefore, we just need to pull the "version" number from the 1st item in this array for our task.

 location: /etc/ansible/roles/FMC-enable-policyrule/tasks/main.yml

  - name: get version number
    set_fact:
      deployment_version: "{{device_array[0].version}}"
    when: config_has_been_changed


  - name: get the deployment device list

Here, we will create a new array which contains the deployable device ID(s).Here, we are getting all the deployable device IDs. Not only the one we just modified in the previous post.

First, we create a new empty array call "deployment_device_list". Then we add every "item.device.id" to this array as new item.

Here we also add "no_log" to this task. As the output log for this task is too much. I just don't want this log to be shown in the terminal.

 location: /etc/ansible/roles/FMC-enable-policyrule/tasks/main.yml

  - name: get the deployment device list  # here we get all the deployable devices, NOT the policy we just changed
    set_fact:
      deployment_device_list: "{{deployment_device_list | default([])}} + ['{{item.device.id}}']"
    with_items: "{{device_array}}"
    no_log: true # don't display output for this session, since too much!
    when: config_has_been_changed


- name: get JSON POST file

In order to make the "POST" call, we need the "POST" call template. We can make this template based on the example provided by FMC API explorer. The template is shown as below:

location: /etc/ansible/roles/FMC-disable-policyrule/tasks/deployment_POST.json

{
  "type": "DeploymentRequest",
  "version": "",
  "forceDeploy": true,
  "ignoreWarning": true,
  "deviceList": []
}


As we can see, this "POST" template is very simple. Its "type" is "DeploymentRequest". Its "version" has a empty string. And the "deviceList" has a empty array.

The following is the way to use "lookup" method to pull the JSON file template.

 location: /etc/ansible/roles/FMC-enable-policyrule/tasks/main.yml

  - name: get JSON POST file
    set_fact:
      deployment_POST: "{{lookup('file','/etc/ansible/roles/FMC-disable-policyrule/tasks/deployment_POST.json')}}"
    when: config_has_been_changed


  - name: modify the version

Since the template has a empty string as "version" value, we will need to update it with our obtained "version".

location: /etc/ansible/roles/FMC-enable-policyrule/tasks/main.yml

  - name: modify the version
    set_fact:
      deployment_POST: "{{deployment_POST | combine({'version': deployment_version})}}"
    when: config_has_been_changed


As mentioned in the previous post, if the key exists in the JSON, "combine" filter will only update the key value. So in this case, the "version" key will be updated with our new version number.

- name: modify the deployment device list

The same, we update the "deviceList" array with our deployable device ID array:

location: /etc/ansible/roles/FMC-enable-policyrule/tasks/main.yml

  - name: modify the deployment device list
    set_fact:
      deployment_POST: "{{deployment_POST | combine({'deviceList': deployment_device_list})}}"
    when: config_has_been_changed


- name: Deployment POST

Finally, we have our "POST" JSON ready. Then we need to make a "POST" call to the FMC and raise a device deployment request:

location: /etc/ansible/roles/FMC-enable-policyrule/tasks/main.yml

  - name: Deployment POST
    uri:
      url: "https://{{FMC_IP}}/api/fmc_config/v1/domain/{{domain}}/deployment/deploymentrequests"
      method: POST
      headers:
        "x-auth-access-token": "{{acc_token}}"
      validate_certs: no
      status_code: 202
      body_format: json
      body: "{{deployment_POST}}"
    when: config_has_been_changed


Just pay attention to the "status_code". For this "POST" call, the return code is "202".

After making this "POST" call, our changes will be deployed to the edge firewalls. And our goal has been achieved.

Conclusion:

The purpose of writing this post series is not only to demonstrate the way how to work with Cisco FMC with Ansible. The most important thing is that we can learn about how to use ansible modules, filters and jinja2 templates to add or modify key/values in JSON and make different API calls. These are the basic skills for REST API interaction.


As more and more network devices have REST API support, if we fully master the way to interact with REST API, network automation will become a common task in our daily work. 

Reference:

No comments:

Post a Comment

NSX Load Balancer "Application Rules" Examples:

Load Balancing is one of the features provided by the NSX Edge Services Gateway (ESG). It can provide L7 Load Balancing by utilizing the HA...