Dig deeper into Ansible and F5 integration
Basics of Ansible and F5 integration were covered in a joint webinar held earlier in March 2017. To learn more about the integration and current F5 module support along with some use cases view the webinar .
We had another joint webinar in June 2017, which went into details on the integration. We spoke about how to F5 ansible modules (that will be part of upcoming Ansible 2.4 release) can be used to perfrom administrative tasks on the BIG-IP, make the BIG-IP ready for application deployment in weeks rather than in days. We also touched upon usage of F5 ansible iApps module to deploy applications on the BIG-IP.
The webinar's was very well received which ended with a great Q and A session. Some of the questions that came up were how do we create playbooks for different workflows, what are the best practices that F5 recommends, can we get a sample playbook etc. We will use this forum to answer some of the questions and dig deeper into the F5 and Ansible integration.
Now what really is a playbook, it is nothing but a collection of tasks that are to be performed sequentially on a system. Let us consider a use case where a customer has just purchased 20 BIG-IP’s and needs to get all of them networked and to a state where the BIG-IPs are ready to deploy applications. We can define a playbook which consists of tasks required to perform Day0 and Day1 configuration on the BIG-IPs.
Lets start with Day0, now networking the system consists of assigning it a NTP and DNS server address, assigning a hostname, making some ssh customizations etc., some of these settings are common to all the BIG-IPs. The common set of configurations can be defined using the concept of a ‘role’ in ansible. Let’s define a ‘onboarding’ role which will configure the common settings like NTP, DNS and SSHD settings.
PLAYBOOK FOR ONBOARDING
- name: Onboarding BIG-IP hosts: bigip gather_facts: false roles: - onboarding //playbook runs tasks defined in the ‘onboarding’ role
This play book will run against all the BIG-IP’s defined in the inventory host file
Example of inventory host file
[bigip] 10.192.73.218 10.192.73.219 10.192.73.220 10.192.73.221
The above playbook will run tasks specified in the 'onboarding' role in file main.yaml (playbooks/roles/onboarding/tasks/main.yaml)
- name: Configure NTP server on BIG-IP bigip_device_ntp: server: "{{ inventory_hostname }}" user: "{{ username }}" password: "{{ password }}" ntp_servers: "{{ ntp_servers }}" validate_certs: False delegate_to: localhost - name: Manage SSHD setting on BIG-IP bigip_device_sshd: server: "{{ inventory_hostname }}" user: "{{ username }}" password: "{{ password }}" banner: "enabled" banner_text: " {{ banner_text }}" validate_certs: False delegate_to: localhost - name: Manage BIG-IP DNS settings bigip_device_dns: server: "{{ inventory_hostname }}" user: "{{ username }}" password: "{{ password }}" name_servers: "{{ dns_servers }}" search: "{{ dns_search_domains }}" ip_version: "{{ ip_version }}" validate_certs: False delegate_to: localhost
Variables will be referenced from the main.yaml file under default directory for the ‘onboarding’ role (playbooks/roles/onboarding/default/main.yaml)
username: admin password: admin banner_text: "--------Welcome to Onboarding BIGIP----------" ntp_servers: - '172.27.1.1' - '172.27.1.2' dns_servers: - '8.8.8.8' - '4.4.4.4' dns_search_domains: - 'local' - 'localhost' ip_version: 4
The BIG-IP is now ready to deploy applications. One application is configuring the BIG-IP to securely load balance applications. This requires configuring the following on the BIG-IP
- Vlans
- Self-IPs
- Nodes/members (2)
- Pool (1)
- Assigning the nodes to the Pool
- Creating a HTTPS Virtual server
- Creating a redirect Virtual server, which will redirect all HTTP requests to the HTTPS virtual server (iRule is assigned to the virtual server to achieve this)
This playbook will be run individually for each BIG-IP since each will use different values for VLANS/Self IP’s/Virtual server address etc. The variables values for this playbook is defined inline and not in a separate file.
PLAYBOOK FOR APPLICATION DEPLOYMENT
- name: creating HTTPS application hosts: bigip tasks: - name: Configure VLANs on the BIG-IP bigip_vlan: server: "{{ inventory_hostname }}" user: "{{ username }}" password: "{{ password }}" validate_certs: False name: "{{ item.name }}" tag: "{{ item.tag }}" tagged_interface: "{{ item.interface }}" with_items: - name: 'External' tag: '10' interface: '1.1' - name: 'Internal tag: '11’ interface: '1.2' delegate_to: localhost - name: Configure SELF-IPs on the BIG-IP bigip_selfip: server: "{{ inventory_hostname }}" user: "{{ username }}" password: "{{ password }}" validate_certs: False name: "{{ item.name }}" address: "{{ item.address }}" netmask: "{{ item.netmask }}" vlan: "{{ item.vlan }}" allow_service: "{{item.allow_service}}" with_items: - name: 'External-SelfIP' address: '10.10.10.10' netmask: '255.255.255.0' vlan: 'External' allow_service: 'default' - name: 'Internal-SelfIP' address: '192.10.10.10' netmask: '255.255.255.0' vlan: 'Internal' allow_service: 'default' delegate_to: localhost - name: Create a web01.internal node //Creating Node1 bigip_node: server: "{{ inventory_hostname }}" user: "admin" password: "admin" host: "192.168.68.140" name: "web01.internal" validate_certs: False delegate_to: localhost - name: Create a web02.internal node //Creating Node2 bigip_node: server: "{{ inventory_hostname }}" user: "admin" password: "admin" host: "192.168.68.141" name: "web02.internal" validate_certs: False delegate_to: localhost - name: Create a web-pool //Creating a pool bigip_pool: server: "{{ inventory_hostname }}" user: "admin" password: "admin" lb_method: "ratio_member" monitors: http name: "web-pool" validate_certs: False delegate_to: localhost - name: Add http node to web-pool //Assigning members to a pool bigip_pool_member: description: "HTTP Webserver-1" host: "{{ item.host }}" name: "{{ item.name }}" user: "admin" password: "admin" pool: "web-pool" port: "80" server: "{{ inventory_hostname }}" validate_certs: False with_items: - host: "192.168.168.140" name: "web01.internal" - host: "192.168.68.141" name: "web02.internal" delegate_to: localhost - name: Create a virtual server //Create a HTTPS Virtual Server bigip_virtual_server: description: "Secure web application" server: "{{ inventory_hostname }}" user: "admin" password: "admin" name: "https_vs" destination: "10.10.20.120" port: 443 snat: "Automap" all_profiles: - http - clientssl pool: "web-pool" validate_certs: False delegate_to: localhost - name: Create a redirect virtual server //Create a redirect virtual server bigip_virtual_server: description: "Redirect Virtual server" server: "{{ inventory_hostname }}" user: "admin" password: "admin" name: "http_redirect" destination: "10.10.20.120" validate_certs: False port: 80 all_profiles: - http all_rules: //Attach an iRule to the Virtual server - _sys_https_redirect delegate_to: localhost
Bookmark this page if you are interested in learning more. We will be updating this blog with new F5 modules that are going to be supported with Ansible 2.4 release
- B_EarpAltocumulus
I have created a simple HA pair deployment using xlsx spreadsheet. No ansible knowledge required. Just fill in the spreadsheet and execute the playbook
Please see: https://github.com/bwearp/simple-ha-pair
- Milko_125350Nimbostratus
Hello team, I'm working with the ansible and LTM V12, now we're using user admin by create, delete or change conf for vs, pool, node, etc. I want not use the user role "admin", I'd like use role "manager" but, with this user I can not work with "get stats" in the pool members. I should use the user admin always vía ansible?
Thanks advance!
- Payal_SRet. Employee
Hi Milko,
Ideally you can use any user, but each user role has different privileges assigned to them. Ansible does not have any limitation on which user, it will follow privileges assigned by the BIG-IP for that role: https://support.f5.com/kb/en-us/products/big-ip_ltm/manuals/product/bigip-user-account-administration-11-6-0/3.html
'Manager' role you should be able to view pool member information, what error are you encountering.
Thanks
- B_Lawrimore_151Nimbostratus
i am trying add a new virtual server, or change a configured virtual server, that has multiple irules attached. i want to dynamically assign the names of the irules via a variable file. while i dont have a problem looping through a task using external data, i do have a problem attaching multiple i rules in a single loop. as you may know each time you assign an irule via ansible (and the REST api), the operation is a replace, not append. if i run the task three times with one irule each time, the last irule i ran through the task is the one that is left configured.
im not the strongest with ansible, and i am having a hard time figuring the best way to assign multiple irules in one pass of a task using a list. any suggestions?
- Payal_SRet. Employee
Try the following
Playbook:
- name: Rule mgmt BIG-IP hosts: localhost connection: local gather_facts: false vars_files: - irule_var.yml tasks: - name: Add iRule bigip_irule: server: "xx.xx.xxx.xxx" user: "admin" password: "****" module: "ltm" name: "{{item}}" content: "{{ lookup('file', '{{item}}') }}" state: present validate_certs: false with_items: "{{irules}}"
Variable file
irules: - irule1 - irule2 - irule3
- B_Lawrimore_151Nimbostratus
I tried to write a detailed comment earlier today, but it was flagged as spam for some reason. I'll try again and be more succinct.
unfortunately, I did not phrase my question well, but Payal your comment gave me something to think about. I'm trying to assign multiple irules to a single virtual server by using a var file. next, I want to modify multiple virtual servers to have multiple irules--again using a var file. it seems each time I use a list of items to assign multiple irules via Ansible, I get a list of exceptions thrown. however, if I manually assign multiple irules to a single VS if I don't use a var file. I can also statically assign two irules and then dynamically assign a third irule from a var file using a loop. When I change the irule variable to be a single member list, I get a runtime exception.
heres my varfile with a simple dictionary that works with a loop:
irule_data: - {irule: irule3, vs: vs_test01}
here is the same varfile that works because it uses a list with one element (I have to change the task code to signify I want to use item '0' of the embedded list):
irule_data: - {irule: [ 'irule3' ], vs: vs_test01}
here's the varfile that fails using the same loop (the dictionary contains a list with two elements): irule_data: - {irule: [ 'irule3', 'irule4' ], vs: vs_test01}
I have tried using with_subelements, with_nested, etc, but all of these do not build the irule list to mimic the following during execution:
irules: - irule1 - irule2 - irule3 - irule4
instead I get two task runs that look like this:
irules: - irule1 - irule2 - irule3 irules: - irule1 - irule2 - irule4
my overall problem is that I cannot find a way to successfully build the list of irules that I want to apply to the VS using a var file because each type of loop I use causes the task to be run multiple times instead of just once.
I have learned a lot from this thread, and I appreciate the attention and support it receives.
Thanks
- Payal_SRet. Employee
So If I get this correctly you want to run a playbook on multiple virtual servers and each virtual server has multiple irules
See if this works for you
Variable file
virtualserver: - name: Test1 ip: "10.192.xx.xx" irules: - irule1 - irule2 - name: Test2 ip: "10.192.xx.xx" irules: - irule1 - irule3
Playbook task:
- name: Add VS on BIG-IP bigip_virtual_server: server: "10.192.xx.xx" user: "****" password: "****" name: "{{item.name}}" destination: "{{item.ip}}" port: 80 irules: "{{item.irules}}" validate_certs: False with_items: "{{virtualserver}}" delegate_to: localhost
Result:
changed: => (item={u'irules': [u'irule1', u'irule2'], u'ip': u'10.192.xx.xx', u'name': u'Test1'}) changed: => (item={u'irules': [u'irule1', u'irule3'], u'ip': u'10.192.xx.xx', u'name': u'Test2'})
- B_Lawrimore_151Nimbostratus
you have the question correct. the answer looks correct as well, however I get an error upon execution:
TypeError: unhashable type: 'list' failed: [10.10.10.10] (item={u'irules': [u'irule1, u'irule2'], u'name': u'vs_test01'}) => { "changed": false, "item": { "irules": [ "irule1", "irule2" ], "name": "vs_test01" }, "rc": 1 } MSG: MODULE FAILURE MODULE_STDERR: Traceback (most recent call last): File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1657, in main() File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1648, in main results = mm.exec_module() File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1402, in exec_module changed = self.present() File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1425, in present return self.update() File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1436, in update if not self.should_update(): File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1444, in should_update result = self._update_changed_options() File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1474, in _update_changed_options change = diff.compare(k) File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1113, in compare result = getattr(self, param) File "/tmp/ansible_RIMJkD/ansible_module_bigip_virtual_server.py", line 1359, in irules if sorted(set(self.want.irules)) != sorted(set(self.have.irules)): TypeError: unhashable type: 'list'
I'm wondering if I have a bad module or plugin version that is causing my headaches. ive updated F5-sdk, suds, bigsuds, etc. I'm running ansible 2.5.2 and python 2.7.5.
Having this ability will round out the automation of building a VS, and modifying VSs in bulk. You've been very helpful.
- Payal_SRet. Employee
Are you using the module that is packaged with ansible 2.5 (just make sure you are not using a local or older copy of the bigip_virtual_server module).
If that is the case that you are using the latest and greatest package that is bundled with ansible 2.5 release then please open an issue on github https://github.com/F5Networks/f5-ansible/issues (provide as much detail as you can regarding the issue)
If you are using a local or older copy: The module bundled with ansible 2.5 version is (https://github.com/F5Networks/f5-ansible/blob/stable-2.5/library/bigip_virtual_server.py), try and replace that and see if it solves your issue.
- B_Lawrimore_151Nimbostratus
I got it Payal! My issue was in the irule section of the task. I did this instead of what you wrote:
irules: - '{{ item.irule }}'
I got to thinking of the error 'unhashable list' and thought maybe its the fact that I'm putting a list in the placeholder of a list item. by moving it up a level as you had written, it started working just as it should. Great work and thank you so much!
Now that I have this playbook complete, and a better understanding of how Ansible works, I can ask my devOps team to just complete a templated var file, drop it in a shared folder, and let the playbook run on a schedule thus building the Virtual Servers automatically.