Five Ways to Automate F5OS with Ansible: A Practical Guide
This article explores five methods for automating F5OS using Ansible, complete with real-world examples you can adapt to your own environment. The five methods, with working code examples, include F5OS collection modules, REST API calls, Linux shell access, F5OS CLI commands, and Bash scripts.
Understanding F5OS Architecture
Our example system is an F5OS appliance named r5900-2
.
When you connect to an F5OS device terminal as the root user, you enter a Linux Bash shell:
[root@appliance-1(r5900-2):Active] ~ # uname -r
3.10.0-1160.71.1.F5.1.1.1.el7_8.x86_64
[root@appliance-1(r5900-2):Active] ~ # cat /etc/centos-release
CentOS Linux release 7.8.2003 (Core)
[root@appliance-1(r5900-2):Active] ~ #
Root access is rarely needed to manage an F5OS appliance. Most tasks can be accomplished via the F5OS CLI.
If you 'substitute user' to the 'admin' user (via the su
command) or SSH directly as admin@r5900-2, you enter the F5OS CLI. Here you configure low-level network components such as Link Aggregation Groups and VLANs, monitor the F5OS platform, and manage BIG-IP tenants.
[root@appliance-1(r5900-2):Active] ~ # su admin
Welcome to the Management CLI
admin connected from 172.18.7.92 using ssh on r5900-2
r5900-2# show system version
system version os-version 1.8.0-16036
system version service-version 1.8.0-16036
system version product F5OS-A
The F5OS CLI is built on top of the F5OS API. With this same admin account, you can target the F5OS API to configure anything at the F5OS layer. A simple test with curl
can confirm you are ready to automate via the F5OS API:
$ curl -k -X GET \
f/data/openconfi> "https://r5900-2:8888/restconf/data/openconfig-system:system/f5-system-version:version" \
H "Content-Type:> -H "Content-Type: application/yang-data+json" \
> -H "Accept: application/yang-data+json" \
> -u admin:'YOUR_PASSWORD'
{
"f5-system-version:version": {
"os-version": "1.8.0-16036",
"service-version": "1.8.0-16036",
"product": "F5OS-A"
}
}
When you create a BIG-IP tenant, it behaves like a traditional BIG-IP instance. The tenant has its own isolated management IP address and iControl REST API endpoint. Your existing BIG-IP Ansible playbooks will continue to work with minimal changes.
However, automation workflows for BIG-IP are not compatible with F5OS. Let's explore how we can automate just about anything on F5OS using Ansible.
Method 1: F5OS Ansible Collection Modules
# Method 1: Using F5OS collection module
- name: Get F5OS system information using F5OS Ansible module
f5os_device_info:
gather_subset:
- system-info
register: f5os_facts
Mechanics: This method uses the official F5OS Ansible collection to communicate with the F5OS REST API over HTTPS. The modules handle authentication, request formatting, and response parsing.
Pros:
- Purpose-built for F5OS operations
- Input validation and error handling
- Consistent interface across F5OS versions
- Simplified syntax for complex operations
Cons:
- Limited to functionality provided by existing modules
- May need to wait for new modules as F5OS features evolve
Method 2: Direct REST API Calls
# Method 2: Using generic Ansible uri module for REST API
- name: Get F5OS version information using REST API direct call
uri:
url: "{{ api_base_url }}/data/openconfig-system:system/f5-system-version:version"
method: GET
force_basic_auth: true
user: "{{ ansible_user }}"
password: "{{ ansible_httpapi_password }}"
headers:
Content-Type: "application/yang-data+json"
Accept: "application/yang-data+json"
validate_certs: "{{ ansible_httpapi_validate_certs }}"
status_code: 200
return_content: true
register: f5os_version
Mechanics: This approach uses Ansible's uri module to make direct REST API calls to F5OS. It provides maximum flexibility but requires detailed knowledge of the F5OS API.
Pros:
- Access to all F5OS API endpoints
- No dependency on F5OS collection modules
- Useful for testing new F5OS features
Cons:
- Requires understanding of F5OS API structure
- More verbose configuration
- Need to handle response parsing manually
Method 3: Linux System Access
# Method 3: Using generic Ansible SSH for Linux system access
- name: Gather Linux system information via SSH
setup:
gather_subset:
- hardware
delegate_to: r5900-2-linux
vars:
ansible_user: root
ansible_connection: ssh
register: linux_facts
Mechanics: This method establishes an SSH connection to access the underlying Linux operating system for system-level operations and diagnostics.
Pros:
- Direct access to system resources
- Useful for troubleshooting
- Leverage existing Linux automation skills
Cons:
- Requires root access
- Risk of affecting system stability
Method 4: F5OS CLI via SSH
# Method 4: Using generic Ansible shell module for F5OS CLI
- name: Get F5OS version information via CLI
shell: |
sshpass -p "{{ f5os_cli_password }}" ssh -t -o StrictHostKeyChecking=no admin@{{ hostvars['r5900-2-cli']['ansible_host'] }} << 'EOF'
show sys version
EOF
delegate_to: localhost
register: cli_version
Mechanics: This method uses SSH to connect directly to the F5OS CLI, similar to how you interact with the device manually.
Pros:
- Familiar CLI interface
- Suitable for operational commands
- Easy to test and debug
Cons:
- Output parsing can be complex
- Less idempotent than API methods
- Requires sshpass installation
Method 5: Bash Scripts with f5sh
# Method 5: Using Ansible copy and shell modules to run f5sh commands from Bash scripts
- name: Transfer f5sh_example.sh script to F5OS device
copy:
content: |
#!/bin/bash
f5sh show sys version
dest: "/root/f5sh_example.sh"
mode: '0755'
delegate_to: r5900-2-linux
vars:
ansible_user: root
ansible_connection: ssh
register: script_transfer
- name: Execute f5sh_example.sh script on F5OS device
shell: "/root/f5sh_example.sh"
delegate_to: r5900-2-linux
vars:
ansible_user: root
ansible_connection: ssh
register: script_output
Mechanics: This method combines Linux shell access with the f5sh
command prefix introduced in F5OS 1.8.0, allowing F5OS CLI commands to be run from Bash scripts.
Pros:
- Combine shell scripting with F5OS commands
Cons:
- Requires root access
Inventory and Connection Methods
Each connection method requires specific inventory settings defined in 'hosts.yml':
API Connections
# hosts.yml
f5os:
hosts:
r5900-2-api:
ansible_host: r5900-2
Used with httpapi
connection in the playbook:
# playbook.yml
- name: F5OS Management Tasks
connection: httpapi
hosts: r5900-2-api
vars:
ansible_user: "admin"
ansible_httpapi_password: "{{ f5os_api_password }}"
ansible_network_os: "f5networks.f5os.f5os"
ansible_httpapi_use_ssl: true
ansible_httpapi_port: 8888
You may send API calls to either port 8888 or port 443. The URI path will change slightly depending on which TCP port you choose to use:
- For port 443: Initial path will be
/api
- For port 8888: Initial path will be
/restconf
F5OS also listens on port 80 and will redirect to TCP port 443. You can then configure distinct network access control policies for traffic to:
- Management interface user interface (443)
- Management interface REST API endpoint (8888)
CLI Access
# hosts.yml
f5os_cli:
hosts:
r5900-2-cli:
ansible_host: r5900-2
ansible_user: admin
ansible_connection: ssh
ansible_become: no
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
Enables CLI commands through SSH:
# playbook.yml
- name: Get F5OS version information via CLI
shell: |
sshpass -p "{{ f5os_cli_password }}" ssh -t -o StrictHostKeyChecking=no admin@{{ hostvars['r5900-2-cli']['ansible_host'] }} << 'EOF'
show sys version
EOF
delegate_to: localhost
Linux Shell Access
# hosts.yml
linux_hosts:
hosts:
r5900-2-linux:
ansible_host: r5900-2
ansible_user: root
ansible_connection: ssh
ansible_become: no
ansible_python_interpreter: /usr/bin/python3
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
Used for system information gathering and script execution:
# playbook.yml
- name: Gather Linux system information via SSH
setup:
gather_subset:
- hardware
delegate_to: r5900-2-linux
vars:
ansible_user: root
ansible_connection: ssh
Notes on hosts.yml
Inventory Entries
The inventory defines three connections to the same device:
r5900-2-api
: HTTPS API accessr5900-2-cli
: SSH CLI access with admin userr5900-2-linux
: SSH root access
SSH configuration:
StrictHostKeyChecking=no
: Skips host key verificationansible_become: no
: No privilege escalation- Custom Python path for Linux shell access
Run playbooks with --ask-pass
to prompt for the root password when accessing the Linux shell:
ansible-playbook -i hosts.yml playbook.yml --ask-pass
See the complete hosts.yml
and playbook.yml
examples below as templates for your own environments. The latest versions are hosted on GitHub.
# hosts.yml - Ansible inventory configuration
# This inventory file defines multiple ways to connect to the same F5OS device
all:
children:
# F5OS API group - For REST API interactions via HTTPS
# This method is used for F5OS platform configuration and monitoring via REST
# User credentials configured as vars in playbook.yml
f5os:
hosts:
r5900-2-api:
ansible_host: r5900-2
# F5OS CLI group - For F5OS CLI access via SSH
# This method is used to run F5OS CLI commands via SSH using admin account
# User credentials prompted for at runtime
f5os_cli:
hosts:
r5900-2-cli:
ansible_host: r5900-2
ansible_user: admin
ansible_connection: ssh
ansible_become: no
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
# Linux group - For direct Linux shell access via SSH
# This method is used to access the underlying Linux OS using root account
# User credentials prompted for at runtime using --ask-pass
linux_hosts:
hosts:
r5900-2-linux:
ansible_host: r5900-2
ansible_user: root
ansible_connection: ssh
ansible_become: no
ansible_python_interpreter: /usr/bin/python3
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
# playbook.yml
#
# This playbook demonstrates multiple methods to interact with F5OS devices using Ansible:
# 1. Using F5OS-specific Ansible modules from the F5 Ansible collection
# - Connects via HTTPS to F5OS REST API
# - Uses F5OS maintained collection modules
# - Best for F5OS platform operations with verified modules
#
# 2. Using Ansible uri module to make direct REST API calls
# - Connects via HTTPS to F5OS REST API
# - Uses Ansible built-in uri module
# - Best for custom REST API operations or when collection modules unavailable
#
# 3. Using SSH connection to access the underlying Linux OS
# - Connects via SSH as root to Linux shell
# - Uses Ansible built-in setup modules
# - Best for system-level operations and diagnostics
#
# 4. Using SSH to connect as admin and run F5OS CLI commands
# - Connects via SSH as admin to F5OS CLI
# - Uses Ansible built-in shell module with sshpass
# - Best for running operational commands and gathering CLI output
#
# 5. Using SSH connection to access the underlying Linux OS
# - Connects via SSH as root to Linux shell
# - Uses Ansible built-in SSH modules
# - Uses Ansible built-in copy module to transfer Bash script
# - Uses Ansible built-in shell module to execute script and display output
# - Best for automated script-based management via f5sh commands
#
# Requirements:
# - sshpass must be installed on the Ansible control node for F5OS CLI access
# Ubuntu/Debian: sudo apt-get install sshpass
# RHEL/CentOS: sudo yum install sshpass
#
# References:
# - F5OS Collection: https://clouddocs.f5.com/products/orchestration/ansible/devel/f5os/F5OS-index.html
# - Ansible URI Module: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html
# - F5OS-A/F5 rSeries - API: https://clouddocs.f5.com/api/rseries-api/rseries-api-index.html
# - Ansible SSH Connection: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ssh_connection.html
# - F5OS-A/F5 rSeries - CLI: https://clouddocs.f5.com/api/rseries-api/rseries-cli-index.html
#
# Debug Notes:
# - Each connection method has debug lines (commented out) that can be enabled
# - Uncomment Debug Info lines when troubleshooting connection issues
# - Use -vvvv verbosity for additional connection debugging
# - CLI task can be modified to show stderr/stdout with no_log: false
---
- name: F5OS Management Tasks
connection: httpapi
hosts: r5900-2-api
gather_facts: false
collections:
- f5networks.f5os
any_errors_fatal: true
vars_prompt:
- name: f5os_api_password
prompt: "ENTER F5OS REST API password (admin user):"
private: yes
- name: f5os_cli_password
prompt: "ENTER F5OS CLI SSH password (admin user):"
private: yes
vars:
ansible_user: "admin"
ansible_httpapi_password: "{{ f5os_api_password }}"
ansible_network_os: "f5networks.f5os.f5os"
ansible_httpapi_use_ssl: true
ansible_httpapi_use_proxy: false
ansible_httpapi_validate_certs: "no"
ansible_httpapi_port: 8888
ansible_command_timeout: 1800
persistent_log_messages: true
api_base_url: "https://{{ ansible_host }}:{{ ansible_httpapi_port }}/restconf"
tasks:
# Method 1: Using F5OS maintained collection module
- name: Get F5OS system information using F5OS Ansible module
f5os_device_info:
gather_subset:
- system-info
register: f5os_facts
# Method 2: Using generic Ansible uri module for REST API
- name: Get F5OS version information using REST API direct call
uri:
url: "{{ api_base_url }}/data/openconfig-system:system/f5-system-version:version"
method: GET
force_basic_auth: true
user: "{{ ansible_user }}"
password: "{{ ansible_httpapi_password }}"
headers:
Content-Type: "application/yang-data+json"
Accept: "application/yang-data+json"
validate_certs: "{{ ansible_httpapi_validate_certs }}"
status_code: 200
return_content: true
register: f5os_version
# Method 3: Using generic Ansible SSH for Linux system access
- name: Gather Linux system information via SSH connection
setup:
gather_subset:
- hardware
delegate_to: r5900-2-linux
vars:
ansible_user: root
ansible_connection: ssh
register: linux_facts
# Method 4: Using generic Ansible shell module for F5OS CLI
- name: Get F5OS version information via CLI
shell: |
sshpass -p "{{ f5os_cli_password }}" ssh -t -o StrictHostKeyChecking=no admin@{{ hostvars['r5900-2-cli']['ansible_host'] }} << 'EOF'
show sys version
EOF
delegate_to: localhost
register: cli_version
changed_when: false
# no_log: false # Uncomment to show CLI output for debugging
# failed_when: false # Uncomment to prevent CLI failures for debugging
# Method 5: Using generic Ansible SSH connection to access the underlying Linux OS
- name: Transfer f5sh_example.sh script to F5OS device
copy:
content: |
#!/bin/bash
f5sh show sys version
dest: "/root/f5sh_example.sh"
mode: '0755'
delegate_to: r5900-2-linux
vars:
ansible_user: root
ansible_connection: ssh
register: script_transfer
- name: Execute f5sh_example.sh script on F5OS device
shell: "/root/f5sh_example.sh"
delegate_to: r5900-2-linux
vars:
ansible_user: root
ansible_connection: ssh
register: script_output
# Display combined information from all five methods
- name: Show combined information from all sources
vars:
system_info:
"F5OS Version Info (via F5 maintained F5OS Ansible module collection, communicating over HTTPS REST API, using: admin account)":
"Platform Type": "{{ f5os_facts.system_info.platform_type }}"
"Software Version": "{{ f5os_facts.system_info.running_software.os_version }}"
# "Debug Info": "{{ f5os_facts | default('f5os_facts not defined') }}"
"F5OS Version Info (via built-in Ansible uri module, communicating over HTTPS REST API, using: admin account)":
"OS Version": "{{ f5os_version.json['f5-system-version:version']['os-version'] }}"
"Product": "{{ f5os_version.json['f5-system-version:version']['product'] }}"
"Service Version": "{{ f5os_version.json['f5-system-version:version']['service-version'] }}"
# "Debug Info": "{{ f5os_version | default('f5os_version not defined') }}"
"Linux System Info (via built-in Ansible ssh module, communicating over SSH, using: root account and bash shell)":
"Distribution": "{{ linux_facts.ansible_facts.ansible_distribution }}"
"Kernel Version": "{{ linux_facts.ansible_facts.ansible_kernel }}"
"Python Version": "{{ linux_facts.ansible_facts.ansible_python_version }}"
"Total Memory": "{{ linux_facts.ansible_facts.ansible_memtotal_mb }} MB"
# "Debug Info": "{{ linux_facts | default('linux_facts not defined') }}"
"F5OS Version Info (via built-in Ansible ssh module, communicating over SSH, using: admin account and f5_confd_cli shell)":
"OS Version": "{{ cli_version.stdout_lines[0].split()[-1] if cli_version is defined and cli_version.rc == 0 else 'Error getting CLI version' }}"
"Service Version": "{{ cli_version.stdout_lines[1].split()[-1] if cli_version is defined and cli_version.rc == 0 else 'Error getting CLI version' }}"
"Product": "{{ cli_version.stdout_lines[2].split()[-1] if cli_version is defined and cli_version.rc == 0 else 'Error getting CLI version' }}"
# "CLI Return Code": "{{ cli_version.rc | default('rc not defined') }}"
# "CLI STDOUT": "{{ cli_version.stdout_lines | default('stdout not defined') }}"
# "CLI STDERR": "{{ cli_version.stderr_lines | default('stderr not defined') }}"
# "Debug Info": "{{ cli_version | default('cli_version not defined') }}"
"F5OS Version Info (via built-in Ansible script copy and execution modules, using: root account, bash shell, and f5sh commands)":
"OS Version": "{{ script_output.stdout_lines[0].split()[-1] if script_output is defined and script_output.rc == 0 else 'Error executing script' }}"
"Product": "{{ script_output.stdout_lines[2].split()[-1] if script_output is defined and script_output.rc == 0 else 'Error executing script' }}"
"Service Version": "{{ script_output.stdout_lines[1].split()[-1] if script_output is defined and script_output.rc == 0 else 'Error executing script' }}"
# "Script Return Code": "{{ script_output.rc | default('rc not defined') }}"
# "Script STDOUT": "{{ script_output.stdout_lines | default('stdout not defined') }}"
# "Script STDERR": "{{ script_output.stderr_lines | default('stderr not defined') }}"
# "Debug Info": "{{ script_output | default('script_output not defined') }}"
debug:
var: system_info
Terminal Demo
ansible-playbook -i hosts.yml playbook.yml --ask-pass
Notes on Software Version Compatibility
If using the F5-maintained F5OS collection, you'll need a compatible version of Ansible:
https://github.com/F5Networks/f5-ansible-f5os
If using an alternate method that depends on compatibility with the Python3 version on the remote host, confirm the Python3 version on your F5OS appliance:
[root@appliance-1(r5900-2):Active] ~ # python3 --version
Python 3.6.8
...against the ansible-core support matrix.
Responsible Authentication
This tutorial focused on simple examples using default local accounts (root, admin) and local authentication.
In production, authentication is more complex, requiring:
- SSH key-based authentication instead of passwords
- API token authentication instead of basic auth
- Role-based access control with dedicated automation accounts
- Secure credential management (e.g. Ansible Vault)
- Regular credential rotation and audit logging
- Remote authentication (LDAP, TACACS+, RADIUS)
- Upgrading from self-signed and invalid certificates to Certificate Authority signed and validated certificates