User Guide
This section gives a good introduction how to get started with ftd_configuration
module that allows to configure FTD devices.
First, make sure that Ansible with FTD modules is installed.
Task operations
ftd_configuration
module allows executing all operations available in REST API in a form of Ansible tasks. Each REST API endpoint can be wrapped into an Ansible play and be a part of a playbook.
Begin by finding a necessary operation from Operation Index. Most operations are similar to CRUD functions and can be divided into the following groups:
get
- fetches a object by its ID (e.g.,getNetworkObject
);getList
- fetches a list of objects matching given criteria (e.g.,getNetworkObjectList
). For example, it can be used to find an object by name or other attribute when its ID is not known;add
- creates a new object (e.g.,addNetworkObject
);edit
- updates an existing object (e.g.,editNetworkObject
). ID of the existing object is a mandatory attribute for this operation type;delete
- deletes an existing object by its ID (e.g.,deleteNetworkObject
);upsert
- creates a new object if it does not exist, or updates it when the object already exists (e.g.,upsertNetworkObject
). By default,upsert
operation looks for an object by its name, but the filtering criteria can be adjusted. See Upsert operation for more details;
Task parameters
Each ftd_configuration
play has required and optional parameters indicating what API endpoint to call and with what parameters.
Parameter | Required | Description | |||
---|---|---|---|---|---|
operation | True | Defines the operation to call. | |||
data | False | Corresponds to the body part in HTTP request and is mandatory for add , edit , and upsert operations. |
|||
path_params | False | Corresponds to URL parameters in HTTP request and is mandatory for edit , and delete operations. |
|||
query_params | False | Corresponds to the query string in HTTP request and is usually used for getList operations. |
|||
filters | False | A map with filter criteria for upsert and getList operations. |
|||
register_as | False | A name for the fact that is registered with the response from the server. |
For example, to create or update an existing object, use the upsert
operation with the data
parameter:
- name: Create a TCP port for PostgreSQL
ftd_configuration:
operation: upsertTCPPortObject
data:
name: PostgreSQL port
port: '5432'
type: tcpportobject
To filter out a specific object, the getList
operation with the filters
parameter can be used:
More examples can be found here.
Upsert operation
Upsert is an idempotent "Insert or Update" operation which came from relational databases.
It allows defining the desired state of the record instead of checking whether the record exists (and should be updated) or not (and should be created).
As a result, playbook syntax looks simpler and more declarative, so we recommend using upsert operations whenever possible.
Here is an example of an upsert operation defining a network:
- name: Upsert localhost network
ftd_configuration:
operation: upsertNetworkObject
data:
name: LocalhostNetwork
subType: HOST
value: 127.0.0.1
type: networkobject
And the equivalent of the upsert task written using other operations:
- name: Get existing networks with LocalhostNetwork name
ftd_configuration:
operation: getNetworkObjectList
query_params:
filter: name:LocalhostNetwork
register_as: my_networks
- name: Create a network if it does not exist
ftd_configuration:
operation: addNetworkObject
data:
name: LocalhostNetwork
subType: HOST
value: 127.0.0.1
type: networkobject
when: my_networks.0 is undefined
- name: Update the network if it already exists
ftd_configuration:
operation: editNetworkObject
data:
name: LocalhostNetwork
subType: HOST
value: 127.0.0.1
type: networkobject
path_params:
objId: {{ my_networks[0].id }}
when: my_networks.0 is not undefined
Filtering in upsert operation
Upsert operation uses filters to look for a record on the device and define if the object already exists or not. Default filtering
is done by name, and the object having a name as in data
parameter is sought.
If you want to use a different attribute for filtering, overwrite filters
parameter. For example, to update a physical interface
with a certain hardware name, add a hardwareName: {VALUE}
keypair to filters
:
- name: Setup Outside Interface with static IP
ftd_configuration:
operation: upsertPhysicalInterface
data:
name: outside
hardwareName: GigabitEthernet0/0
monitorInterface: true
ipv4:
addressNull: false
defaultRouteUsingDHCP: false
dhcp: false
ipAddress:
ipAddress: 192.168.10.2
netmask: 255.255.255.0
standbyIpAddress: 192.168.10.3
type: haipv4address
ipType: STATIC
type: interfaceipv4
mtu: 1500
enabled: true
mode: ROUTED
type: physicalinterface
filters:
hardwareName: GigabitEthernet0/0
When filters
are too generic and more than one existing object satisfy filtering criteria, the module stops the play and raises
an error asking to specify filters more precisely to match one object exactly.
Registering objects as Ansible facts
In order to reuse objects in subsequent plays during an ansible-playbook run, they should be registered as variables.
FTD modules automatically register server responses as Ansible facts. Then, these facts can be used in the playbook as
regular variables.
By default, fact names are constructed as {OBJECT_TYPE}_{LOWERCASE_OBJECT_NAME}
. For example, an addNetworkObject
play creating a NetworkObject
named LocalhostNetwork
registers the newly created object in anetworkobject_localhostnetwork
fact:
$ ansible-playbook test.yml -vvv
TASK [Create localhost network] ***********************************************************
ok: [localhost] => {
"ansible_facts": {
"networkobject_localhostnetwork": {
"description": null,
"dnsResolution": null,
"id": "a016b91b-cbdb-11e8-8efc-8f046e5a37e0",
"isSystemDefined": false,
"links": {
"self": "https://localhost/api/fdm/v2/object/networks/a016b91b-cbdb-11e8-8efc-8f046e5a37e0"
},
"name": "LocalhostNetwork",
"subType": "HOST",
"type": "networkobject",
"value": "127.0.0.1",
"version": "oc2amviloyzed"
}
},
...
If you want to change the default naming convention, add a register_as
parameter with the desired fact name to the play.
- name: Create network and register as 'localNet'
ftd_configuration:
operation: addNetworkObject
data:
name: LocalhostNetwork
subType: HOST
value: 127.0.0.1
type: networkobject
register_as: localNet
- name: Update description
ftd_configuration:
operation: editNetworkObject
data:
id: {{ localNet.id }}
version: {{ localNet.version }}
name: LocalhostNetwork
description: Loopback network to access local computer
subType: HOST
value: 127.0.0.1
type: networkobject
path_params:
objId: {{ localNet.id }}
Facts are set on a host-by-host basis. When the playbook is run on multiple devices (hosts), facts are
visible in a context of a single device (host) only.
Task idempotency
A task is idempotent if the result of running it once is exactly the same as the result of running it
multiple times. As Ansible requires modules to be idempotent, ftd_configuration
complies with this requirement.
Before executing the operation, ftd_configuration
checks whether the desired final state is already achieved.
If yes, no actions are executed, and the operation finishes showing that the state is not changed. Comparation of
objects is described below.
For example, when running addNetworkObject
operation multiple times without changing the play configuration,
only the first run results in changed
status. Subsequent runs are finished with ok
status.
$ ansible-playbook test.yml
TASK [Create localhost network] *******************************************
changed: [localhost]
PLAY RECAP ****************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0
$ ansible-playbook test.yml
TASK [Create localhost network] *******************************************
ok: [localhost]
PLAY RECAP ****************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
When playbook tasks are idempotent, the playbook itself must be idempotent too. Having idempotent playbook is
recommended, as it allows to re-run them safely.
How objects are being compared
Comparation take place every time, when object should be modified. The first object is an object from playbook
(local object) and the second object is an object on server (remote object).
There are several cases when local and remote objects have:
1) same fields and values - same objects
| Local object | Remote object | Value |
| -------------- | -------------- | ----- |
| field1: value1 | field1: value1 | Same |
| field2: value2 | field2: value2 | Same |
| field3: value3 | field3: value3 | Same |
2) same fields and different values - different objects;
| Local object | Remote object | Value |
| -------------- | --------------- | ---------- |
| field1: value1 | field1: value10 | Different |
| field2: value2 | field2: value20 | Different |
| field3: value3 | field3: value3 | Same |
3) different set of fields but common fields has same values - same objects;
| Local object | Remote object | Value |
| -------------- | -------------- | -------- |
| field1: value1 | | No Field |
| field2: value2 | field2: value2 | Same |
| field3: value3 | field3: value3 | Same |
| | field4: value4 | No Field |
4) different set of fields but common fields has different values - different objects.
| Local object | Remote object | Value |
| -------------- | --------------- | --------- |
| field1: value1 | | No Field |
| field2: value2 | field2: value20 | Different |
| field3: value3 | field3: value3 | Same |
| | field4: value4 | No Field |