Introduction
In the fast-paced world of networking, automation has become a crucial element in optimising efficiency and reducing manual effort. One prominent player in the networking space is F5 BIG-IP, a powerful application delivery controller. Pairing it with Ansible, a powerful automation tool, can unlock a whole new level of network automation capabilities. In this blog post, we will explore the benefits and techniques of automating F5 BIG-IP using Ansible.
Introducing Ansible for Network Automation
Ansible is an open-source automation tool that provides a simple and efficient way to automate IT infrastructure. With its declarative language and agentless architecture, Ansible makes it easy to automate F5 BIG-IP tasks. It allows network administrators to define the desired state of their infrastructure and automatically brings it into compliance.
Setting Up the Ansible Environment
Before diving into F5 BIG-IP automation with Ansible, it’s essential to set up the necessary environment. Following official Ansible documentation will guide you through the installation steps based on your platform.
Ansible Official Documentation
Testing Environment
- F5 BIG-IP VM Appliance Version - 13.1.5.1
- Ansible Controller OS - Red Hat Enterprise Linux release 9.3 (Plow)
- Ansible Core Version - 2.15.6
- Python Version - 3.11
Creating Ansible Playbooks for F5 BIG-IP Automation
Ansible playbooks are at the heart of automating F5 BIG-IP tasks. In this section, we will explore the structure and components of Ansible playbooks tailored specifically for F5 BIG-IP automation.
Ensure your controller is installed with F5 Ansible collection. More Info
ansible-galaxy collection install f5networks.f5_modules ./collection
To further enhance the reusability and modularity of F5 BIG-IP automation, Ansible roles can be employed. Create a Ansible role as f5-bigip (Any preferred role name can be used for Ansible).
ansible-galaxy init ./roles/f5-bigip
When you create the Ansible role, you’ll find the folder structure as follows.
❯ tree roles
roles
└── f5-bigip
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
└── vars
└── main.yml
9 directories, 5 files
We are now ready to start developing Ansible playbooks to automate F5 BIG-IP. The first thing we need to do is test connectivity between F5 and Ansible controller.We need to specify these connection parameters in order to establish a connection with the remote device. At the time of this writing, the F5 Ansible modules communicate almost exclusively over the REST API of the F5 device and this is accomplished by using a provider pattern.
In order to do this, update roles/vars/main.yml
as below.
---
# vars file for f5-bigip
ansible_connection: local
f5_provider:
password: {update the f5 user account here}
server: {update the f5 user account here}
user: {update the f5 user account here}
validate_certs: False
server_port: 443
The connection provider has now been set up, and now we need to create the Ansible role tasks.
In this Ansible role, we will cover the following workflow,
Validate the node, virtual server and pool existence → Create new nodes → Create pool → Add nodes to the created pool → Create virtual server → Attach pool to the virtual server.
To separate each task in the Ansible role, I have the following YAML files within the roles/tasks folder:
├── tasks
│ ├── add_members_to_pool.yml
│ ├── add_nodes.yml
│ ├── add_virtual_server.yml
│ ├── create_pool_list.yml
│ ├── f5_gather_fact_nodes.yml
│ ├── f5_gather_fact_vip_pool.yml
│ └── main.yml
Validate Nodes Existence
Here, I have added the following tasks to f5_gather_fact_nodes.yml
in order to validate nodes’ existence using the bigip_device_info
module.
---
- name: Collect BIG-IP LTM Pool, Nodes and Virtual Servers Information
bigip_device_info:
gather_subset:
- "nodes"
provider: "{{ f5_provider }}"
delegate_to: localhost
register: info_out
- name: Set the query with replacing with {{ item.name }} variable
set_fact:
node_query: "ansible_net_nodes[?name=='{{ item.name }}'].name"
- name: Set the node name
set_fact:
node_name:
- "{{ info_out.ansible_facts| json_query ( node_query ) | first }}"
- name: Print node name
ansible.builtin.debug:
msg:
- "Node name {{ node_name[0] }} is already exist"
Validate Pool and Virtual Server Existence
The following tasks have been added to f5_gather_fact_vip_pool.yml
to validate the existence of the pool and virtual server.
---
- name: Collect BIG-IP LTM Pool, Nodes and Virtual Servers Information
bigip_device_info:
gather_subset:
- "ltm-pools"
- "virtual-servers"
provider: "{{ f5_provider }}"
delegate_to: localhost
register: info_out
- name: Set the query with replacing with variable
set_fact:
pool_query: "ansible_net_ltm_pools[?name=='{{ pool_name }}'].name"
vip_name_query: "ansible_net_virtual_servers[?name=='{{ vip_name }}'].name"
- name: Set the fact with replacing with {{ pool_name }} and {{ vip_name }} variable
set_fact:
pool_name_status:
- "{{ info_out.ansible_facts| json_query ( pool_query ) | first }}"
virtual_ip_name:
- "{{ info_out.ansible_facts| json_query ( vip_name_query ) | first }}"
- name: Print virtual server,and pool name
ansible.builtin.debug:
msg:
- "Pool name {{ pool_name_status }} is already exist"
- "Virtual Server name {{ virtual_ip_name }} is already exist"
Create Multiple Nodes
The next step is to create the pool members in your BIG-IP configuration once the validation is complete. Here, I have added the following tasks to add_nodes.yml
to create multiple nodes with a block and rescue controller. A rescue block specifies tasks that will run when an earlier task fails in a block. It is similar to the way many programming languages handle exceptions. Here again, I have used the bigip_device_info
module for the second validation, and if the validation fails, the bigip_node
module will create all pool members.
---
- block:
- name: Collect BIG-IP LTM Nodes information
bigip_device_info:
gather_subset:
- "nodes"
provider: "{{ f5_provider }}"
delegate_to: localhost
register: info_out
- name: Set the query with replacing with {{ item.name }} variable
set_fact:
node_query: "ansible_net_nodes[?name=='{{ item.name }}'].name"
- name: Set the node name
set_fact:
node_name:
- "{{ info_out.ansible_facts| json_query ( node_query ) | first }}"
- name: Print node name
ansible.builtin.debug:
msg: "Node name {{ node_name[0] }} is already exist"
rescue:
- name: Create {{ item.name }} node in the F5
bigip_node:
host: "{{ item.host }}"
name: "{{ item.name }}"
provider: "{{ f5_provider }}"
delegate_to: localhost
when: node_name is undefined
- name: Print node {{ item.name }} status
ansible.builtin.debug:
msg: "The {{ item.name }} node has been created "
Create Pool List
Now, we have to create the pool. Here, I have also followed the same approach as the previous tasks and added the following task to the create_pool_list.yml
and used the bigip_pool
module to create the pool.
---
- block:
- name: Collect BIG-IP LTM Pool information
bigip_device_info:
gather_subset:
- "ltm-pools"
provider: "{{ f5_provider }}"
delegate_to: localhost
register: info_out
- name: Set the query with replacing with {{ pool_name }} variable
set_fact:
pool_query: "ansible_net_ltm_pools[?name=='{{ pool_name }}'].name"
- name: Set the pool name
set_fact:
pool_name_status:
- "{{ info_out.ansible_facts| json_query ( pool_query ) | first }}"
- name: Print pool name
ansible.builtin.debug:
msg: "Node name {{ pool_name_status }} is already exist"
rescue:
- name: Create a {{ pool_name }} pool in F5
bigip_pool:
provider: "{{ f5_provider }}"
lb_method: "{{ load_balancing_method }}"
name: "{{ pool_name }}"
slow_ramp_time: 120
delegate_to: localhost
- name: Print {{ pool_name }} pool status
ansible.builtin.debug:
msg: "The {{ pool_name }} pool has been created"
Add Members to the Created Pool
Now that we have created the pool and the nodes for that pool. The next step is to add all created nodes to the pool as members. Here, I have made a separate task in add_members_to_pool.yml
using the bigip_pool_member
module.
---
- name: Add {{ item.name}} members to the {{ pool_name }} pool
bigip_pool_member:
provider: "{{ f5_provider }}"
description: "webserver {{ item.name }}"
host: "{{ item.host }}"
name: "{{ item.name }}"
pool: "{{ pool_name }}"
port: "{{ service_port }}"
delegate_to: localhost
Create Virtual Server and Attach the Pool
I have created separate tasks in the add_virtual_server.yml
playbook to create the virtual server and attach the previously created pool to the virtual server. I used the second validation option as before and used the bigip_virtual_server
module to create a virtual server in F5.
---
- block:
- name: Collect BIG-IP LTM Virtual Servers information
bigip_device_info:
gather_subset:
- "virtual-servers"
provider: "{{ f5_provider }}"
delegate_to: localhost
register: info_out
- name: Set the query with replacing with {{ vip_name }} variable
set_fact:
vip_name_query: "ansible_net_virtual_servers[?name=='{{ vip_name }}'].name"
- name: Set the vip name
set_fact:
virtual_ip_name:
- "{{ info_out.ansible_facts| json_query ( vip_name_query ) | first }}"
- name: Print virtual server name
ansible.builtin.debug:
msg: "Virtual Server name {{ virtual_ip_name }} is already exist"
rescue:
- name: Create the {{ vip_name }} vip in F5
bigip_virtual_server:
provider: "{{ f5_provider }}"
description: "{{ description }}"
destination: "{{ virtual_ip }}"
name: "{{ vip_name }}"
pool: "{{ pool_name }}"
port: "{{ vip_service_port }}"
snat: Automap
profiles:
- http
- clientssl
delegate_to: localhost
- name: Print the {{ vip_name }} status
ansible.builtin.debug:
msg: "The {{ vip_name }} virtual server has been created"
Import Tasks to the main.yaml
Finally, all created playbooks have been imported to the main.yml
file under the block and rescue controller. If the validation fails, recuse will execute all tasks under the rescue section and create the nodes, pools and virtual server accordingly. A loop has also been created for the member list within the relevant tasks.
---
- block:
- name: Gather fact from F5 BIG-IP
ansible.builtin.include_tasks: f5_gather_fact_nodes.yml
loop: "{{ member_list }}"
- name: Gather fact from F5 BIG-IP
ansible.builtin.import_tasks: f5_gather_fact_vip_pool.yml
rescue:
- name: Create nodes
ansible.builtin.include_tasks: add_nodes.yml
loop: "{{ member_list }}"
- name: Create a pool List
ansible.builtin.import_tasks: create_pool_list.yml
- name: Add members to the Pool
ansible.builtin.include_tasks: add_members_to_pool.yml
loop: "{{ member_list }}"
- name: Create virtual server and attach the pool
ansible.builtin.import_tasks: add_virtual_server.yml
Setup Default Variables
Now, you must pass all required variables in /roles/defaults/main.yml
file as below. The following example uses the following values according to my testing environment. Consequently, you may need to modify it for your particular environment.
# Pool Information
load_balancing_method: ratio-member
pool_name: test-pool
# Member Information
service_port: 80
member_list:
- host: 10.1.1.1
name: app01
- host: 10.1.1.2
name: app02
- host: 10.1.1.5
name: app03
# Virtual Server Information
description: test-vip
virtual_ip: 192.168.1.2
vip_name: test_vip
vip_service_port: 80
Create main.yml
to call the Ansible Role
Your Ansible role is now referenced through the main playbook below. In this example, I renamed the main playbooks as f5.yaml
.
---
- name: Create F5 LTM Rule
hosts: localhost
connection: local
collections:
- f5networks.f5_modules
roles:
- f5-bigip
Update your Inventory
and ansible.cfg
You can update your inventory
and ansible.cfg
file according to your Ansible environment. I prefer to have separate configurations for each test I do in my environment. According to this configuration, it refers to the current working directory.
ansible.cfg
File
[defaults]
INVENTORY = inventory
command_warnings = False
collections_paths = ./collections/ansible_collections
ansible_python_interpreter= /usr/bin/python3.11
roles_path = ./roles
Inventory
File
localhost
Run the Ansible Role
Our Ansible role is now ready to run. In this case, we will run it from the top-level Ansible directory.
❯ tree f5_bigip_automation_with_ansible
f5_bigip_automation_with_ansible
├── README.md
├── ansible.cfg
├── collections
│ └── requirements.yml
├── f5.yml
├── inventory
└── roles
└── f5-bigip
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ ├── add_members_to_pool.yml
│ ├── add_nodes.yml
│ ├── add_virtual_server.yml
│ ├── create_pool_list.yml
│ ├── f5_gather_fact_nodes.yml
│ ├── f5_gather_fact_vip_pool.yml
│ └── main.yml
├── templates
└── vars
└── main.yml
11 directories, 16 files
Please refer to the following command.
ansible-playbook f5.yml
Below is an example of the output you should see if you followed the above steps correctly.
[dhananjak@ansible-core ansible_projects]$ ansible-playbook f5.yml
PLAY [Create F5 LTM Rule] *******************************************************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Gather fact from F5 BIG-IP] ************************************************************************************************************************************************************************************
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/f5_gather_fact_nodes.yml for localhost => (item={'host': '10.1.1.1', 'name': 'app01'})
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/f5_gather_fact_nodes.yml for localhost => (item={'host': '10.1.1.2', 'name': 'app02'})
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/f5_gather_fact_nodes.yml for localhost => (item={'host': '10.1.1.5', 'name': 'app03'})
TASK [f5-bigip : Collect BIG-IP LTM Pool, Nodes and Virtual Servers Information] ************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the query with replacing with app01 variable] **************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the node name] *********************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: No first item, sequence was empty.. No first item, sequence was empty.\n\nThe error appears to be in '/home/dhananjak/ansible_projects/roles/f5-bigip/tasks/f5_gather_fact_nodes.yml': line 14, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Set the node name\n ^ here\n"}
TASK [f5-bigip : Create nodes] **************************************************************************************************************************************************************************************************
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_nodes.yml for localhost => (item={'host': '10.1.1.1', 'name': 'app01'})
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_nodes.yml for localhost => (item={'host': '10.1.1.2', 'name': 'app02'})
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_nodes.yml for localhost => (item={'host': '10.1.1.5', 'name': 'app03'})
TASK [f5-bigip : Collect BIG-IP LTM Nodes information] **************************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the query with replacing with app01 variable] **************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the node name] *********************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: No first item, sequence was empty.. No first item, sequence was empty.\n\nThe error appears to be in '/home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_nodes.yml': line 15, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Set the node name\n ^ here\n"}
TASK [f5-bigip : Create app01 node in the F5] ***********************************************************************************************************************************************************************************
changed: [localhost]
TASK [f5-bigip : Print node app01 status] ***************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "The app01 node has been created "
}
TASK [f5-bigip : Collect BIG-IP LTM Nodes information] **************************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the query with replacing with app02 variable] **************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the node name] *********************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: No first item, sequence was empty.. No first item, sequence was empty.\n\nThe error appears to be in '/home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_nodes.yml': line 15, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Set the node name\n ^ here\n"}
TASK [f5-bigip : Create app02 node in the F5] ***********************************************************************************************************************************************************************************
changed: [localhost]
TASK [f5-bigip : Print node app02 status] ***************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "The app02 node has been created "
}
TASK [f5-bigip : Collect BIG-IP LTM Nodes information] **************************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the query with replacing with app03 variable] **************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the node name] *********************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: No first item, sequence was empty.. No first item, sequence was empty.\n\nThe error appears to be in '/home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_nodes.yml': line 15, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Set the node name\n ^ here\n"}
TASK [f5-bigip : Create app03 node in the F5] ***********************************************************************************************************************************************************************************
changed: [localhost]
TASK [f5-bigip : Print node app03 status] ***************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "The app03 node has been created "
}
TASK [f5-bigip : Collect BIG-IP LTM Pool information] ***************************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the query with replacing with test-pool variable] **********************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the pool name] *********************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: No first item, sequence was empty.. No first item, sequence was empty.\n\nThe error appears to be in '/home/dhananjak/ansible_projects/roles/f5-bigip/tasks/create_pool_list.yml': line 15, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Set the pool name\n ^ here\n"}
TASK [f5-bigip : Create a test-pool pool in F5] *********************************************************************************************************************************************************************************
changed: [localhost]
TASK [f5-bigip : Print test-pool pool status] ***********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "The test-pool pool has been created"
}
TASK [f5-bigip : Add members to the Pool] ***************************************************************************************************************************************************************************************
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_members_to_pool.yml for localhost => (item={'host': '10.1.1.1', 'name': 'app01'})
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_members_to_pool.yml for localhost => (item={'host': '10.1.1.2', 'name': 'app02'})
included: /home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_members_to_pool.yml for localhost => (item={'host': '10.1.1.5', 'name': 'app03'})
TASK [f5-bigip : Add app01 members to the test-pool pool] ***********************************************************************************************************************************************************************
changed: [localhost]
TASK [f5-bigip : Add app02 members to the test-pool pool] ***********************************************************************************************************************************************************************
changed: [localhost]
TASK [f5-bigip : Add app03 members to the test-pool pool] ***********************************************************************************************************************************************************************
changed: [localhost]
TASK [f5-bigip : Collect BIG-IP LTM Virtual Servers information] ****************************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the query with replacing with test_vip variable] ***********************************************************************************************************************************************************
ok: [localhost]
TASK [f5-bigip : Set the vip name] **********************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: No first item, sequence was empty.. No first item, sequence was empty.\n\nThe error appears to be in '/home/dhananjak/ansible_projects/roles/f5-bigip/tasks/add_virtual_server.yml': line 15, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Set the vip name\n ^ here\n"}
TASK [f5-bigip : Create the test_vip vip in F5] *********************************************************************************************************************************************************************************
changed: [localhost]
TASK [f5-bigip : Print the test_vip status] *************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "The test_vip virtual server has been created"
}
PLAY RECAP **********************************************************************************************************************************************************************************************************************
localhost : ok=35 changed=8 unreachable=0 failed=0 skipped=0 rescued=6 ignored=0
[dhananjak@ansible-core ansible_projects]$
F5 Big-IP Web Interface
Nodes
Pool
Virtual Server
Pool Members
Network Map
Conclusion
In today’s rapidly evolving networking landscape, automation is no longer a luxury but a necessity. By leveraging Ansible for F5 BIG-IP automation, network administrators can unlock the full potential of their infrastructure. From reducing manual effort to ensuring consistency and scalability, this powerful combination empowers technical professionals to focus on strategic initiatives rather than mundane operational tasks. Embrace F5 BIG-IP automation with Ansible and witness the transformation of your network infrastructure
Github Repo
https://github.com/DhananjaK/f5_bigip_automation_with_ansible
Reference
-
https://clouddocs.f5.com/training/fas-ansible-use-cases/Inventory/Instructor_Inventory_Ansible.html
-
https://clouddocs.f5.com/products/orchestration/ansible/devel/usage/getting_started.html
-
https://clouddocs.f5.com/products/orchestration/ansible/devel/f5_bigip/install_f5_bigip.html
-
https://docs.ansible.com/ansible/latest/collections/f5networks/f5_modules/index.html