ansible
103 TopicsAnsible modules for F5OS
Hello everyone, Any ansible modules available to configure F5OS hypervisor?, i am looking to automate the baseline configuration when we have a new F5OS based box, when i say baseline configuration it includes setting the hostnames, login banner, dns server, ntp, snmp, creating new tenants etc... are they any plans from F5 to release ansible modules for F5OS like what F5 team releases modules for BIG-IP??. if there are no plans what would be the general recommendation to go with either ansible shell module (or) ansible expect module to configure the F5OS box?? thank you, kumar73Views0likes1CommentBigIP UCS Backup script; looking for some guidance on design
Greetings, I've began to work on a bash script, intended to be ran locally on each F5 appliance via a cron task. The criteria for this script has been, Saves the UCS /w encryption using {Hostname}-YYYY-MM-DD.ucs naming format. Uploads the generated UCS file to a SFTP server SFTP native commands are a MUST, SCP will not work due to it's reliance on command shell/login. Rollover after X # of saved files in order to prevent storage exhaustion on the target SFTP Server I strongly doubt any form of deduplication will work with a encrypted UCS Sends an email notification if the backup failed I've so far written a script that addresses the first 3 criteria and have been waiting for those to go through their paces in testing before adding in notification logic. The commands and logic being used have gotten more complex, the further I've gotten into the script's development. This has lead to some concerns about whether this is the best approach given the nature of the F5 BigIP systems being a vendor appliance and worry that there's a large possibility commands may stop working correctly after a major x. version update, requiring an overhaul of a fairly complex script. I'm almost wondering if setting up an AWX/Tower host in our environment and then using the f5networks Ansible Module for the majority of the heavy lifting followed by some basic logic for file rotation, would be a better long term approach. Ansible would also be a bit more flexible in that I wouldn't have to hardcore values that diverge between individual hosts into the script itself. It's however not clear if the F5networks ansible module supports SFTP as I only see SCP referenced. https://my.f5.com/manage/s/article/K35454259 Advice and insight is much appreciated! #!/bin/bash # F5 backup script based on https://my.f5.com/manage/s/article/K000138297 # User-configurable Variables UCS_DIR="/var/ucs" REMOTE_USER="svc_f5backup" REMOTE_HOST="myhost.contoso.local" REMOTE_DIR="/data/f5/dev" SSH_KEY="/shared/scripts/f5-backup/mykeys/f5user" ENCRYPTION_PASSPHRASE='' # Blank out the value to not encrypt the UCS backup. LOG_FILE="/var/log/backupscript.log" MAX_FILES=45 # Maximum number of backup files to keep # Dynamic Variables (do not edit) HOSTNAME=$(/bin/hostname) DATE=$(date +%Y-%m-%d) UCS_FILE="${UCS_DIR}/${HOSTNAME}-${DATE}.ucs" # Start logging echo "$(date +'%Y-%m-%d %H:%M:%S') - Starting backup script." >> ${LOG_FILE} # Save the UCS backup file if [ -n "${ENCRYPTION_PASSPHRASE}" ]; then echo "Running the UCS save operation (encrypted)." >> ${LOG_FILE} tmsh save /sys ucs ${UCS_FILE} passphrase "${ENCRYPTION_PASSPHRASE}" >> ${LOG_FILE} 2>&1 else echo "Running the UCS save operation (not encrypted)." >> ${LOG_FILE} tmsh save /sys ucs ${UCS_FILE} >> ${LOG_FILE} 2>&1 fi # Create a temporary batch file for SFTP commands BATCH_FILE=$(mktemp) echo "cd ${REMOTE_DIR}" > $BATCH_FILE echo "put ${UCS_FILE}" >> $BATCH_FILE echo "bye" >> $BATCH_FILE # Log that the transfer is starting echo "Starting SFTP transfer." >> ${LOG_FILE} # Execute SFTP command and capture the output transfer_command_output=$(sftp -b "$BATCH_FILE" -i "${SSH_KEY}" -oBatchMode=no "${REMOTE_USER}@${REMOTE_HOST}" 2>&1) transfer_status=$? # Extract the "Transferred:" line transfer_summary=$(echo "$transfer_command_output" | grep "^Transferred: sent") if [ $transfer_status -eq 0 ]; then if [ -n "$transfer_summary" ]; then echo "UCS file copied to the SFTP server successfully (remote:${REMOTE_HOST}:${REMOTE_DIR}/${UCS_FILE}). $transfer_summary" >> ${LOG_FILE} else echo "UCS file copied to the SFTP server successfully (remote:${REMOTE_HOST}:${REMOTE_DIR}/${UCS_FILE}). Please check the log for details." >> ${LOG_FILE} fi else echo "$transfer_command_output" >> ${LOG_FILE} echo "UCS SFTP copy operation failed. Please read the log for details." >> ${LOG_FILE} rm -f $BATCH_FILE exit 1 fi # Clean up the temporary batch file rm -f $BATCH_FILE # Rollover backup files if the number exceeds MAX_FILES echo "Checking and maintaining the maximum number of backup files." >> ${LOG_FILE} # Create a list of files to delete sftp -i "${SSH_KEY}" -oBatchMode=no "${REMOTE_USER}@${REMOTE_HOST}" <<EOF > file_list.txt cd ${REMOTE_DIR} ls -1 ${HOSTNAME}-*.ucs bye EOF # Filter out unwanted lines and sort the files alphanumerically grep -v 'sftp>' file_list.txt | grep -v '^cd ' | sort > filtered_file_list.txt # Determine files to delete files_to_delete=$(head -n -${MAX_FILES} filtered_file_list.txt) if [ -n "$files_to_delete" ]; then # Create a temporary batch file for SFTP cleanup commands CLEANUP_BATCH_FILE=$(mktemp) echo "cd ${REMOTE_DIR}" > $CLEANUP_BATCH_FILE for file in $files_to_delete; do echo "Deleting $file" >> ${LOG_FILE} echo "rm $file" >> $CLEANUP_BATCH_FILE done echo "bye" >> $CLEANUP_BATCH_FILE # Execute SFTP cleanup command and log the output cleanup_command_output=$(sftp -b "$CLEANUP_BATCH_FILE" -i "${SSH_KEY}" -oBatchMode=no "${REMOTE_USER}@${REMOTE_HOST}" 2>&1) echo "$cleanup_command_output" >> ${LOG_FILE} # Clean up the temporary batch file rm -f $CLEANUP_BATCH_FILE else echo "No files to delete. Total files within limit." >> ${LOG_FILE} fi # Clean up the file lists rm -f file_list.txt filtered_file_list.txt # Delete the local copy of the UCS archive tmsh delete /sys ucs ${UCS_FILE} >> ${LOG_FILE} 2>&1 echo "$(date +'%Y-%m-%d %H:%M:%S') - Backup script completed." >> ${LOG_FILE}128Views0likes2CommentsAnsible F5 imperative collection works with proxies, declarative collection doesn't
I have a case where most of the F5s in our environment are accessible from our Ansible Tower environment controllers without going through an internal proxy. However, a few F5s can only be accessed via an http/s forwarding proxy. I have gotten F5's imperative (f5_modules) and declarative (f5_bigip) Ansible collections to work fine without using a proxy as described at: https://clouddocs.f5.com/products/orchestration/ansible/devel/ However, when using a proxy (by specifying them using the ansible 'environment' parameter), I've only been able to get the imperative collection to work. I've had no success getting the declarative collection to work through a proxy, which is a bit ironic as the httpapi connection type indicates that 'use_proxy' is true by default. I've done testing on a test VM that I created with Ansible installed and have had the same results as when using Ansible Tower. Some details of the test configuration: inventory file: [f5_cluster] cnb-ilb01-t001 ansible_host=10.9.254.23 cnb-ilb01-t002 ansible_host=10.9.254.24 vars.yml file: --- ansible_ssh_user: "root" ansible_ssh_pass: "{{ ansible_ssh_pass_vault }}" proxy_env: http_proxy: http://10.139.25.13:3128 https_proxy: http://10.139.25.13:3128 playbook (excerpt, showing only the first task): - hosts: "{{ f5_cluster }}" environment: "{{ proxy_env | default({}) }}" collections: f5networks.f5_bigip connection: httpapi vars: ansible_server: "{{ ansible_host }}" ansible_user: "{{ ansible_ssh_user }}" ansible_network_os: f5networks.f5_bigip.bigip ansible_httpapi_password: "{{ ansible_ssh_pass }}" ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_use_proxy: true ansible_httpapi_validate_certs: false tasks: - name: get failover state bigip_device_info: gather_subset: - devices register: f5_device_info I've enabled debug logging: export ANSIBLE_LOG_PATH=~/ansible.log export ANSIBLE_DEBUG=True and used '-vvvv' when running ansible-playbook and can see the ansible 'httpapi' and F5 'f5_bigip' collections being used. However, it appears that proxy environment variables are loaded (during the implicit 'gather_facts') after the connection has been established: <10.9.254.23> EXEC /bin/sh -c 'http_proxy=http://10.139.25.13:3128 https_proxy=http://10.139.25.13:3128 /usr/bin/python3 /home/osboxes/.ansible/tmp/ansible-local-67825q3anan16/ansible-tmp-1716235016.7934482-67834-4566673767345/AnsiballZ_setup.py && sleep 0' And when the 'get failover state' task is run, it appears to be using the session established during gather_facts: <10.9.254.23> found existing local domain socket, using it! and hence not still not using any proxy environment values. I ran a tcpdump on the proxy host itself and no traffic is ever sent to the proxy from the playbook. I have tried setting 'gather_facts: false', but then I see no debug output even mentioning using a proxy. I'm not sure if I'm doing something wrong (quite possible), if it's an f5_bigip collection issue, or something with Ansible. Has anyone had any success getting declarative (f5_bigip) collection to work using a proxy? I've tried everything I can think of, but no luck.39Views0likes0CommentsError while running ansible
I am getting the following error when I am trying to run ansible script on f5 instance through jumphost The full traceback is: Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 1344, in do_open h.request(req.get_method(), req.selector, req.data, headers, File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/http/client.py", line 1336, in request self._send_request(method, url, body, headers, encode_chunked) File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/http/client.py", line 1382, in _send_request self.endheaders(body, encode_chunked=encode_chunked) File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/http/client.py", line 1331, in endheaders self._send_output(message_body, encode_chunked=encode_chunked) File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/http/client.py", line 1091, in _send_output self.send(msg) File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/http/client.py", line 1035, in send self.connect() File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/http/client.py", line 1477, in connect self.sock = self._context.wrap_socket(self.sock, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/ssl.py", line 455, in wrap_socket return self.sslsocket_class._create( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/ssl.py", line 1042, in _create self.do_handshake() File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/ssl.py", line 1320, in do_handshake self._sslobj.do_handshake() ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1000) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/Users/pranaychowd.pinapaka/.ansible/tmp/ansible-tmp-1714109490.0885582-73185-193526554178977/AnsiballZ_bigip_command.py", line 107, in <module> _ansiballz_main() File "/Users/pranaychowd.pinapaka/.ansible/tmp/ansible-tmp-1714109490.0885582-73185-193526554178977/AnsiballZ_bigip_command.py", line 99, in _ansiballz_main invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS) File "/Users/pranaychowd.pinapaka/.ansible/tmp/ansible-tmp-1714109490.0885582-73185-193526554178977/AnsiballZ_bigip_command.py", line 47, in invoke_module runpy.run_module(mod_name='ansible_collections.f5networks.f5_modules.plugins.modules.bigip_command', init_globals=dict(_module_fqn='ansible_collections.f5networks.f5_modules.plugins.modules.bigip_command', _modlib_path=modlib_path), File "<frozen runpy>", line 226, in run_module File "<frozen runpy>", line 98, in _run_module_code File "<frozen runpy>", line 88, in _run_code File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/modules/bigip_command.py", line 757, in <module> File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/modules/bigip_command.py", line 750, in main File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/modules/bigip_command.py", line 680, in exec_module File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/modules/bigip_command.py", line 631, in exec_module File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/module_utils/icontrol.py", line 551, in tmos_version File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/module_utils/bigip.py", line 31, in api File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/module_utils/bigip.py", line 52, in connect_via_token_auth File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/module_utils/icontrol.py", line 239, in post File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/module_utils/icontrol.py", line 194, in send File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible/module_utils/urls.py", line 1578, in open File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 215, in urlopen return opener.open(url, data, timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 515, in open response = self._open(req, data) ^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 532, in _open result = self._call_chain(self.handle_open, protocol, protocol + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 492, in _call_chain result = func(*args) ^^^^^^^^^^^ File "/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible/module_utils/urls.py", line 605, in https_open File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py", line 1347, in do_open raise URLError(err) urllib.error.URLError: <urlopen error [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1000)> fatal: [hostip ]: FAILED! => { "changed": false, "module_stderr": "Traceback (most recent call last):\n File \"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py\", line 1344, in do_open\n h.request(req.get_method(), req.selector, req.data, headers,\n File \"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/http/client.py\", line 1336, in request\n .. . . . payload.zip/ansible_collections/f5networks/f5_modules/plugins/module_utils/bigip.py\", line 52, in connect_via_token_auth\n File \"/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/module_utils/icontrol.py\", line 239, in post\n File \"/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible_collections/f5networks/f5_modules/plugins/module_utils/icontrol.py\", line 194, in send\n File \"/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible/module_utils/urls.py\", line 1578, in open\n File \"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py\", line 215, in urlopen\n return opener.open(url, data, timeout)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py\", line 515, in open\n response = self._open(req, data)\n ^^^^^^^^^^^^^^^^^^^^^\n File \"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py\", line 532, in _open\n result = self._call_chain(self.handle_open, protocol, protocol +\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py\", line 492, in _call_chain\n result = func(*args)\n ^^^^^^^^^^^\n File \"/var/folders/4c/dnty3w814gxd01c5lq6910nr0000gn/T/ansible_bigip_command_payload_rjjis8dv/ansible_bigip_command_payload.zip/ansible/module_utils/urls.py\", line 605, in https_open\n File \"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/urllib/request.py\", line 1347, in do_open\n raise URLError(err)\nurllib.error.URLError: <urlopen error [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1000)>\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1 }193Views0likes4CommentsRundeck ansible F5 errors
We use rundeck to deploy some code and within that code we take advantage of the ansible to remove hosts in and out of the respective pool in the F5. Recently we upgraded to a new version of rundeck and the latest version of ansible. I've seen other posts where someone took out the delegate_to: line and that fixed it. I can do that or install a legacy version of ansible. Here is debug output from the failed task: TASK [f5_modify : Disable from pool -Test-API-8080] ********* fatal: [hostname.example.com -> localhost]: FAILED! => {"changed": false, "msg": "argument 'server_port' is of type <class 'NoneType'> found in 'provider'. and we were unable to convert to int: <class 'NoneType'> cannot be converted to an int"} PLAY RECAP ********************************************************************* hostname.example.com : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 ansible code: --- - name: "Disable from pool {{ pool_name }}" bigip_pool_member: provider: server: "{{ f5_ipaddress }}" user: "{{ f5_user }}" password: "{{ f5_pwd }}" validate_certs: "no" transport: "rest" state: forced_offline pool: "{{ pool_name }}" partition: "Common" host: "{{ansible_default_ipv4.address}}" port: "{{ pool_member_port }}" delegate_to: localhost when: action == "disable" tags: f5_manage # Enable pool member again if the deploy type is rolling, or the env is not prod - name: "Enable in pool {{ pool_name }}" bigip_pool_member: provider: server: "{{ f5_ipaddress }}" user: "{{ f5_user }}" password: "{{ f5_pwd }}" validate_certs: "no" transport: "rest" state: enabled pool: "{{ pool_name }}" partition: "Common" host: "{{ansible_default_ipv4.address}}" port: "{{ pool_member_port }}" delegate_to: localhost when: action == "enable" tags: f5_manage - name: Wait for clients to gracefully bleed off the server wait_for: host: "{{ansible_default_ipv4.address}}" port: "{{ pool_member_port }}" delay: 5 timeout: 120 state: drained ignore_errors: True when: - action == "disable" - deploy_type == "rolling" tags: f5_manage102Views0likes1CommentError when running bigip_command Playbook against LTM : Syntax Error: unexpected argument /bin/sh\n
I am running a Playbook to test bigip_command using ansible [core 2.15.3] against LTM running 15.1.6.1. When I run the playbook I get the following message: UNREACHABLE! => { “changed”: false, “msg”: “Failed to create temporary directory. In some cases, you may have been able to authenticate and did not have permissions on the target directory. Consider changing the remote tmp path in ansible.cfg to a path rooted in "/tmp", for more error information use -vvv. Failed command was: ( umask 77 && mkdir -p "echo $HOME/tmp"&& mkdir "echo $HOME/tmp/ansible-tmp-1709037153.0021555-3167181-73985055078518" && echo ansible-tmp-1709037153.0021555-3167181-73985055078518="echo $HOME/tmp/ansible-tmp-1709037153.0021555-3167181-73985055078518" ), exited with result 1”, “unreachable”: true } When I run the Playbook with -vvv flags I see the following line just before the error message pasted above: Failed to connect to the host via ssh: Syntax Error: unexpected argument “/bin/sh” Similarly, if I test the ssh I also see this same unexpected argument message: ansible all -m shell -a id -vvv Syntax Error: unexpected argument “/bin/sh”\n’ I have tried editing various things in hosts file and ansible.cfg but nothing I have tried seems to have made a difference. Is anyone familiar with this and what is needed to resolve this. Any help is greatly appreciated!277Views1like4CommentsAnsible very slow
I’m trying to make e playbook to create pools and virtual servers but it take a while, below an example, any solution? --- - name: Create local traffic objects on a BIG-IP hosts: F5 gather_facts: False connection: local vars: provider: password: "{{ ansible_password }}" server: "{{ ansible_host }}" user: "{{ ansible_user }}" validate_certs: False pools: - ["POOL_1", "INTERNET", "MEM_1"] - ["POOL_2", "INTERNET", "MEM_2"] - ["POOL_3", "WAN", "MEM_3"] tasks: - name: Create pools bigip_pool: state: present name: "{{ item[0] }}" partition: "{{ item[1] }}" provider: "{{ provider }}" #delegate_to: localhost loop: "{{ pools }}" - name: Add pool members bigip_pool_member: name: "{{ item[0] }}" partition: "{{ item[1] }}" host: "{{ item[2] }}" port: "80" provider: "{{ provider }}" reuse_nodes: false delegate_to: localhost loop: "{{ pools }}"63Views0likes0CommentsInfrastructure as Code: Automating F5 Distributed Cloud CEs with Ansible
Introduction Welcome to the first installment of our Infrastructure as Code (IaC) series, focusing on F5 products and Ansible. This series has been a long-standing desire of mine to showcase the ability of IaC utilizing Ansible Automation Platform to deliver Day 0 through Day 2 operations with multiple F5 virtualized platforms. Over time, I've encountered numerous financial clients expressing interest in this topic. For many of these clients, the prospect of leveraging IaC to redeploy an environment outweighs the traditional approach of performing upgrades. This series will hopefully provide insight, documentation, and code for anyone embarking on this journey. Why Ansible Automation Platform? Like most people, I started my journey with community editions of Ansible. As my coding became more complex, so did the need to ensure that my lab infrastructure adhered to the best security guidelines required by my company (my goal being to mimic how customers would/should do things in real life). I began utilizing Ansible Automation Platform to ensure my credentials were protected, as well as to organize and share my code with the rest of my team (following the 'just in case you got hit by a bus' theory). Ansible Automation Platform utilizes execution environments (EE) to ensure code runs efficiently and cleanly every time. Now, I am also creating Execution Environments via GitHub with workflows and pushing them up to Quay.io (https://github.com/VDI-Tech-Guy/f5-execution-engines). Huge thanks to Colin McNaughton at Red Hat for making my life so much easier with building EEs! Why deploy F5 Distributed Cloud on VMware vSphere? As I mentioned before, I had this desire to build this Infrastructure as Code (IaC) code a while back. This was prior to the Broadcom acquisition of VMware. Being an ex-VMware employee, I had a lot of knowledge of virtualization platform infrastructure going into this project, and I started my focus on deploying on VMware vSphere. F5 Distributed Cloud can be deployed in any cloud, anywhere. However, I really wanted to focus on on-premises deployments because not every customer can afford the cloud. Moreover, there's always a back-and-forth battle between on-premises and the cloud, which has evolved into the Hybrid Cloud and the Multi-Cloud. I do intend to extend this series to the Multi-Cloud, but these initial deployments will be focused on VMware vSphere, as it is still utilized in many organizations across the globe. Information about the Setup in the Demo Video If you watch the video (down below) on how the deployment works, you can see i did a bunch of the pre-work prior to launching the deployment, in the git repostory (link in Resources). Here are some Prework items i did Had a fully functional Ansible Automation Platform 2.4+ enviornment setup and working. (at the time the controller version was 4.4.4) Execution Environment was imported into Ansible Automation Platform Controller The Project was setup to import the Playbooks from the Git Repository (In Resources Section below) and setup the Default Execution Environment Demo Inventory was setup (in our usecase we only needed the vCenter Host) We Setup Network Credentials for the vCenter The Template was setup and had Variables populated in it (Note the API Key was hidden). As mentioned in the Video (Below) The variables were populated to my environment, this contains all the information, i have provided a Demo Example in the git repository for anyone to mimic my settings to their environment, also the example has comments about each field or area of a field and the purpose of the variable. { "rhel_location": "https://vesio.blob.core.windows.net/releases/rhel/9/x86_64/images/vmware/rhel-9.2023.29-20231212012955-single-nic.ova", "xc_api_credential": "_____________________________________", "xc_namespace": "mmabis-automation", "xc_console_host": "f5-bd", "xc_user": "admin", "xc_pass": "Ansible123!", "vcenter_hostname": "{{ ansible_host }}", "vcenter_username": "{{ ansible_env.ANSIBLE_NET_USERNAME }}", "vcenter_password": "{{ ansible_env.ANSIBLE_NET_PASSWORD }}", "vcenter_validate_certs": false, "datacenter_name": "Apex", "cluster_name": "Worlds-Edge", "datastore": "TrueNAS-SSD", "dvs_switch_name": "DSC-DVS", "dns_name_servers": [ "192.168.192.20", "192.168.192.1" ], "dns_name_search": [ "dsc-services.local", "localdomain" ], "ntp_servers": [ "0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org" ], "domain_fqdn": "dsc-services.local", "DVS_Name": "{{dvs_switch_name}}", "Internal_Network": "DVS-Server-vLan", "External_Network": "DVS-DMZ-vLan", "resource_pool_name": "Lab-XC", "waiting_period": 2, "temp_download_location": "/tmp/xc-ova-download.ova", "xc_ova_builds": [ { "hostname": "xc-automation-rhel-demo", "tmpl_name": "xc-automation-rhel-demo", "admin_password": "Ansible123!", "cluster_name": "xc-automation-cluster-rhel-demo", "dhcp": "no", "external_ip": "172.16.192.170", "external_ip_subnet_prefix": "24", "external_ip_gw": "172.16.192.1", "external_ip_route": "0.0.0.0/0", "internal_ip": "192.168.192.170", "internal_ip_subnet_prefix": "22", "internal_ip_gw": "192.168.192.1", "certified_hw": "vmware-regular-nic-voltmesh", "latitude": "39.51833126", "longitude": "-104.759496962", "build_count": 3, "nic_config": "rhel-multi" } ] } Launching the Code With all of that prework Handled it was as easy as launch the code, there were a few caviats i learned over time when dealing with the atuomation that i wanted to share. Never re-use a cluster name in F5 Distributed Cloud, especially if it was used in a different version of the CE (there were communications issues with the CEs and previous cluster information that was stored in F5 Distributred Cloud Console) The Api Credentials are system level when trying to accept registration or create the token for importing in to the environment. This code is designed to check for "{{ xc-namespace}}-token" if it exists then it will utilize the existing token, if not it will try to create it so you need system level permissions to do this. Build Count should be 3 by default (still needs to be defined) or an ODD number based on recomendations i have heard from our F5 Field. If there are more that i think of ill definatly edit the post and make sure its up-to-date. When launching the code i was able to get the lab to build up correctly multiple times, so please if there is an issue or something i might not have documented well, feel free to let me know and give it a shot for yourself! YouTube Video now on DevCentral Channel Resources https://github.com/f5devcentral/f5-bd-ansible-day0-automation - The Code utilized for this deployment https://github.com/VDI-Tech-Guy/f5-execution-engines - Building Execution Environments with Github and Workflows Conclusion I do hope that this series will help everyone who wants to embrace IaC and if you have any questions feel free to reach out!502Views3likes0CommentsUser-Role for Ansible
Hello, we want to start Ansible-Integration for our BIG IP, main reason: automate Certs with LetsEncrypt. How do you handle the user/password? we're a little bit concerned about save username/password in ansible. another question: what User-Role would be needed for Certs and SSL Profiles?281Views0likes2CommentsAnsible - Bricking freshly installed vcmp guests with ansible
Hello fellow F5 admins, currently I try to establisheda workflow, where new vcmp guests are created and configured with a standard basic config (and even building a HA setup). The creation part is working, but here begin the problems: tl;dr Question: What is the properway to bootstrap a freshly installed vcmp guests(or appliance), when you are forced to change the default passwords on 1st login, without doing it by hand? The only solution I found (link below) will lock me out of the system forever. Long Version: Freshly installed systems enforce a password change for admin user on 1st access. This password change cannot be accomplished with the standard ansible module "bigip_user". If you try, you will get an error telling you, password has expired and it has to be changed. I then found an article about the security password policy and how one is supposed to change the password with ansible (https://techdocs.f5.com/en-us/bigip-14-0-0/big-ip-system-secure-password-policy/secure-password-policy-chapter-title.html) So I gave it a try and the password was changed "a" password, but not the one provided by the playbook variable. Neither GUI nor SSH or REST login will work. I am locked out. Befor you ask: yes the password in ansible-vault style is correct, because it is used to create the guest on the vcmp hosts. Here is my playbook: --- - name: Test vCMP-Guest hosts: vcmp_guests gather_facts: false vars: f5_api_admin_user: admin f5_api_admin_password: !vault | $ANSIBLE_VAULT;1.1;AES256 35613438373864653838386266616364666366363332646635303036343266646664656333643932 6462363934306365636265313038376436353032303330370a656434643837343165316333393932 66616133376433303136366664303563373034353630656531663864323433663166653539303937 3937646663613064390a663631623733376339353735633362633139383635386661376137653434 6237 bigip_provider: server: "{{ ansible_host }}" server_port: 443 user: "{{ f5_api_admin_user }}" password: "{{ f5_api_admin_password }}" validate_certs: false transport: rest tasks: - name: Set admin Password uri: url: "https://{{ ansible_host }}/mgmt/shared/authz/users/admin" method: PATCH body: '{"oldPassword":"admin","password":"{{ f5_api_admin_password }}"}' body_format: json validate_certs: false force_basic_auth: true user: admin password: admin headers: Content-Type: "application/json" register: result delegate_to: localhost - name: Debug ansible.builtin.debug: var: result - name: Try to get system info f5networks.f5_modules.bigip_device_info: gather_subset: - system-info provider: "{{ bigip_provider }}" register: output delegate_to: localhost - name: Debug ansible.builtin.debug: var: output The Output of the the passwordreset task look fine to me: TASK [Debug] ******************************************************************************************************************************************************************************** task path: ~/guest-playbook.yml:47 ok: [test-guest] => { "result": { "cache_control": "no-store, no-cache, must-revalidate", "changed": false, "connection": "close", "content_length": "330", "content_security_policy": "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; img-src 'self' data: http://127.4.1.1 http://127.4.2.1", "content_type": "application/json; charset=UTF-8", "cookies": {}, "cookies_string": "", "date": "Fri, 29 Sep 2023 11:48:50 GMT", "elapsed": 0, "expires": "-1", "failed": false, "json": { "displayName": "Admin User", "encryptedPassword": "<removed>", "generation": 0, "kind": "shared:authz:users:usersworkerstate", "lastUpdateMicros": 0, "name": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "selfLink": "https://localhost/mgmt/shared/authz/users/********", "shell": "/sbin/nologin" }, "msg": "OK (330 bytes)", "pragma": "no-cache", "redirected": false, "server": "Jetty(9.2.22.v20170606)", "status": 200, "strict_transport_security": "max-age=16070400; includeSubDomains", "url": "https://<removed>/mgmt/shared/authz/users/********", "x_content_type_options": "nosniff", "x_frame_options": "SAMEORIGIN", "x_xss_protection": "1; mode=block" The next task, will already fail with a "unauthorized" message. From now on, I cannot access the system any more, and believe me, I tried a lot. One interesting Thing: When I don't use a ansible-vault encrypted password and instead set the variable directly to the string, login is possible, BUT only to the GUI. I cannot do rest api calls with this password. When I change the admin password again (from within GUI), I can however use rest api again. When I change it back to the original one, api calls will fail. There is one difference I noticedin /var/log/audit in the case, when I set the password as clear-text: User authentication is logged like this and the api request fails: AUDIT - user admin - RAW: httpd(pam_audit): User=admin tty=(unknown) After setting a new password within the GUI oder tmsh and running the same api request, audit messaged changed like this and the request is successfull: [...] AUDIT - user admin - RAW: rest(pam_audit): user=admin(admin)[...] When I now change the password back to the previous one, api request fails again [...]AUDIT - user admin - RAW: httpd(pam_audit): User=admin tty=(unknown)[...] What on earth is going on? How is one supposed to bootstrap a vcmp guest from ground up without manually interaction for setting passwords and stuff? Any usefull advice isthoroughly appreciate. Cheers IchnafiSolved812Views0likes3Comments